From f3af3dd8570e0cd91314ab25c881bfec26879501 Mon Sep 17 00:00:00 2001 From: jinye Date: Fri, 30 Oct 2026 22:56:35 +0800 Subject: [PATCH] refactor(managed-agent): Split server into Maven modules (#15871) * refactor(managed-agent): split server into Maven modules Separate contracts, orchestration, persistence, adapters, assembly, and verification while retaining the existing service delivery model. Move tests and build consumers together and document local evidence and pending CI acceptance. Co-authored-by: Qwen-Coder * refactor(managed-agent): reduce configuration and SDK boilerplate Co-authored-by: Qwen-Coder * refactor(managed-agent): narrow contracts and simplify test scaffolding Co-authored-by: Qwen-Coder * fix(managed-agent): repair verification after module migration Resolve shared fixtures and worker paths from the Maven module root, update CI packaging assertions, and align existing test fixtures with the extracted contracts. Restore restricted Workspace constructors and remove redundant test visibility changes without altering inherited MySQL coverage. Co-authored-by: Qwen-Coder * test(managed-agent): align remaining Harness and OSS fixtures Preserve SDK refusal messages and register the managed OSS adapter at fixture injection points. Keep the existing retry and client shutdown assertions intact. Co-authored-by: Qwen-Coder * style(managed-agent): order Hosted cleanup Harness imports Co-authored-by: Qwen-Coder * test(managed-agent): fix recovery smoke fixture and import order Supply the required transcript coverage in the recovery browser fixture and wait for history before testing a complete handoff. Preserve its existing safety and submission assertions. Order the cleanup coordinator test imports for Checkstyle. Co-authored-by: Qwen-Coder --------- Co-authored-by: Qwen-Coder --- .github/scripts/ci-runner-routing.test.mjs | 2 +- .github/workflows/ci.yml | 4 +- .github/workflows/qwen-triage.yml | 2 +- .../release-managed-agent-reference.yml | 4 +- .github/workflows/release-managed-runtime.yml | 5 +- .../workflows/sdk-java-contract-open-prs.yml | 2 +- .../workflows/sdk-java-flyway-open-prs.yml | 6 + .github/workflows/sdk-java.yml | 98 +- .github/workflows/verify-maven-repo.yml | 11 +- .prettierignore | 1 + deploy/k2-resident/README.md | 12 +- deploy/k2-resident/README.zh-CN.md | 12 +- .../2026-10-15-managed-agent-server-layout.md | 2 + ...10-15-managed-agent-server-layout.zh-CN.md | 2 + ...-10-19-managed-agent-package-boundaries.md | 2 + ...-managed-agent-package-boundaries.zh-CN.md | 2 + ...2026-10-23-managed-agent-admin-launcher.md | 24 +- ...0-23-managed-agent-admin-launcher.zh-CN.md | 24 +- .../2026-10-29-managed-agent-maven-modules.md | 281 ++++ ...10-29-managed-agent-maven-modules.zh-CN.md | 207 +++ docs/users/managed-local-serve.md | 8 +- docs/users/managed-local-serve.zh-CN.md | 8 +- eslint.config.js | 3 +- .../helpers/hosted-latency-driver.ts | 4 +- .../cli/src/serve/capability-parity.test.ts | 2 +- .../src/serve/hosted-csi-tool-turn.test.ts | 2 +- packages/cli/src/serve/hosted-input.test.ts | 2 +- packages/managed-agent-server/.gitignore | 2 +- packages/managed-agent-server/AGENTS.md | 21 +- packages/managed-agent-server/Dockerfile | 2 +- packages/managed-agent-server/QWEN.md | 4 +- packages/managed-agent-server/README.md | 61 +- .../managed-agent-server/adapters/pom.xml | 87 ++ .../artifact/AliyunObjectStoreClient.java} | 45 +- .../AliyunToolPublicationObjectStore.java | 4 +- .../event/LiteTopicEventTransport.java | 5 +- .../event/LiteTopicPublicEventTransport.java | 21 +- .../adapters/event/RocketMqEventClients.java | 146 ++ .../adapters/event/RocketMqMessages.java | 28 + .../ManagedExtensionArchiveStore.java | 22 +- .../runtime/ResidentWorkspaceProvisioner.java | 38 +- .../ResidentWorkspaceReclamation.java | 43 +- .../runtime/WorkspaceCsiResourceGuard.java | 26 +- .../WorkspaceCsiRuntimeProvisioner.java | 59 +- .../adapters}/workspace/ChildWorktreeGit.java | 29 +- .../workspace/LocalRecoveryWorker.java | 84 ++ .../workspace/LocalWorktreeFiles.java | 11 + .../workspace/SessionWorktreeGit.java | 14 +- .../harness/HarnessDiscovery.java | 0 .../harness/HarnessNegotiation.java | 33 + .../harness/HarnessPoolConnector.java | 24 +- .../harness/HarnessSdkValues.java | 106 ++ .../code/managedagent/harness/HarnessTls.java | 4 +- .../harness/KubernetesHarnessDiscovery.java | 12 +- .../harness/QwenHostedHarnessConnector.java | 56 +- .../harness/SdkHarnessBoundary.java | 422 ++++++ .../managedagent/harness/SdkHarnessRules.java | 25 + .../harness/UnavailableHarnessConnector.java | 3 + .../adapter}/HttpRuntimeTransport.java | 26 +- .../security/AesGcmSecretProtector.java | 4 +- .../ExternalHttpRuntimeClient.java | 3 +- .../ExternalRuntimeProvisioner.java | 4 +- .../ExternalWorkspaceProvisioner.java | 3 + .../provisioning/KubernetesCsiLogReader.java | 5 +- .../KubernetesHttpRuntimeClient.java | 26 +- .../KubernetesRuntimeProvisioner.java | 5 +- .../LocalProcessRuntimeProvisioner.java | 5 +- .../provisioning/LocalRuntimeStore.java | 6 +- .../provisioning/MacOsHostIdentity.java | 0 .../NativeStorageIdentityReader.java | 120 ++ .../RoutingRuntimeProvisioner.java | 0 .../provisioning/SshConnectionManager.java | 3 +- .../provisioning/SshRuntimeProvisioner.java | 11 +- .../StaticRuntimeProvisioner.java | 0 .../provisioning/WorkspaceFileProcess.java | 6 +- .../managed-agent-server/contracts/pom.xml | 79 + .../contracts/artifact/ArtifactReadLease.java | 13 + .../artifact/ManagedArtifactPolicy.java | 15 + .../artifact/ToolPublicationObjectStore.java | 12 +- .../artifact/ToolPublicationRetention.java | 8 + .../ToolPublicationStorageException.java | 2 +- .../catalog/AgentDefinitionExtensions.java | 9 +- .../channel/ChannelAdapterModels.java | 2 +- .../event/CommittedEventPublisher.java | 4 +- .../contracts/event/EventMessageListener.java | 6 + .../contracts/event/EventSubscriptions.java | 8 + .../contracts}/event/EventTransport.java | 4 +- .../event/JournalEventTransport.java | 6 +- .../event/ManagedEventEnvelope.java | 2 +- .../event/ManagedEventEnvelopes.java | 26 +- .../extensions/ExtensionArchives.java | 11 + .../extensions/ExtensionPackages.java | 20 + .../extensions/ManagedExtensionPackages.java | 2 +- .../contracts}/harness/HarnessConnector.java | 162 +- .../harness/HarnessDisabledException.java | 2 +- ...ostedHarnessRecoveryDeclinedException.java | 2 +- .../persistence/AgentDefinitionCompiler.java | 17 +- .../ChannelDeliveryRepository.java | 20 +- .../persistence/ChannelRouteRepository.java | 18 +- .../persistence/ChannelStoreSupport.java | 8 +- .../contracts}/persistence/EventIdentity.java | 10 +- .../ExternalStorageRegistration.java | 6 +- .../persistence/ManagedChannelRecords.java | 4 +- .../persistence/ManagedConfigInstall.java | 19 +- .../ManagedExtensionProjection.java | 3 +- .../persistence/ManagedExtensionRecords.java | 26 +- .../ManagedHarnessReleaseRecords.java | 2 +- .../ManagedHistoryMaintenanceRecords.java | 12 +- .../persistence/ManagedHookRecords.java | 4 +- .../ManagedHostedRecoveryRecords.java | 17 +- .../persistence/ManagedMcpRecords.java | 6 +- .../ManagedSessionCommandRecords.java | 4 +- .../ManagedSessionMessageRecords.java | 4 +- .../ManagedSessionStoreModels.java | 4 +- .../persistence/ManagedStorageVersions.java | 6 + .../persistence/ManagedTeamRecords.java | 4 +- .../persistence/ManagedToolStageRecords.java | 4 +- .../persistence/StorageReadPolicy.java | 15 + .../persistence/ToolPublicationContract.java | 8 +- .../WorkspaceCsiApplicationJson.java | 16 +- .../persistence/WorkspaceCsiRegistration.java | 4 +- .../contracts/runtime/ProtectionIdentity.java | 19 + .../contracts/runtime/Receipt.java | 9 + .../contracts/runtime/WorkerArtifact.java | 15 + .../security/WriterCredentialPolicy.java | 15 +- .../session/AcceptanceEvidence.java | 10 +- .../contracts}/session/AgentStateStore.java | 335 +++-- .../session/HarnessConflictCodes.java | 2 +- .../session/ReplayCursorExpired.java | 7 +- .../contracts/session/UserMessagePolicy.java | 14 + .../workspace/ChildWorkspaceException.java | 6 +- .../workspace/ChildWorkspaceProvider.java | 8 +- .../contracts/workspace/ChildWorktreeGit.java | 71 + .../workspace/LocalWorkspaceFiles.java | 13 + .../workspace/PrivateCsiSession.java | 21 + .../contracts/workspace/RecoveryWorker.java | 15 + .../workspace/ResidentReclamation.java | 36 + .../workspace/ResidentWorkspaceCatalog.java | 65 + .../workspace/ResidentWorkspaceSnapshot.java | 58 + .../contracts/workspace}/RuntimeWarmer.java | 34 +- .../workspace/SessionWorktreeOperations.java | 30 + .../workspace/SshWorkspaceFiles.java | 12 + .../workspace/StorageIdentityReader.java | 8 + .../workspace/WorkspaceRecoveryProtocol.java | 97 ++ .../workspace/WorkspaceSelection.java | 7 +- .../workspace/WorkspaceToolProfiles.java | 2 +- .../contracts/workspace/WorktreeFiles.java | 10 + .../exception/ManagedAgentException.java | 14 +- .../contracts/HarnessCapabilities.java | 13 + .../contracts/HarnessCapabilityFailure.java | 20 + .../contracts/HarnessCapacityFailure.java | 7 + .../harness/contracts/HarnessFailure.java | 6 + .../contracts/HarnessGenerationFailure.java | 20 + .../harness/contracts/HarnessHttpFailure.java | 24 + .../HarnessMutationUnknownFailure.java | 10 + .../contracts/HarnessProtocolFailure.java | 7 + .../harness/contracts/HarnessRules.java | 10 + .../contracts/HarnessRuntimeRecovery.java | 27 + .../HarnessSessionRefusedFailure.java | 16 + .../HarnessSettleAnsweredFailure.java | 5 + .../contracts/HarnessTransportFailure.java | 7 + .../code/managedagent/model/ApiModels.java | 8 +- .../managedagent/model/RequestDigests.java | 2 - .../code/managedagent/model/StoreModels.java | 7 +- .../managedagent/model/TenantContext.java | 3 +- .../repository/AutomationLedgerStore.java | 184 +++ .../repository/ChannelInstanceStore.java | 92 ++ .../repository/ChildResultRelayStore.java | 140 ++ .../repository/ChildWorkspaceStore.java | 101 ++ .../ExtensionPackageRepository.java | 28 + .../repository/HarnessPlacementStore.java | 32 + .../repository/HostedCleanupDebtStore.java | 21 + .../repository/ManagedActionStore.java | 90 ++ .../ManagedAgentDefinitionStore.java | 57 + .../repository/ManagedArtifactReader.java | 30 + .../ManagedExtensionRecordStore.java | 114 ++ .../repository/ManagedSessionSkills.java | 22 + .../repository/ManagedSessionStore.java | 116 ++ .../repository/ManagedTaskEventStore.java | 105 ++ .../repository/ManagedToolDetailsStore.java | 8 + .../repository/ManagedToolResultStore.java | 61 + .../repository/ManagedUserMessageStore.java | 36 + .../repository/ManagedWorkspaceRegistry.java | 60 + .../ResidentRetirementRepository.java | 83 + .../repository/SessionMessageRelayStore.java | 65 + .../repository/SessionRecoveryRepository.java | 9 + .../repository/SessionWorktreeStore.java | 107 ++ .../repository/SshWorkspaceStore.java | 84 ++ .../repository/TransportConsumerStore.java | 30 + .../repository/WakeRecoveryStore.java | 19 + .../repository/WorkspaceCsiReservations.java | 19 + .../repository/WorkspaceExecutionStore.java | 82 + .../repository/WorkspaceLifecycleStore.java | 28 + .../settings/ManagedAgentSettings.java | 228 +++ .../managedagent/settings/SshSettings.java | 28 + .../qwen/code/runtimebroker/BrokerValues.java | 2 +- .../CsiFilesRetirementProfile.java | 9 + .../CsiNativeActivationProof.java | 18 +- .../runtimebroker/CsiNativeToolContent.java | 160 ++ .../runtimebroker/CsiOwnershipRevision.java | 0 .../ExecutionReconciliation.java | 0 .../code/runtimebroker/HarnessOwnership.java | 4 +- .../runtimebroker/HarnessSessionResolver.java | 10 +- .../qwen/code/runtimebroker/JsonCodec.java | 0 .../ManagedCheckpointHistory.java | 6 +- .../ManagedExtensionExecutionProfile.java | 0 .../NativeCsiExecutionRepository.java | 24 + .../ResidentWorkspaceProfile.java | 0 .../code/runtimebroker/RuntimeAdmission.java | 17 +- .../runtimebroker/RuntimeAttestation.java | 2 +- .../runtimebroker/RuntimeBindingRecord.java | 24 +- .../RuntimeBindingRepository.java | 78 +- .../runtimebroker/RuntimeBrokerException.java | 0 .../RuntimeBrokerOperations.java | 123 ++ .../runtimebroker/RuntimeDrainReceipt.java | 4 +- .../RuntimeExtensionCommandVerifier.java | 2 +- .../qwen/code/runtimebroker/RuntimeLease.java | 0 .../RuntimeLifecycleAuthority.java | 0 .../runtimebroker/RuntimeObservation.java | 0 .../RuntimeProvisionRequest.java | 6 +- .../runtimebroker/RuntimeProvisionSeed.java | 4 +- .../runtimebroker/RuntimeProvisioner.java | 36 +- .../RuntimePublicationGrant.java | 0 .../RuntimePublicationVerifier.java | 6 +- .../RuntimeRecoveryEvidence.java | 6 +- .../runtimebroker/RuntimeRequestKeys.java | 56 + .../runtimebroker/RuntimeResourceHandle.java | 0 .../code/runtimebroker/RuntimeRetention.java | 16 + .../qwen/code/runtimebroker/RuntimeScope.java | 0 .../code/runtimebroker/RuntimeSession.java | 0 .../runtimebroker/RuntimeSessionRecord.java | 4 +- .../RuntimeSessionRepository.java | 16 +- .../code/runtimebroker/RuntimeTransport.java | 57 +- .../runtimebroker/StoreFailureException.java | 8 + .../runtimebroker/ToolExecutionRecord.java | 34 +- .../ToolExecutionRepository.java | 42 +- .../qwen/code/runtimebroker/ToolHoldRow.java | 0 .../WorkspaceCsiRuntimeIdentity.java | 0 .../WorkspaceExecutionProfile.java | 0 .../WorkspacePhysicalTransport.java | 29 + .../managedworkspace/ContextBinding.java | 4 +- .../managedworkspace/ResolvedWorkspace.java | 0 .../managedworkspace/WorkspaceAccess.java | 0 .../WorkspaceAccessPolicy.java | 2 +- .../managedworkspace/WorkspaceActor.java | 0 .../managedworkspace/WorkspaceCatalog.java | 6 +- .../managedworkspace/WorkspaceException.java | 10 +- .../managedworkspace/WorkspacePage.java | 0 .../managedworkspace/WorkspaceRecord.java | 4 +- .../managedworkspace/WorkspaceRegistry.java | 7 +- .../WorkspaceRelativePath.java | 0 .../managedworkspace/WorkspaceSelection.java | 0 .../managedworkspace/WorkspaceState.java | 0 .../managedworkspace/WorkspaceValues.java | 18 +- .../managedworkspace/WorkspaceView.java | 0 .../protocol}/ExternalRuntimeKinds.java | 10 +- .../protocol/KubernetesNames.java | 20 + .../protocol}/ManagedContextProtocol.java | 6 +- .../protocol}/ProviderRuntimeProtocol.java | 6 +- .../protocol/RuntimeProviderKinds.java | 9 + .../schema}/ManagedSchemaContract.java | 2 +- .../schema}/ManagedSchemaKeys.java | 2 +- .../security/ProtectedSecret.java | 0 .../security/SecretProtector.java | 4 +- .../spi}/ExternalRuntimeClient.java | 6 +- .../spi}/ExternalStorageRegistry.java | 6 +- .../spi/KubernetesMaintenanceClient.java | 30 + .../spi}/KubernetesRuntimeClient.java | 10 +- .../runtimebroker/spi/SshConnections.java | 24 + .../runtimebroker/spi}/SshRuntimeNode.java | 2 +- .../runtimebroker/spi}/SshRuntimeStorage.java | 3 +- .../transport/CsiFileFinalizeProtocol.java | 5 +- .../transport/CsiFileHistoryProtocol.java | 0 .../transport/CsiNativeReadbackProtocol.java | 2 +- .../transport/ManagedCsiFilesProtocol.java | 2 +- .../transport/ManagedCsiProtocol.java | 7 +- .../ManagedExtensionCommandProtocol.java | 6 +- .../transport/ManagedExtensionProtocol.java | 12 +- .../transport/ManagedHoldControlProtocol.java | 14 +- .../transport/ManagedHoldFamily.java | 0 .../transport/ManagedHookProtocol.java | 6 +- .../transport/ManagedMcpProtocol.java | 10 +- .../transport/ManagedMonitorProtocol.java | 4 +- .../transport/ManagedShellProtocol.java | 4 +- .../csi-native-file-declarations.json | 0 .../managed-agent-public-api.openapi.json | 0 .../control-plane/pom.xml | 87 ++ .../artifact/ManagedArtifactService.java | 30 +- .../artifact/ManagedToolDetailsService.java | 5 +- .../ToolPublicationRetentionObserver.java | 10 +- .../automation/AutomationScanner.java | 28 +- .../managedagent/automation/CronSlots.java | 0 .../automation/ManagedAutomationService.java | 135 +- .../ManagedAgentDefinitionService.java | 19 +- .../catalog/ManagedHookCatalogService.java | 2 +- .../catalog/ManagedMcpCatalogService.java | 2 +- .../catalog/ManagedMcpManagementService.java | 35 +- .../catalog/ManagedSkillCatalogService.java | 4 +- .../channel/ManagedChannelService.java | 129 +- .../CompositeCommittedEventPublisher.java | 2 + .../event/EventTransportConsumer.java | 26 +- .../event/MessageMaterializer.java | 4 +- .../event/TransportExecutors.java | 0 .../event/TransportWakeActivator.java | 8 +- .../ManagedExtensionManagementService.java | 45 +- .../ManagedExtensionPackageService.java | 222 +++ .../managedagent/security/PublicSurface.java | 0 .../session/ActionResponseCoordinator.java | 27 +- .../ContextCompressionCoordinator.java | 11 +- .../session/ContextCompressionService.java | 10 +- .../session/HarnessCoordinator.java | 72 +- .../session/HarnessEventProjector.java | 4 +- .../HostedCleanupRetryCoordinator.java | 24 +- .../session/ManagedActionService.java | 25 +- .../session/ManagedAgentService.java | 473 ++---- .../session/ManagedEventStreamService.java | 8 +- .../session/ManagedInputAttachments.java | 5 +- .../session/ManagedTaskCancelService.java | 6 +- .../session/ManagedTaskService.java | 43 +- .../session/ManagedUserMessagePolicy.java | 330 ++++ .../session/OperationReconciler.java | 2 + .../session/OwnerSubscriptions.java | 16 +- .../managedagent/session/RenameEvidence.java | 4 +- .../session/ReplayFloorAdvancer.java | 7 +- .../managedagent/session/SessionEventHub.java | 0 .../session/SessionLifecycleCoordinator.java | 108 +- .../session/SessionLifecycleService.java | 10 +- .../session/SessionMessageRelay.java | 20 +- .../session/SessionRecoveryService.java | 43 +- .../session/SessionRenameCoordinator.java | 9 +- .../session/TaskCancelCoordinator.java | 28 +- .../session/TurnInputEvidence.java | 2 + .../session/UserMessageDispatcher.java | 10 +- .../managedagent/session/WakeRecovery.java | 12 +- .../workspace/ChildLifecycleAdmissions.java | 4 +- .../workspace/ChildResultRelay.java | 29 +- .../workspace/ChildWorkspaceService.java | 56 +- .../workspace/SessionWorktreeService.java | 56 +- .../workspace/SshWorkspaceService.java | 90 +- .../workspace/WorkspaceDiscoveryService.java | 9 +- .../runtime/ManagedRuntimeCoordinator.java | 317 ++++ .../ResidentWorkspaceRetirementService.java | 213 +++ .../runtime/RuntimeRecoveryCoordinator.java | 13 +- .../runtime/RuntimeRetentionJob.java | 14 +- .../runtime/WorkspaceRuntimeProvisioner.java | 10 +- .../runtime/WorkspaceRuntimeResolver.java | 91 +- .../runtime/WorkspaceRuntimeTransport.java | 72 +- .../ConfiguredWorkspaceRegistry.java | 8 +- .../GrantedWorkspaceAccessPolicy.java | 8 +- .../docs/runtime-broker.md | 14 +- .../managed-agent-server/persistence/pom.xml | 92 ++ .../persistence/AutomationLedgerStore.java | 92 +- .../persistence/ChannelInstanceStore.java | 64 +- .../persistence/ChildResultRelayStore.java | 95 +- .../persistence/ChildWorkspaceStore.java | 73 +- .../ExternalStorageRegistrationStore.java | 15 +- .../persistence/HarnessPlacementStore.java | 34 +- .../persistence/HostedCleanupDebtStore.java | 18 +- .../persistence/HostedCleanupReferences.java | 13 +- .../JdbcChannelDeliveryRepository.java | 4 +- .../JdbcChannelRouteRepository.java | 12 +- .../JdbcExtensionPackageRepository.java} | 338 ++--- .../JdbcSessionRecoveryRepository.java | 30 + .../persistence/LocalStorageOwners.java | 21 +- .../persistence/ManagedActionStore.java | 113 +- .../ManagedAgentDefinitionStore.java | 53 +- .../persistence/ManagedAgentStore.java | 534 +++---- .../persistence/ManagedArtifactReader.java | 17 +- .../persistence/ManagedConfigStore.java | 19 + .../ManagedExtensionAgentLaunch.java | 12 +- .../ManagedExtensionCommandVerifier.java | 11 +- .../ManagedExtensionRecordStore.java | 166 +- .../ManagedLegacySessionGuard.java | 9 +- .../persistence/ManagedMcpSettings.java | 7 +- .../ManagedNativeJournalSnapshots.java | 54 +- .../ManagedProjectionResourceCollector.java | 2 +- .../persistence/ManagedReceipts.java | 6 +- .../ManagedRuntimeRetentionGuard.java | 14 +- .../ManagedSchemaJdbcTemplate.java | 2 +- .../ManagedSessionRetirementStore.java | 10 +- .../persistence/ManagedSessionSkills.java | 13 +- .../persistence/ManagedSessionStore.java | 238 +-- .../persistence/ManagedTaskEventStore.java | 113 +- .../persistence/ManagedToolDetailsStore.java | 8 +- .../ManagedToolResultProjector.java | 22 +- .../persistence/ManagedToolResultStore.java | 49 +- .../persistence/ManagedUserMessageStore.java | 33 +- .../persistence/ManagedWorkspaceRegistry.java | 68 +- .../ResidentWorkspaceRetirementStore.java | 530 +++---- .../persistence/ResidentWorkspaces.java | 82 +- .../persistence/SessionMessageRelayStore.java | 51 +- .../SessionResourceCollectionCollector.java | 18 +- .../persistence/SessionWorktreeStore.java | 134 +- .../persistence/SshWorkspaceStore.java | 111 +- .../ToolPublicationAdmissionStore.java | 18 +- .../persistence/ToolPublicationCollector.java | 14 +- .../persistence/ToolPublicationDataStore.java | 114 +- .../ToolPublicationRetentionStore.java | 35 +- .../persistence/ToolPublicationStore.java | 25 +- .../persistence/ToolPublicationVerifier.java | 1 + .../persistence/TransportConsumerStore.java | 24 +- .../persistence/WakeRecoveryStore.java | 15 +- .../persistence/WorkspaceCreationRoots.java | 35 +- .../persistence/WorkspaceCreationStore.java | 53 +- .../WorkspaceCsiApplicationHistory.java | 19 +- .../WorkspaceCsiApplicationHistoryImage.java | 4 +- .../WorkspaceCsiApplicationImage.java | 13 +- .../WorkspaceCsiApplicationInventory.java | 116 +- .../WorkspaceCsiApplicationStore.java | 19 +- .../WorkspaceCsiApplicationWorkerMatch.java | 5 +- .../WorkspaceCsiCheckpointSnapshotStore.java | 9 +- .../WorkspaceCsiReservationStore.java | 204 ++- .../WorkspaceCsiRuntimeAccess.java | 21 +- .../WorkspaceCsiSessionCommands.java | 48 + .../WorkspaceCsiWorkerAckStore.java | 9 +- .../WorkspaceEvidenceResources.java | 2 +- .../persistence/WorkspaceExecutionStore.java | 107 +- .../persistence/WorkspaceFileStore.java | 51 +- .../persistence/WorkspaceFileUploadStore.java | 47 +- .../persistence/WorkspaceLifecycleStore.java | 106 +- .../WorkspaceLocalDirectories.java | 7 +- .../WorkspaceMaintenanceRecovery.java | 0 .../WorkspaceMigrationAdmission.java | 16 +- .../persistence/WorkspaceMigrationStore.java | 23 +- .../persistence/WorkspaceOperations.java | 8 +- .../WorkspaceOperatorRecoveryStore.java | 5 +- .../WorkspaceProfileLifecycleVerifier.java | 19 +- .../persistence/WorkspaceRecoveryReader.java | 29 +- .../persistence/WorkspaceRecoveryStore.java | 108 +- .../persistence/WorkspaceStorageGuard.java | 309 ++-- .../WorkspaceStorageKindGuard.java | 9 +- .../persistence/evidence}/CancelEvidence.java | 10 +- .../JdbcRuntimeBrokerSchema.java | 0 .../managedschema/ManagedSchema.java | 4 +- .../managedschema/ManagedSchemaCatalog.java | 2 + .../ManagedSchemaProjection.java | 0 .../managedschema/ManagedSchemaRows.java | 0 .../managedschema/ManagedSchemaTime.java | 0 .../CsiNativeToolReservation.java | 152 +- .../InMemoryRuntimeBindingRepository.java | 17 +- .../InMemoryRuntimeSessionRepository.java | 11 +- .../InMemoryToolExecutionRepository.java | 8 +- .../JdbcCsiActivationAdmission.java | 36 +- .../JdbcCsiApplicationProgress.java | 12 +- .../JdbcCsiApplicationRetirement.java | 8 +- .../JdbcCsiExecutionAdmission.java | 13 +- .../JdbcCsiFilesRetirementGuard.java | 14 +- .../persistence}/JdbcCsiReadyAdmission.java | 7 +- .../persistence}/JdbcCsiRetirementSeal.java | 25 +- .../persistence/JdbcHarnessResources.java | 18 + .../persistence}/JdbcRepositorySupport.java | 69 +- .../JdbcRuntimeBindingRepository.java | 55 +- .../persistence}/JdbcRuntimeRetention.java | 17 +- .../JdbcRuntimeSessionRepository.java | 23 +- .../JdbcToolExecutionRepository.java | 10 +- .../db/managed-schema/V1__managed_schema.sql | 0 packages/managed-agent-server/pom.xml | 1130 ++------------ .../runtime-broker/pom.xml | 51 + .../service}/RuntimeBrokerService.java | 101 +- packages/managed-agent-server/server/pom.xml | 319 ++++ .../WorkspaceRecoveryApplication.java | 0 .../ManagedAgentServerApplication.java | 0 .../api/AgentDefinitionController.java | 0 .../managedagent/api/ApiExceptionHandler.java | 4 +- .../api/AuthenticatedTenantActor.java | 0 ...ExternalStorageRegistrationController.java | 2 +- .../api/ManagedActionController.java | 0 .../api/ManagedArtifactController.java | 0 .../api/ManagedAutomationController.java | 0 .../api/ManagedChannelAdapterController.java | 18 +- .../api/ManagedChannelController.java | 0 .../ManagedExtensionManagementController.java | 0 .../ManagedExtensionPackageController.java | 2 +- .../api/ManagedHookController.java | 0 .../api/ManagedMcpController.java | 0 .../api/ManagedSessionStoreController.java | 32 +- .../api/ManagedToolDetailsController.java | 0 .../api/PublicAgentController.java | 20 +- .../managedagent/api/RequestIdFilter.java | 0 .../api/SessionWorktreeController.java | 4 +- .../managedagent/api/SignatureAuthFilter.java | 2 +- .../api/SshWorkspaceController.java | 2 +- .../api/TenantContextArgumentResolver.java | 0 .../managedagent/api/TenantContextFilter.java | 0 .../api/ToolPublicationController.java | 12 +- .../api/TrustedActorHeaderFilter.java | 0 .../api/UserMessageController.java | 4 +- .../api/WebShellAgentController.java | 15 +- .../api/WebShellSkillsController.java | 0 .../WorkspaceCsiApplicationController.java | 4 +- .../api/WorkspaceDiscoveryController.java | 7 +- .../api/WorkspaceFileController.java | 3 +- ...olPublicationStorageAutoConfiguration.java | 25 + .../managedagent/config/BrokerSecurity.java | 0 .../config/EventTransportConfiguration.java | 161 ++ .../config/ExecutionConfiguration.java | 0 .../config/HarnessConfiguration.java | 18 +- .../config/InternalSurfaceConfiguration.java | 0 .../config/ManagedAgentConfiguration.java | 138 ++ .../ManagedAgentInternalConfiguration.java | 2 +- .../ManagedAgentPersistenceConfiguration.java | 0 .../config/ManagedAgentProperties.java | 1329 ++--------------- .../config/ManagedAgentWebConfiguration.java | 0 .../config/ManagedArtifactConfiguration.java | 2 +- .../config/ManagedLocalConfiguration.java | 0 .../config/ManagedSchemaConfiguration.java | 0 .../ManagedSessionJsonConfiguration.java | 4 +- .../config}/ManagedSessionReaderVersion.java | 16 +- .../config/RuntimeBrokerConfiguration.java | 18 +- .../config/RuntimeRetentionConfiguration.java | 8 +- .../config/SshRuntimeSettings.java | 40 +- .../config/ToolPublicationConfiguration.java | 4 +- .../managedagent/config/WebConfiguration.java | 0 .../runtime/EmbeddedRuntimeBroker.java | 271 +--- .../ResidentWorkspaceRetirementMain.java | 13 +- .../maintenance}/WorkspaceCsiHostedMain.java | 27 +- .../WorkspaceCsiProtectionMain.java | 5 +- .../WorkspaceCsiRegistrationMain.java | 4 +- .../WorkspaceCsiRetirementMain.java | 8 +- .../maintenance}/WorkspaceCsiRuntimeMain.java | 36 +- .../maintenance/WorkspaceCsiSessionMain.java | 71 + .../WorkspaceCsiWorkerAckMain.java | 41 +- .../WorkspaceMaintenanceRecoveryMain.java | 7 +- .../maintenance}/WorkspaceMigrationMain.java | 24 +- .../WorkspaceRecoveryCommand.java | 23 +- .../maintenance/WorkspaceRecoveryMain.java | 88 ++ .../WorkspaceStorageRegistrationMain.java | 5 +- .../preflight/HarnessBrokerUrl.java | 0 .../preflight/ManagedAgentPreflight.java | 39 +- .../preflight/ResolvedConfiguration.java | 0 .../preflight/ServiceAccount.java | 0 .../http}/RuntimeBrokerHttpServer.java | 9 +- ...ot.autoconfigure.AutoConfiguration.imports | 0 .../resources/application-managed-local.yml | 0 .../src/main/resources/application.yml | 0 .../artifact/ManagedArtifactPolicy.java | 15 - .../config/EventTransportConfiguration.java | 278 ---- .../config/ManagedAgentConfiguration.java | 45 - .../persistence/WorkspaceCsiSessionMain.java | 119 -- .../persistence/WorkspaceRecoveryMain.java | 163 -- .../managed-agent-server/verification/pom.xml | 731 +++++++++ .../src/test/embedding-host/pom.xml | 0 .../test/java/example/EmbeddingHostTest.java | 0 .../test/java/example/EnabledHostTest.java | 0 .../WorkspaceBrowserFixtureMain.java | 0 .../WorkspaceRecoveryApplicationTest.java | 0 .../qwen/code/managedagent/ActionJournal.java | 4 +- .../AliyunToolPublicationObjectStoreTest.java | 4 +- .../managedagent/AutomationHarnessFake.java | 28 +- .../managedagent/BackfillWitnessesTest.java | 0 .../code/managedagent/BrokerSignatures.java | 0 .../managedagent/ChannelContractTest.java | 0 .../ChildResultRelayStoreTest.java | 16 +- .../ContainerArtifactSelectionTest.java | 2 +- .../EmbeddedRuntimeBrokerTest.java | 21 +- .../EventTransportBrokerGate.java | 30 +- ...ventTransportBrokerGateStatisticsTest.java | 0 .../EventTransportMultiNodeGate.java | 184 ++- .../managedagent/ExtensionRecordJournal.java | 40 +- .../HarnessEventProjectorTest.java | 4 +- .../managedagent/HookAdmissionHistory.java | 6 +- .../HostedAutomationCrashFixtureMain.java | 2 +- .../HostedAutomationFaultGatesIT.java | 6 +- .../HostedAutomationSpringCrashIT.java | 0 .../HostedBackgroundPublicationGate.java | 12 +- .../managedagent/HostedCancellationProbe.java | 4 +- .../HostedChannelCrashFixtureMain.java | 0 .../HostedChannelFaultGatesIT.java | 3 +- .../HostedChannelGateSupport.java | 3 +- .../HostedChannelProcessCrashIT.java | 0 .../HostedCleanupRetentionMySqlIT.java | 0 .../HostedCleanupRetentionTest.java | 18 +- .../HostedCommittedEventLineReplayIT.java | 0 .../HostedConcurrentTurnBurstMySqlIT.java | 2 +- .../managedagent/HostedDurableWorkers.java | 0 .../HostedEventStreamLivenessIT.java | 0 .../managedagent/HostedEventStreamRelay.java | 0 .../HostedEventStreamRelayTest.java | 0 .../code/managedagent/HostedExtensionsIT.java | 0 .../managedagent/HostedGateTestSupport.java | 10 +- .../HostedGateTestSupportTest.java | 0 .../managedagent/HostedHarnessMySqlIT.java | 2 +- .../HostedHookCancelFaultGatesIT.java | 5 +- .../managedagent/HostedHookFaultGatesIT.java | 5 +- .../HostedHookMessageDisplayReloadIT.java | 0 .../HostedHookReplyLossFaultGatesIT.java | 7 +- .../managedagent/HostedHookStreamGapIT.java | 0 .../managedagent/HostedLifecycleRelay.java | 0 .../HostedLifecycleRelayTest.java | 0 .../managedagent/HostedMcpReplyLossIT.java | 5 +- .../HostedMcpRestartFaultGatesIT.java | 5 +- .../HostedMcpResultDeliveryIT.java | 5 +- .../HostedMcpStoreFaultGatesIT.java | 7 +- .../managedagent/HostedMcpStreamGapIT.java | 2 +- .../HostedMonitorRunFaultGatesIT.java | 11 +- .../managedagent/HostedMonitorRunProbe.java | 5 +- .../HostedMySqlIsolationActivationTest.java | 0 .../HostedMySqlIsolationExtension.java | 0 .../HostedMySqlIsolationExtensionTest.java | 0 .../managedagent/HostedMySqlIsolationIT.java | 0 .../HostedOperatorRecoveryProbe.java | 6 +- .../HostedOperatorRecoveryProbeTest.java | 0 .../HostedProcessCrashFixtureMain.java | 6 +- .../managedagent/HostedProcessCrashIT.java | 11 +- .../managedagent/HostedProcessLiveness.java | 0 .../HostedProcessLivenessTest.java | 0 .../HostedProviderControlProbe.java | 6 +- .../HostedProviderMediaProbe.java | 6 +- .../HostedProviderMediaProbeTest.java | 2 +- .../managedagent/HostedPublicWorkspaceIT.java | 10 +- .../managedagent/HostedRecoveryWedgeGate.java | 13 +- .../HostedSessionRelayFaultGatesIT.java | 6 +- .../HostedSessionRelayForwardingProxy.java | 0 .../HostedShellLifecycleFixtureMain.java | 3 +- .../HostedShellLifecycleRecoveryIT.java | 8 +- .../managedagent/HostedShellOutputProbe.java | 11 +- .../code/managedagent/HostedSseGapProbe.java | 4 +- .../managedagent/HostedTeamLeadMySqlIT.java | 3 +- .../HostedTeamMemberCommandMySqlIT.java | 2 +- .../HostedWorkflowFaultGatesIT.java | 4 +- .../managedagent/HostedWorkflowProbe.java | 0 .../HostedWorkflowRelayFaultGatesIT.java | 8 +- .../HostedWorkspaceColdLoadProbe.java | 0 .../HostedWorkspaceToolTurnIT.java | 14 +- .../Issue13180HardenedVerificationTest.java | 6 +- .../Issue13180InternalPortTest.java | 2 +- .../Issue13180SignedModeTest.java | 0 .../JournalEventPublicationPointTest.java | 8 +- .../managedagent/LegacyCwdOperations.java | 10 +- .../qwen/code/managedagent/LegacyEvents.java | 0 .../code/managedagent/ManagedActionsTest.java | 29 +- .../ManagedAgentApiContractTest.java | 33 +- .../ManagedAgentDefinitionAdmissionTest.java | 22 +- .../ManagedAgentDefinitionTest.java | 0 .../managedagent/ManagedAgentMySqlIT.java | 103 +- .../managedagent/ManagedAgentPackagingIT.java | 11 +- .../ManagedAgentServerIntegrationTest.java | 32 +- .../ManagedAgentStoreLocaleTest.java | 4 +- .../ManagedApprovalChangeMySqlIT.java | 0 .../ManagedApprovalChangeTest.java | 30 +- .../ManagedApprovalSelectionTest.java | 12 +- .../ManagedArtifactApiIntegrationTest.java | 19 +- .../ManagedArtifactReadIntegrationTest.java | 4 +- .../ManagedArtifactSchedulingTest.java | 4 +- .../ManagedAutomationRecordContractTest.java | 6 +- .../ManagedChannelJdbcContract.java | 16 +- .../ManagedChannelJdbcContractTest.java | 0 .../ManagedChannelRecordContractTest.java | 4 +- ...agedChildAcceptanceRecordContractTest.java | 4 +- .../ManagedChildRunRecordContractTest.java | 6 +- .../ManagedConfigInstallContractTest.java | 4 +- .../ManagedContextCompressionMySqlIT.java | 4 +- ...anagedContextCompressionOperationTest.java | 6 +- .../ManagedCwdChangeOperationTest.java | 34 +- .../ManagedCwdOperationContractShapeTest.java | 0 .../ManagedEventEnvelopeContractTest.java | 4 +- .../ManagedEventIdentityMigrationTest.java | 4 +- .../ManagedExtensionCommandVerifierTest.java | 13 +- ...edExtensionConfigurationAdmissionTest.java | 9 +- .../ManagedExtensionConfigurationMySqlIT.java | 0 .../ManagedExtensionCreationMySqlIT.java | 0 ...anagedExtensionProjectionContractTest.java | 6 +- .../ManagedExtensionRecordContractTest.java | 6 +- .../ManagedExtensionRecordStoreTest.java | 33 +- .../ManagedHookAdmissionIndexTest.java | 2 +- .../ManagedHookCatalogContractTest.java | 0 .../ManagedHookRecordContractTest.java | 10 +- .../ManagedMaterializationDeferTest.java | 8 +- .../ManagedMcpCatalogContractTest.java | 0 .../ManagedMcpRecordContractTest.java | 4 +- .../ManagedMcpSettingsMySqlIT.java | 0 .../ManagedMcpSettingsStoreTest.java | 2 +- .../managedagent/ManagedModelContextTest.java | 26 +- .../ManagedModelSelectionTest.java | 24 +- .../ManagedNativeJournalSnapshotsMySqlIT.java | 2 +- .../ManagedNativeJournalSnapshotsTest.java | 10 +- .../ManagedOperationSchemaUpgradeTest.java | 9 +- .../ManagedRenameReconciliationMySqlIT.java | 0 .../ManagedRenameReconciliationTest.java | 11 +- .../ManagedRuntimeRetentionRaceMySqlIT.java | 0 .../ManagedRuntimeRetentionRaceTest.java | 14 +- .../ManagedSchemaJournalAtomicityTest.java | 4 +- .../ManagedSchemaJournalMySqlIT.java | 0 ...nagedSessionCommandRecordContractTest.java | 4 +- .../ManagedSessionCreationSkillsTest.java | 4 +- .../ManagedSessionLifecycleTest.java | 39 +- ...nagedSessionMessageRecordContractTest.java | 4 +- .../ManagedSessionMessageStoreTest.java | 8 +- .../ManagedSessionOperationMigrationTest.java | 4 +- .../ManagedSessionOperationStoreTest.java | 11 +- .../managedagent/ManagedSessionOwnerTest.java | 6 +- .../ManagedSessionQueryMySqlIT.java | 0 .../managedagent/ManagedSessionQueryTest.java | 4 +- .../ManagedSessionStoreBindingTest.java | 4 +- ...anagedSessionStoreContractFixtureTest.java | 4 +- .../ManagedSessionStoreIntegrationTest.java | 10 +- ...ManagedSessionStoreProcessFixtureMain.java | 20 +- ...anagedSessionToolProfileMigrationTest.java | 0 .../ManagedTaskCancelOperationTest.java | 21 +- .../ManagedTaskEventJournalTest.java | 14 +- .../ManagedTeamRecordContractTest.java | 6 +- .../managedagent/ManagedTeamStoreTest.java | 6 +- .../ManagedToolDetailsMySqlIT.java | 0 .../ManagedToolStageContractTest.java | 6 +- .../ManagedTurnCommandOperationMySqlIT.java | 5 +- .../ManagedTurnCommandOperationTest.java | 14 +- .../managedagent/ManagedTurnQueryTest.java | 4 +- .../ManagedUserMessageMySqlIT.java | 0 .../ManagedUserMessageStoreTest.java | 8 +- .../ManagedWebShellRenameTest.java | 0 .../ManagedWorkspaceAdmissionTest.java | 152 +- .../ManagedWorkspaceRolesMigrationTest.java | 6 +- .../MySqlForkRoutingListener.java | 0 .../MySqlForkRoutingListenerTest.java | 0 .../code/managedagent/MySqlLockProbe.java | 0 .../code/managedagent/OpenApiContract.java | 0 .../managedagent/PlannedTaskContractTest.java | 0 .../PublicationJournalFixture.java | 16 +- .../RuntimeBrokerBracketKeyBindingTest.java | 2 +- ...imeBrokerConfigurationIntegrationTest.java | 12 +- .../RuntimeBrokerDefaultOnTest.java | 4 +- .../RuntimeBrokerFlywaySchemaTest.java | 8 +- .../qwen/code/managedagent/SchedulerHold.java | 0 .../code/managedagent/SchedulerHoldTest.java | 0 .../SessionMessageRelayStoreTest.java | 31 +- ...StandaloneDataSourceConfigurationTest.java | 0 .../SurfaceAdmissionAcceptanceTest.java | 222 ++- .../ToolPublicationConfigurationTest.java | 7 +- .../ToolPublicationContractTest.java | 2 +- .../ToolPublicationLifecycleMySqlIT.java | 12 +- .../ToolPublicationLockOrderMySqlIT.java | 34 +- .../ToolPublicationRecoveryMySqlIT.java | 0 .../ToolPublicationStoreTest.java | 31 +- .../code/managedagent/TurnEventLines.java | 0 .../managedagent/VerificationFixtures.java | 69 + .../WorkspaceCreationProcessFixtureMain.java | 4 +- .../WorkspaceSessionCloseMySqlIT.java | 20 +- .../WorkspaceSessionCloseTest.java | 18 +- .../WorkspaceSessionRetentionMySqlIT.java | 38 +- .../WorkspaceSessionRetentionTest.java | 22 +- .../event/LiteTopicEventTransportTest.java | 20 +- .../LiteTopicPublicEventTransportTest.java | 4 +- .../ResidentWorkspaceProvisionerTest.java | 17 +- .../WorkspaceCsiResourceGuardTest.java | 10 +- .../WorkspaceCsiRuntimeIdentityTest.java | 20 +- .../WorkspaceCsiRuntimeProvisionerTest.java | 16 +- .../workspace/ChildWorktreeGitTest.java | 160 +- .../api/ApiExceptionHandlerTest.java | 8 +- .../ManagedSnapshotRestoreResponseTest.java | 4 +- .../api/ManagedSurfaceCapture.java | 0 .../api/ManagedSurfaceCaptureTest.java | 0 .../api/SignatureAuthFilterTest.java | 39 +- .../managedagent/api/SurfaceRegistry.java | 0 .../api/SurfaceRegistryGateNegativeTest.java | 0 .../api/SurfaceRegistryGateTest.java | 0 .../api/TenantContextFilterTest.java | 0 .../api/ToolPublicationControllerTest.java | 10 +- .../api/TrustedActorHeaderFilterTest.java | 0 ...WorkspaceCsiApplicationControllerTest.java | 2 +- .../api/WorkspaceFileControllerTest.java | 0 .../architecture/ModuleArchitectureTest.java | 59 + .../AliyunToolPublicationObjectStoreTest.java | 2 + .../artifact/ManagedArtifactServiceTest.java | 11 +- .../AutomationScannerConcurrencyMySqlIT.java | 6 +- .../automation/AutomationScannerTest.java | 120 +- .../automation/CronSlotsTest.java | 0 .../ManagedAutomationServiceTest.java | 16 +- .../ManagedAgentDefinitionServiceTest.java | 13 +- .../ManagedHookCatalogServiceTest.java | 3 +- .../catalog/ManagedMcpCatalogServiceTest.java | 3 +- .../ManagedMcpManagementServiceTest.java | 13 +- .../channel/ManagedChannelServiceTest.java | 65 +- .../config/BrokerSecurityTest.java | 0 .../EventTransportConfigurationTest.java | 20 +- .../config/HarnessConfigurationTest.java | 114 ++ .../config/HarnessCredentialsFileTest.java | 0 .../InternalSurfaceConfigurationTest.java | 0 .../config/ManagedAgentEmbeddingTest.java | 0 .../ManagedAgentInternalEmbeddingTest.java | 2 +- .../config/ManagedAgentPropertiesTest.java | 191 ++- .../ManagedArtifactConfigurationTest.java | 0 .../config/ManagedLocalConfigurationTest.java | 0 .../ManagedSchemaConfigurationTest.java | 0 .../ManagedSessionJsonConfigurationTest.java | 2 +- .../ManagedSessionReaderVersionTest.java | 15 +- .../RuntimeBrokerConfigurationTest.java | 4 +- .../RuntimeRetentionConfigurationTest.java | 8 +- .../config/SessionStoreDefaultsTest.java | 2 +- ...olPublicationStorageConfigurationTest.java | 20 +- .../config/runtime/EmbeddedBrokerFixture.java | 17 + .../runtime/ExternalWorkspaceFactoryTest.java | 28 +- .../AgentDefinitionExtensionsTest.java | 6 +- .../event/ManagedEventEnvelopesTest.java | 2 +- .../AgentDefinitionCompilerTest.java | 6 +- .../persistence/EventIdentityTest.java | 4 +- .../ExternalStorageRegistrationMySqlIT.java | 7 +- .../ManagedHistoryMaintenanceRecordsTest.java | 2 +- .../ManagedHostedRecoveryRecordsTest.java | 2 +- .../security/WriterCredentialPolicyTest.java | 4 +- .../workspace/WorkspaceSelectionTest.java | 2 +- .../CompositeCommittedEventPublisherTest.java | 2 + .../event/EventTransportConsumerMySqlIT.java | 0 .../event/EventTransportConsumerSqlTest.java | 39 +- .../event/EventTransportConsumerTest.java | 14 +- .../event/EventTransportNodeMain.java | 76 +- .../event/MessageMaterializerTest.java | 2 +- .../event/TransportWakeActivatorTest.java | 5 +- .../ManagedExtensionPackageServiceTest.java | 22 +- .../harness/HarnessPoolConnectorTest.java | 9 +- .../harness/HarnessSdkValuesTest.java | 157 ++ .../HostedHarnessCreateOrLoadPinningTest.java | 2 +- .../harness/HostedHarnessPoolMySqlIT.java | 7 +- .../KubernetesHarnessDiscoveryTest.java | 0 ...HostedHarnessColdCancelRegressionTest.java | 39 +- .../QwenHostedHarnessConnectorTest.java | 72 +- ...HostedHarnessNewSessionRegressionTest.java | 3 +- ...QwenHostedMcpManagementAttachmentTest.java | 2 +- ...esidentWorkspaceRetirementMainMySqlIT.java | 4 +- .../ResidentWorkspaceRetirementMainTest.java | 3 +- .../WorkspaceCsiHostedMainTest.java | 2 +- .../WorkspaceCsiRuntimeMainTest.java | 6 +- .../WorkspaceCsiSessionMainTest.java | 61 +- .../WorkspaceCsiWorkerAckMainTest.java | 4 +- .../WorkspaceRecoveryCommandTest.java | 2 +- .../model/RequestDigestsTest.java | 0 .../AgentStateStoreProxyTransactionTest.java | 96 ++ .../AgentStateStoreTransactionTest.java | 2 +- .../AliyunToolPublicationReadRetryTest.java | 4 +- .../AutomationCommandTimestampTest.java | 0 .../AutomationLedgerStoreTest.java | 5 +- .../persistence/ChildWorkspaceStoreTest.java | 4 +- .../ConsolidatedActionStoreTest.java | 18 +- .../ConsolidatedChannelStoreTest.java | 7 +- .../persistence/ConsolidatedStoreFixture.java | 0 .../ConsolidatedTaskEventStoreTest.java | 14 +- .../CsiRuntimeMarkerProbeTest.java | 2 +- .../persistence/ExecutedQueryPlan.java | 0 .../persistence/ExecutedQueryPlanTest.java | 0 .../ExternalStorageRegistrationTest.java | 43 +- .../FaultingExtensionRecordStore.java | 27 +- .../persistence/HarnessPlacementMySqlIT.java | 0 .../HarnessPlacementStoreTest.java | 5 +- .../HostedCleanupDebtStoreTest.java | 6 +- .../HostedWorkspaceConcurrencyIT.java | 29 +- .../HostedWorkspaceStorageGuardMySqlIT.java | 7 +- ...solatedManagedSchemaMySqlTestDatabase.java | 0 .../persistence/LocalStorageAliasMySqlIT.java | 0 .../persistence/LocalStorageAliasTest.java | 24 +- .../persistence/ManagedActionStoreTest.java | 25 +- .../ManagedAgentCsiMutationMySqlIT.java | 50 +- ...dAgentEventSequenceConcurrencyMySqlIT.java | 0 ...agedAgentEventSequenceConcurrencyTest.java | 4 +- ...nagedAgentStoreChildCreationFenceTest.java | 9 +- .../ManagedAgentTurnDeferralTest.java | 4 +- .../ManagedExtensionAgentLaunchTest.java | 2 +- ...nagedExtensionRecordLifecycleGateTest.java | 0 ...xtensionRecordVerdictReconcileMySqlIT.java | 3 +- ...edExtensionRecordVerdictReconcileTest.java | 3 +- .../ManagedMessagePagingMySqlIT.java | 0 .../persistence/ManagedMessagePagingTest.java | 11 +- .../ManagedMessageSnapshotRemovalTest.java | 13 +- .../ManagedModelContextRetentionTest.java | 5 +- .../ManagedNativeSnapshotRetirementTest.java | 4 +- .../persistence/ManagedReceiptScopeTest.java | 6 +- .../persistence/ManagedReceiptsTest.java | 0 .../ManagedRuntimeRetentionGuardMySqlIT.java | 0 .../ManagedRuntimeRetentionGuardTest.java | 18 +- .../ManagedSchemaBusinessMySqlIT.java | 0 .../ManagedSchemaBusinessStoreTest.java | 19 +- .../ManagedSchemaJdbcTimeTest.java | 0 .../ManagedSchemaPublicationLoadMySqlIT.java | 13 +- .../ManagedSchemaPublicationMySqlIT.java | 0 .../ManagedSchemaPublicationStoreTest.java | 8 +- .../ManagedSchemaRelayLoadMySqlIT.java | 4 +- .../ManagedSchemaSessionMySqlIT.java | 4 +- .../ManagedSchemaSessionStoreTest.java | 30 +- .../ManagedSessionGuardConvoyIT.java | 42 +- .../ManagedSessionGuardMappingTest.java | 3 +- .../ManagedSessionHistoryTest.java | 8 +- .../ManagedSessionNativeReadTest.java | 21 +- .../ManagedSessionPhysicalFenceMySqlIT.java | 1 + .../ManagedSessionPhysicalFenceTest.java | 15 +- .../ManagedSessionStoreCredentialTest.java | 17 +- .../ManagedSnapshotNumericPagingTest.java | 12 +- .../persistence/ManagedSnapshotPoolTest.java | 13 +- .../ManagedToolResultCaptureTest.java | 2 +- .../ManagedToolResultProjectorTest.java | 12 +- .../ManagedWorkspaceRolesMySqlIT.java | 0 .../persistence/O4FaultProcess.java | 0 .../persistence/O4FileObjects.java | 2 +- .../persistence/O4MySqlDatabase.java | 0 .../persistence/O4MySqlDatabaseTest.java | 0 .../managedagent/persistence/O4MySqlGate.java | 5 +- .../managedagent/persistence/O4OssGate.java | 11 +- .../persistence/PersistenceTestAccess.java | 7 + .../persistence/PrivateCsiFixture.java | 10 + .../RecoverySettlementStoreMySqlIT.java | 0 .../RecoverySettlementStoreTest.java | 6 +- .../ResidentRetirementFixture.java | 72 +- .../ResidentWorkspaceGraceDeadlineTest.java | 8 +- ...entWorkspaceReclamationProcessMySqlIT.java | 20 +- .../ResidentWorkspaceRetirementMySqlIT.java | 0 ...dentWorkspaceRetirementProcessMySqlIT.java | 10 +- .../ResidentWorkspaceRetirementStoreTest.java | 67 +- ...identWorkspaceReuseConcurrencyMySqlIT.java | 0 ...ResidentWorkspaceReuseConcurrencyTest.java | 15 +- .../persistence/ResidentWorkspacesTest.java | 29 +- ...essionResourceCollectionCollectorTest.java | 63 +- .../SessionResourceCollectionMySqlIT.java | 0 .../persistence/SessionRowLockOrderTest.java | 83 +- .../persistence/SessionWorktreeMySqlIT.java | 0 .../persistence/SessionWorktreeStoreTest.java | 39 +- .../SharedLocalProjectsMySqlIT.java | 0 .../persistence/SharedLocalProjectsTest.java | 13 +- .../persistence/SshWorkspaceMySqlIT.java | 0 .../persistence/SshWorkspaceStoreTest.java | 22 +- .../persistence/StoreFaultGate.java | 8 +- .../persistence/StoreFaultGateTest.java | 2 +- .../persistence/StoreModelsTest.java | 6 +- ...ToolPublicationAcknowledgementMySqlIT.java | 0 .../ToolPublicationAcknowledgementTest.java | 19 +- ...olPublicationAsyncVerificationMySqlIT.java | 0 .../ToolPublicationAsyncVerificationTest.java | 24 +- .../ToolPublicationCollectorMySqlIT.java | 0 .../ToolPublicationCollectorTest.java | 14 +- ...lPublicationRetentionClockFixtureMain.java | 3 +- .../ToolPublicationRetentionStoreTest.java | 27 +- .../persistence/WakeRecoveryMySqlIT.java | 0 .../persistence/WakeRecoveryStoreTest.java | 5 +- .../WorkspaceCreationStoreTest.java | 12 +- .../WorkspaceCsiApplicationHistoryTest.java | 12 +- ...rkspaceCsiApplicationInventoryMySqlIT.java | 0 .../WorkspaceCsiApplicationInventoryTest.java | 110 +- .../WorkspaceCsiCheckpointMySqlIT.java | 0 ...rkspaceCsiCheckpointSnapshotStoreTest.java | 19 +- .../WorkspaceCsiHostedFilesTest.java | 3 +- .../WorkspaceCsiNativeActivationGateTest.java | 33 +- ...paceCsiNativeActivationRenewalMySqlIT.java | 0 .../WorkspaceCsiReservationMySqlIT.java | 0 .../WorkspaceCsiReservationStoreTest.java | 15 +- .../WorkspaceCsiRetainedArtifactsMySqlIT.java | 0 .../WorkspaceCsiRetainedArtifactsTest.java | 20 +- .../WorkspaceCsiRetirementStoreTest.java | 9 +- ...orkspaceCsiRuntimeConstructionFixture.java | 13 +- .../WorkspaceCsiSessionGuardTest.java | 30 +- .../WorkspaceCsiSqlWorkerMySqlIT.java | 25 +- .../WorkspaceCsiWorkerAckStoreTest.java | 35 +- .../WorkspaceDirectorySelectionMySqlIT.java | 0 .../persistence/WorkspaceFileMySqlIT.java | 0 .../persistence/WorkspaceFileStoreTest.java | 27 +- .../WorkspaceFileUploadMySqlIT.java | 0 .../WorkspaceFileUploadStoreTest.java | 13 +- .../WorkspaceLifecycleMySqlIT.java | 22 +- .../WorkspaceLifecycleStoreTest.java | 86 +- ...rkspaceMaintenanceLinuxAcceptanceMain.java | 8 +- .../WorkspaceMigrationMySqlIT.java | 33 +- .../WorkspaceMigrationStoreTest.java | 46 +- .../WorkspaceNativeAcceptanceMain.java | 0 .../WorkspaceProfileLifecycleMySqlIT.java | 13 +- .../WorkspaceProfileLifecycleTest.java | 58 +- .../WorkspaceQueryScaleMySqlIT.java | 24 +- .../persistence/WorkspaceRecoveryMySqlIT.java | 0 .../WorkspaceRecoveryReaderAssertions.java | 10 +- .../WorkspaceRecoveryStoreTest.java | 86 +- .../WorkspaceSchemaConsolidationTest.java | 13 +- .../WorkspaceStorageGuardTest.java | 37 +- .../persistence/WorkspaceTestStorage.java | 4 +- .../ExternalRuntimeConfigurationTest.java | 10 +- .../preflight/HarnessBrokerUrlTest.java | 0 .../preflight/ManagedAgentPreflightTest.java | 0 .../preflight/ResolvedConfigurationTest.java | 0 .../preflight/ServiceAccountTest.java | 0 .../ActionResponseCoordinatorTest.java | 3 +- .../AdmittedTurnRetryTerminalStateTest.java | 50 +- .../session/ChildWorktreeGitVersionTest.java | 2 +- .../ContextCompressionCoordinatorTest.java | 5 +- .../session/CoordinatorTestSupport.java | 0 .../managedagent/session/EventStreams.java | 0 .../session/HarnessCoordinatorTest.java | 119 +- .../HostedCleanupRetryCoordinatorTest.java | 33 +- .../session/Issue13181QueryBudgetTest.java | 24 +- .../session/ManagedAgentReplayOutageTest.java | 6 +- ...ManagedAgentServiceChildWorkspaceTest.java | 5 +- .../ManagedAgentServiceWorkflowChildTest.java | 5 +- .../ManagedAgentServiceWorktreeTest.java | 7 +- .../ManagedAgentSessionCapabilitiesTest.java | 15 +- .../ManagedAgentWorkspaceReplayTest.java | 23 +- .../session/ManagedEventReplayTest.java | 0 .../ManagedEventStreamServiceTest.java | 20 +- .../session/ManagedInputAttachmentTest.java | 8 +- .../session/MessageProjectionGapHealTest.java | 6 +- .../session/OperationReconcilerTest.java | 12 +- .../OperationRetryTerminalStateTest.java | 70 +- .../session/OwnerSubscriptionsTest.java | 6 +- .../session/RecoverySettlementTest.java | 49 +- .../ReplayFloorAdvancerBacklogTest.java | 0 .../session/ReplayFloorAdvancerTest.java | 1 + .../session/ReplayFloorRetractionTest.java | 0 .../session/ReplayFloorSchedulerTest.java | 0 .../session/ResidentAcceptanceSnapshot.java | 0 .../session/SessionEventHubPinningTest.java | 0 .../session/SessionEventHubTest.java | 0 .../SessionLifecycleCoordinatorTest.java | 99 +- .../SessionLifecycleProtocolChoiceTest.java | 3 +- .../SessionLifecycleServiceResumeTest.java | 13 +- .../session/SessionMessageRelayTest.java | 24 +- .../SessionRecoveryInitialStateTest.java | 25 +- .../session/SessionRecoveryServiceTest.java | 12 +- .../session/TaskCancelCoordinatorTest.java | 16 +- .../session/WakeRecoveryTest.java | 4 +- .../session/WorkspaceRecoveryTest.java | 0 .../workspace/ChildResultRelayTest.java | 39 +- .../workspace/ChildWorkspaceServiceTest.java | 38 +- .../workspace/SessionWorktreeGitTest.java | 16 +- .../workspace/SessionWorktreeServiceTest.java | 12 +- .../workspace/SshWorkspaceServiceTest.java | 2 +- .../HostedWorkspaceRecoveryWorkerIT.java | 8 +- .../runtime/ResidentHostedRuntimeFixture.java | 8 +- .../RuntimeRecoveryCoordinatorTest.java | 2 +- .../runtime/RuntimeRetentionJobTest.java | 2 +- .../runtime/WorkspaceRecoveryContract.java | 38 +- .../WorkspaceRuntimeInstallProbeTest.java | 31 +- .../WorkspaceRuntimeResolutionPivotTest.java | 10 +- .../runtime/WorkspaceRuntimeTest.java | 34 +- .../WorkspaceRuntimeTransportTest.java | 10 +- .../code/runtimebroker/BrokerProcess.java | 42 +- .../BrokerRenewalPinningTest.java | 13 +- .../code/runtimebroker/BrokerValuesTest.java | 23 +- .../BrokerVirtualThreadPinningTest.java | 15 +- .../qwen/code/runtimebroker/CarrierCount.java | 0 .../code/runtimebroker/CarrierCountTest.java | 6 +- .../ConcurrencyStorageFaultGateTest.java | 21 +- .../ContextInstallationFaultGateTest.java | 8 +- .../CsiNativeActivationProofTest.java | 11 +- .../CsiNativeFileHistoryTest.java | 11 +- .../CsiNativeFinalOutputProofTest.java | 15 +- .../runtimebroker/CsiNativeTextProofTest.java | 19 +- .../CsiNativeToolResultProofTest.java | 6 +- .../CsiOwnershipRevisionTest.java | 0 .../runtimebroker/CsiRetirementSealTest.java | 4 +- .../DelegatingBindingRepository.java | 0 .../DelegatingToolExecutionRepository.java | 0 .../DriverManagerDataSource.java | 0 .../DurableLocalRuntimeFaultGateTest.java | 19 +- .../DurableRuntimeRecoveryTest.java | 17 +- .../ExecutionTakeoverContract.java | 6 +- .../ExternalRuntimeWorkerFaultGateTest.java | 8 +- .../FakeAttestationWorkerTest.java | 0 .../FakeKubernetesRuntimeClient.java | 4 +- .../runtimebroker/FaultGateControlTest.java | 8 +- .../runtimebroker/FaultGateTransport.java | 2 +- .../qwen/code/runtimebroker/FaultProxy.java | 34 +- .../runtimebroker/HarnessOwnershipTest.java | 4 +- .../runtimebroker/InMemoryRepositoryTest.java | 20 +- .../Issue13183AdversarialTest.java | 15 +- .../Issue13183RegressionTest.java | 23 +- .../JdbcPhysicalStorageReleaseTest.java | 21 +- .../runtimebroker/JdbcRepositoryTest.java | 8 +- .../KubernetesRuntimeWorkerTest.java | 6 +- .../LocalRebootFaultGateTest.java | 11 +- .../LostResponseFaultGateTest.java | 11 +- .../MaintenanceProbeWaiterTest.java | 13 +- .../ManagedCheckpointHistoryMySqlIT.java | 2 +- .../ManagedContextRecoveryTest.java | 14 +- ...ManagedCsiAcknowledgementProtocolTest.java | 6 +- .../ManagedCsiFilesHttpTransportTest.java | 3 +- .../ManagedCsiHttpTransportTest.java | 8 +- .../ManagedExtensionExecutionProfileTest.java | 0 .../runtimebroker/ManagedSchemaMySqlIT.java | 8 +- .../ManagedToolResultConformanceTest.java | 11 +- .../ProcessCrashFaultGateTest.java | 8 +- .../qwen/code/runtimebroker/ProcessTrees.java | 12 +- .../ProviderRuntimeTransportTest.java | 6 +- .../RecoverableProcessProvisioner.java | 2 +- .../RuntimeBrokerStorageAdmissionTest.java | 8 +- .../RuntimeDispatchAdmissionTest.java | 8 +- .../RuntimeExtensionMetadataControlTest.java | 4 + .../RuntimeMaintenanceRecoveryTest.java | 37 +- .../RuntimeProvisionSeedTest.java | 7 +- .../RuntimeRecoveryContract.java | 38 +- .../runtimebroker/RuntimeRecoveryTest.java | 7 +- .../RuntimeSessionTombstoneTest.java | 6 +- .../RuntimeSshAcknowledgementTest.java | 18 +- .../TakeoverReconciliationTest.java | 9 +- .../qwen/code/runtimebroker/TcpRelay.java | 14 +- .../ToolExecutionRecordFixtures.java | 4 +- .../UnknownExecutionObservationTest.java | 6 +- .../WorkspaceCreationSchemaMySqlIT.java | 4 +- .../WorkspaceExecutionProfileTest.java | 4 +- .../WorkspaceMigrationRepositoryTest.java | 8 +- .../adapter}/HttpRuntimeTransportTest.java | 22 +- .../HttpRuntimeTransportTestAccess.java | 2 +- ...edCsiAcknowledgementHttpTransportTest.java | 3 +- ...agedRuntimeAttestationConformanceTest.java | 2 +- .../security/AesGcmSecretProtectorTest.java | 3 +- ...ManagedExtensionExecutionContractTest.java | 2 +- .../ManagedSchemaCatalogTest.java | 7 +- .../managedschema/ManagedSchemaMapping.java | 0 .../ManagedSchemaProjectionTest.java | 0 .../managedschema/ManagedSchemaRowsTest.java | 13 +- .../managedschema/ManagedSchemaTest.java | 2 + .../ManagedSchemaTimeContractTest.java | 18 +- .../managedschema/ManagedSchemaTimeTest.java | 0 .../ConstructorVisibilityTest.java | 6 +- .../managedworkspace/ContextBindingTest.java | 17 +- ...ManagedContextEnvelopeConformanceTest.java | 11 +- ...anagedWorkspaceBindingConformanceTest.java | 11 +- .../managedworkspace/TestWorkspaces.java | 12 +- .../managedworkspace/TextFieldProbeTest.java | 8 +- .../managedworkspace/WorkspaceAccessTest.java | 4 +- .../managedworkspace/WorkspaceActorTest.java | 4 +- .../WorkspaceCatalogTest.java | 21 +- .../managedworkspace/WorkspaceRecordTest.java | 9 +- .../WorkspaceRelativePathTest.java | 7 +- .../WorkspaceSelectionTest.java | 7 +- .../managedworkspace/WorkspaceViewTest.java | 4 +- .../BindingExecutionInventoryTest.java | 23 +- .../CsiApplicationProgressTest.java | 21 +- .../CsiFilesRetirementProfileTest.java | 13 +- .../CsiNativeReadContinuationProofTest.java | 12 +- .../CsiNativeToolReservationTest.java | 11 +- .../DispatchAuthorizationEvidenceTest.java | 19 +- .../persistence}/FaultGateRig.java | 120 +- .../persistence/InMemoryTestAccess.java | 8 + .../JdbcCsiBindingControlTest.java | 17 +- .../persistence}/JdbcPlacementGuardTest.java | 11 +- .../persistence}/JdbcRepositoryContract.java | 22 +- .../JdbcRepositorySupportTest.java | 5 +- .../JdbcRuntimeBrokerMySqlIT.java | 20 +- .../JdbcRuntimeRetentionMySqlIT.java | 9 +- .../JdbcRuntimeRetentionTest.java | 18 +- .../ManagedCheckpointHistorySqlTest.java | 26 +- .../ResidentWorkspaceAdmissionTest.java | 12 +- .../ResidentWorkspacePlacementTest.java | 11 +- .../RuntimeBrokerHttpServerTest.java | 31 +- .../persistence}/RuntimeBrokerTestAccess.java | 20 +- .../SharedHarnessOwnershipTest.java | 19 +- .../protocol}/ManagedContextProtocolTest.java | 7 +- ...bleLocalProcessRuntimeProvisionerTest.java | 24 +- .../ExternalHttpRuntimeClientTest.java | 0 .../ExternalRuntimeProvisionerTest.java | 11 +- .../ExternalWorkspaceBootContractTest.java | 11 +- .../ExternalWorkspaceProvisionerTest.java | 27 +- .../provisioning/FaultGateBroker.java | 14 +- .../KubernetesCsiLogReaderTest.java | 1 + .../KubernetesHttpRuntimeClientTest.java | 6 +- .../KubernetesRuntimeProvisionerTest.java | 22 +- .../LocalProcessRuntimeProvisionerTest.java | 39 +- .../LocalProcessStopExecutorTest.java | 4 +- .../provisioning/LocalRebootTestSupport.java | 2 +- .../provisioning/LocalRuntimeStoreTest.java | 2 +- .../provisioning/MacOsHostIdentityTest.java | 0 .../RoutingRuntimeProvisionerTest.java | 8 +- .../RuntimeHarnessDrainMySqlIT.java | 0 .../provisioning/RuntimeHarnessDrainTest.java | 20 +- .../SshConnectionManagerTest.java | 1 + .../provisioning/SshRuntimeInteropTest.java | 21 +- .../SshRuntimeProvisionerTest.java | 25 +- .../WorkerEnvironmentRoutingTest.java | 4 +- .../WorkspaceFileProcessTest.java | 0 .../service/BrokerServiceTestAccess.java | 46 + .../RuntimeBrokerServiceBindingReadTest.java | 21 +- .../service}/RuntimeBrokerServiceTest.java | 39 +- .../CsiFileFinalizeProtocolTest.java | 25 +- .../transport/CsiFileHistoryProtocolTest.java | 4 +- .../CsiNativeReadbackProtocolTest.java | 12 +- .../ManagedCsiFilesProtocolTest.java | 6 +- .../transport/ManagedCsiProtocolTest.java | 8 +- .../ManagedExtensionProtocolTest.java | 1 + .../ManagedExtensionTransportTest.java | 3 +- .../transport/ManagedMonitorProtocolTest.java | 1 - .../transport/ManagedShellProtocolTest.java | 1 - .../ConfiguredWorkspaceRegistryTest.java | 19 +- .../GrantedWorkspaceAccessPolicyTest.java | 10 +- .../qwen/managedprobes/ProbeAdvice.java | 4 +- .../managedprobes/PublicationProbeWiring.java | 38 +- .../org.junit.jupiter.api.extension.Extension | 0 ....platform.launcher.LauncherSessionListener | 0 .../resources/capability-parity/README.md | 0 .../capability-parity/managed-surface.txt | 0 .../test/resources/csi-hosted-file-turns.mjs | 0 .../csi-native-activation-fixture.json | 0 .../csi-native-activation-generator.mjs | 0 .../csi-native-final-output-fixture.json | 0 .../csi-native-final-output-generator.mjs | 2 +- .../csi-native-function-parts-baseline.json | 0 .../resources/csi-native-json-fixture.json | 0 .../resources/csi-native-json-generator.mjs | 0 .../csi-native-read-continuation-fixture.json | 0 .../csi-native-resource-schema-fixture.sql | 0 .../resources/csi-native-text-fixture.json | 0 .../test/resources/csi-native-text-turns.mjs | 0 .../resources/csi-sql-worker-acceptance.mjs | 0 .../db/managed-schema/legacy-table-map.tsv | 0 .../resources/fake-attestation-worker.mjs | 0 .../resources/hosted-definition-census.mjs | 0 .../resources/hosted-extension-process.mjs | 0 .../resources/hosted-hook-fault-process.mjs | 0 .../resources/hosted-mcp-fault-process.mjs | 0 .../managed-media-retention-generator.mjs | 0 .../resources/openapi/contract-known-gaps.txt | 0 .../original-managed-table-inventory.tsv | 0 .../resources/recovery-initial-journal.mjs | 0 .../resident-acceptance/hosted-and-model.mjs | 0 .../resident-acceptance/resident-model.mjs | 0 .../resident-tool-evidence.mjs | 0 .../resident-tool-evidence.test.mjs | 0 .../test/resources/tool-v3-interop-server.mjs | 0 .../client/e2e/managed-recovery.spec.ts | 3 + .../scripts/generate-managed-agent-api.mjs | 2 +- scripts/check-contract-open-prs.js | 2 + scripts/check-failsafe-reports.js | 12 +- scripts/check-flyway-open-prs.js | 13 +- scripts/contract-version-utils.js | 3 + scripts/create-managed-runtime-package.js | 3 +- scripts/flyway-migration-utils.js | 12 + scripts/managed-agent-dev.js | 4 +- scripts/package-managed-agent-reference.mjs | 15 +- scripts/run-managed-agent-server-e2e.ts | 6 +- scripts/tests/check-failsafe-reports.test.js | 7 +- scripts/tests/check-flyway-open-prs.test.js | 2 +- .../tests/event-transport-broker-ci.test.js | 15 +- .../event-transport-multi-node-ci.test.js | 15 +- scripts/tests/hosted-process-ci.test.js | 37 +- scripts/tests/managed-agent-dev.test.js | 4 +- .../tests/managed-agent-server-e2e.test.js | 24 +- scripts/tests/managed-runtime-package.test.js | 1 + scripts/tests/managed-runtime-release.test.js | 14 +- .../package-managed-agent-reference.test.mjs | 22 +- scripts/tests/qwen-triage-workflow.test.js | 20 +- scripts/tests/sdk-java-workflow.test.js | 60 +- 1231 files changed, 17813 insertions(+), 12514 deletions(-) create mode 100644 docs/design/2026-10-29-managed-agent-maven-modules.md create mode 100644 docs/design/2026-10-29-managed-agent-maven-modules.zh-CN.md create mode 100644 packages/managed-agent-server/adapters/pom.xml rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/config/AliyunToolPublicationStorageAutoConfiguration.java => adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/artifact/AliyunObjectStoreClient.java} (53%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters}/artifact/AliyunToolPublicationObjectStore.java (96%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters}/event/LiteTopicEventTransport.java (94%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters}/event/LiteTopicPublicEventTransport.java (87%) create mode 100644 packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/RocketMqEventClients.java create mode 100644 packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/RocketMqMessages.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters}/extensions/ManagedExtensionArchiveStore.java (84%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/workspace => adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters}/runtime/ResidentWorkspaceProvisioner.java (76%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/persistence => adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/runtime}/ResidentWorkspaceReclamation.java (84%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/workspace => adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters}/runtime/WorkspaceCsiResourceGuard.java (90%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/workspace => adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters}/runtime/WorkspaceCsiRuntimeProvisioner.java (94%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters}/workspace/ChildWorktreeGit.java (98%) create mode 100644 packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/LocalRecoveryWorker.java create mode 100644 packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/LocalWorktreeFiles.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters}/workspace/SessionWorktreeGit.java (96%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessDiscovery.java (100%) create mode 100644 packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessNegotiation.java rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnector.java (96%) create mode 100644 packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessSdkValues.java rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessTls.java (95%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/managedagent/harness/KubernetesHarnessDiscovery.java (96%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnector.java (98%) create mode 100644 packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/SdkHarnessBoundary.java create mode 100644 packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/SdkHarnessRules.java rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/managedagent/harness/UnavailableHarnessConnector.java (90%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/transport => adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/adapter}/HttpRuntimeTransport.java (98%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/adapter}/security/AesGcmSecretProtector.java (97%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalHttpRuntimeClient.java (97%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisioner.java (99%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisioner.java (97%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReader.java (96%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClient.java (89%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisioner.java (99%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisioner.java (99%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStore.java (98%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/MacOsHostIdentity.java (100%) create mode 100644 packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/NativeStorageIdentityReader.java rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/RoutingRuntimeProvisioner.java (100%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManager.java (99%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisioner.java (98%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/StaticRuntimeProvisioner.java (100%) rename packages/managed-agent-server/{ => adapters}/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkspaceFileProcess.java (90%) create mode 100644 packages/managed-agent-server/contracts/pom.xml create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ArtifactReadLease.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ManagedArtifactPolicy.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/artifact/ToolPublicationObjectStore.java (76%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ToolPublicationRetention.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/artifact/ToolPublicationStorageException.java (88%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/catalog/AgentDefinitionExtensions.java (95%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/channel/ChannelAdapterModels.java (98%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/event/CommittedEventPublisher.java (79%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/EventMessageListener.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/EventSubscriptions.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/event/EventTransport.java (92%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/event/JournalEventTransport.java (81%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/event/ManagedEventEnvelope.java (93%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/event/ManagedEventEnvelopes.java (97%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/extensions/ExtensionArchives.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/extensions/ExtensionPackages.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/extensions/ManagedExtensionPackages.java (97%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/harness/HarnessConnector.java (60%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/harness/HarnessDisabledException.java (86%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/harness/HostedHarnessRecoveryDeclinedException.java (93%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/AgentDefinitionCompiler.java (90%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ChannelDeliveryRepository.java (78%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ChannelRouteRepository.java (81%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ChannelStoreSupport.java (68%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/EventIdentity.java (93%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ExternalStorageRegistration.java (93%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedChannelRecords.java (98%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedConfigInstall.java (91%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedExtensionProjection.java (99%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedExtensionRecords.java (99%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedHarnessReleaseRecords.java (96%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedHistoryMaintenanceRecords.java (91%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedHookRecords.java (97%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedHostedRecoveryRecords.java (96%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedMcpRecords.java (97%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedSessionCommandRecords.java (98%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedSessionMessageRecords.java (97%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedSessionStoreModels.java (98%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedStorageVersions.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedTeamRecords.java (99%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedToolStageRecords.java (95%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/StorageReadPolicy.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ToolPublicationContract.java (98%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/WorkspaceCsiApplicationJson.java (87%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/WorkspaceCsiRegistration.java (96%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/runtime/ProtectionIdentity.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/runtime/Receipt.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/runtime/WorkerArtifact.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/security/WriterCredentialPolicy.java (91%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/session/AcceptanceEvidence.java (81%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/session/AgentStateStore.java (73%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/session/HarnessConflictCodes.java (94%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/session/ReplayCursorExpired.java (82%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/UserMessagePolicy.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/workspace/ChildWorkspaceException.java (91%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/workspace/ChildWorkspaceProvider.java (68%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ChildWorktreeGit.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/LocalWorkspaceFiles.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/PrivateCsiSession.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/RecoveryWorker.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ResidentReclamation.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ResidentWorkspaceCatalog.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ResidentWorkspaceSnapshot.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace}/RuntimeWarmer.java (64%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/SessionWorktreeOperations.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/SshWorkspaceFiles.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/StorageIdentityReader.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorkspaceRecoveryProtocol.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/workspace/WorkspaceSelection.java (89%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent => contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts}/workspace/WorkspaceToolProfiles.java (97%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorktreeFiles.java rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/managedagent/exception/ManagedAgentException.java (68%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessCapabilities.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessCapabilityFailure.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessCapacityFailure.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessFailure.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessGenerationFailure.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessHttpFailure.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessMutationUnknownFailure.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessProtocolFailure.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessRules.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessRuntimeRecovery.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessSessionRefusedFailure.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessSettleAnsweredFailure.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessTransportFailure.java rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/managedagent/model/ApiModels.java (99%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/managedagent/model/RequestDigests.java (95%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/managedagent/model/StoreModels.java (98%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/managedagent/model/TenantContext.java (76%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/AutomationLedgerStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ChannelInstanceStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ChildResultRelayStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ChildWorkspaceStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ExtensionPackageRepository.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/HarnessPlacementStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/HostedCleanupDebtStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedActionStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedAgentDefinitionStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedArtifactReader.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedExtensionRecordStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedSessionSkills.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedSessionStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedTaskEventStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedToolDetailsStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedToolResultStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedUserMessageStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedWorkspaceRegistry.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ResidentRetirementRepository.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SessionMessageRelayStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SessionRecoveryRepository.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SessionWorktreeStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SshWorkspaceStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/TransportConsumerStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WakeRecoveryStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WorkspaceCsiReservations.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WorkspaceExecutionStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WorkspaceLifecycleStore.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/settings/ManagedAgentSettings.java create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/settings/SshSettings.java rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/BrokerValues.java (99%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiFilesRetirementProfile.java (88%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProof.java (99%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolContent.java rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiOwnershipRevision.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/ExecutionReconciliation.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnership.java (82%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessSessionResolver.java (79%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/JsonCodec.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistory.java (97%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/ManagedExtensionExecutionProfile.java (100%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/NativeCsiExecutionRepository.java rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/ResidentWorkspaceProfile.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAdmission.java (83%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAttestation.java (96%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRecord.java (96%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRepository.java (64%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerException.java (100%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerOperations.java rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeDrainReceipt.java (98%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionCommandVerifier.java (68%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeLease.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeLifecycleAuthority.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeObservation.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionRequest.java (94%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeed.java (98%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisioner.java (81%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimePublicationGrant.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimePublicationVerifier.java (53%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryEvidence.java (95%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRequestKeys.java rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeResourceHandle.java (100%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRetention.java rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeScope.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSession.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRecord.java (96%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRepository.java (67%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeTransport.java (83%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/StoreFailureException.java rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecord.java (94%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRepository.java (81%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolHoldRow.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspaceCsiRuntimeIdentity.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspaceExecutionProfile.java (100%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspacePhysicalTransport.java rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBinding.java (98%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ResolvedWorkspace.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccess.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessPolicy.java (78%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceActor.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalog.java (96%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceException.java (81%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspacePage.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecord.java (96%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRegistry.java (78%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRelativePath.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceSelection.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceState.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceValues.java (81%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceView.java (100%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning => contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol}/ExternalRuntimeKinds.java (61%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/KubernetesNames.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning => contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol}/ManagedContextProtocol.java (98%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning => contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol}/ProviderRuntimeProtocol.java (98%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/RuntimeProviderKinds.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema => contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/schema}/ManagedSchemaContract.java (98%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema => contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/schema}/ManagedSchemaKeys.java (97%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/security/ProtectedSecret.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/security/SecretProtector.java (52%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning => contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi}/ExternalRuntimeClient.java (69%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning => contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi}/ExternalStorageRegistry.java (90%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/KubernetesMaintenanceClient.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning => contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi}/KubernetesRuntimeClient.java (64%) create mode 100644 packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/SshConnections.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning => contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi}/SshRuntimeNode.java (95%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning => contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi}/SshRuntimeStorage.java (89%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocol.java (97%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileHistoryProtocol.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocol.java (99%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocol.java (99%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocol.java (98%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionCommandProtocol.java (93%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocol.java (97%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHoldControlProtocol.java (91%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHoldFamily.java (100%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHookProtocol.java (93%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMcpProtocol.java (93%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocol.java (90%) rename packages/managed-agent-server/{ => contracts}/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocol.java (90%) rename packages/managed-agent-server/{ => contracts}/src/main/resources/csi-native-file-declarations.json (100%) rename packages/managed-agent-server/{ => contracts}/src/main/resources/openapi/managed-agent-public-api.openapi.json (100%) create mode 100644 packages/managed-agent-server/control-plane/pom.xml rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactService.java (93%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedToolDetailsService.java (85%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ToolPublicationRetentionObserver.java (80%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/automation/AutomationScanner.java (97%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/automation/CronSlots.java (100%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationService.java (89%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionService.java (88%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogService.java (96%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogService.java (98%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementService.java (84%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedSkillCatalogService.java (89%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelService.java (88%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisher.java (95%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumer.java (89%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializer.java (97%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/event/TransportExecutors.java (100%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivator.java (95%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionManagementService.java (83%) create mode 100644 packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageService.java rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/security/PublicSurface.java (100%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinator.java (92%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinator.java (92%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionService.java (93%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinator.java (97%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessEventProjector.java (97%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinator.java (89%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedActionService.java (94%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentService.java (83%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamService.java (98%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachments.java (95%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskCancelService.java (94%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskService.java (85%) create mode 100644 packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedUserMessagePolicy.java rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/OperationReconciler.java (97%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptions.java (91%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/RenameEvidence.java (85%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancer.java (91%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionEventHub.java (100%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinator.java (93%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleService.java (96%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelay.java (97%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryService.java (84%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRenameCoordinator.java (92%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinator.java (95%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/TurnInputEvidence.java (90%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/UserMessageDispatcher.java (82%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/session/WakeRecovery.java (86%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildLifecycleAdmissions.java (94%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelay.java (98%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceService.java (91%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeService.java (81%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceService.java (79%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceDiscoveryService.java (92%) create mode 100644 packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ManagedRuntimeCoordinator.java create mode 100644 packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentWorkspaceRetirementService.java rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinator.java (79%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJob.java (75%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeProvisioner.java (92%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolver.java (77%) rename packages/managed-agent-server/{ => control-plane}/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransport.java (89%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace => control-plane/src/main/java/com/alibaba/qwen/code/runtimebroker/workspace}/ConfiguredWorkspaceRegistry.java (95%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace => control-plane/src/main/java/com/alibaba/qwen/code/runtimebroker/workspace}/GrantedWorkspaceAccessPolicy.java (83%) create mode 100644 packages/managed-agent-server/persistence/pom.xml rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStore.java (91%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelInstanceStore.java (89%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildResultRelayStore.java (91%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStore.java (85%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationStore.java (94%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStore.java (90%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStore.java (87%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupReferences.java (81%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelDeliveryRepository.java (97%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelRouteRepository.java (93%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageService.java => persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcExtensionPackageRepository.java} (53%) create mode 100644 packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcSessionRecoveryRepository.java rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageOwners.java (89%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStore.java (91%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentDefinitionStore.java (85%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStore.java (94%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedArtifactReader.java (87%) create mode 100644 packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedConfigStore.java rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunch.java (93%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionCommandVerifier.java (92%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordStore.java (94%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedLegacySessionGuard.java (87%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMcpSettings.java (92%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeJournalSnapshots.java (91%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedProjectionResourceCollector.java (98%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceipts.java (97%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuard.java (94%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaJdbcTemplate.java (97%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionRetirementStore.java (96%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionSkills.java (94%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStore.java (94%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedTaskEventStore.java (83%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolDetailsStore.java (98%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjector.java (94%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultStore.java (95%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedUserMessageStore.java (94%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedWorkspaceRegistry.java (87%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStore.java (68%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaces.java (72%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionMessageRelayStore.java (88%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollector.java (97%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStore.java (77%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStore.java (81%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAdmissionStore.java (97%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollector.java (97%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationDataStore.java (97%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStore.java (96%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationStore.java (98%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationVerifier.java (96%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/TransportConsumerStore.java (89%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStore.java (96%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationRoots.java (86%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStore.java (88%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistory.java (97%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryImage.java (95%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationImage.java (93%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventory.java (96%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationStore.java (97%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationWorkerMatch.java (97%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStore.java (98%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStore.java (80%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeAccess.java (95%) create mode 100644 packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionCommands.java rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStore.java (98%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceEvidenceResources.java (97%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceExecutionStore.java (90%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStore.java (83%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStore.java (90%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStore.java (82%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLocalDirectories.java (94%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceRecovery.java (100%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationAdmission.java (83%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStore.java (96%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperations.java (96%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperatorRecoveryStore.java (98%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleVerifier.java (95%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReader.java (92%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStore.java (92%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuard.java (77%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageKindGuard.java (77%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/session => persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/evidence}/CancelEvidence.java (94%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/JdbcRuntimeBrokerSchema.java (100%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchema.java (100%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalog.java (99%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaProjection.java (100%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaRows.java (100%) rename packages/managed-agent-server/{ => persistence}/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTime.java (100%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/CsiNativeToolReservation.java (86%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/InMemoryRuntimeBindingRepository.java (96%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/InMemoryRuntimeSessionRepository.java (95%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/InMemoryToolExecutionRepository.java (98%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcCsiActivationAdmission.java (97%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcCsiApplicationProgress.java (93%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcCsiApplicationRetirement.java (97%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcCsiExecutionAdmission.java (98%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcCsiFilesRetirementGuard.java (97%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcCsiReadyAdmission.java (94%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcCsiRetirementSeal.java (93%) create mode 100644 packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcHarnessResources.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcRepositorySupport.java (72%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcRuntimeBindingRepository.java (98%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcRuntimeRetention.java (97%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcRuntimeSessionRepository.java (97%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcToolExecutionRepository.java (99%) rename packages/managed-agent-server/{ => persistence}/src/main/resources/db/managed-schema/V1__managed_schema.sql (100%) create mode 100644 packages/managed-agent-server/runtime-broker/pom.xml rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker => runtime-broker/src/main/java/com/alibaba/qwen/code/runtimebroker/service}/RuntimeBrokerService.java (98%) create mode 100644 packages/managed-agent-server/server/pom.xml rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/maintenance/WorkspaceRecoveryApplication.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/ManagedAgentServerApplication.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/AgentDefinitionController.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandler.java (98%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/AuthenticatedTenantActor.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ExternalStorageRegistrationController.java (98%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedActionController.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedArtifactController.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedAutomationController.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedChannelAdapterController.java (78%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedChannelController.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedExtensionManagementController.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedExtensionPackageController.java (99%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedHookController.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedMcpController.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedSessionStoreController.java (89%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedToolDetailsController.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/PublicAgentController.java (95%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/RequestIdFilter.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/SessionWorktreeController.java (97%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilter.java (99%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/SshWorkspaceController.java (97%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/TenantContextArgumentResolver.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/TenantContextFilter.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationController.java (96%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/TrustedActorHeaderFilter.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/UserMessageController.java (91%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/WebShellAgentController.java (96%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/WebShellSkillsController.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationController.java (91%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceDiscoveryController.java (96%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceFileController.java (96%) create mode 100644 packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/AliyunToolPublicationStorageAutoConfiguration.java rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/BrokerSecurity.java (100%) create mode 100644 packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfiguration.java rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/ExecutionConfiguration.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/HarnessConfiguration.java (83%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/InternalSurfaceConfiguration.java (100%) create mode 100644 packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentConfiguration.java rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalConfiguration.java (95%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentPersistenceConfiguration.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentProperties.java (58%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentWebConfiguration.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedArtifactConfiguration.java (99%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedLocalConfiguration.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSchemaConfiguration.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfiguration.java (94%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/persistence => server/src/main/java/com/alibaba/qwen/code/managedagent/config}/ManagedSessionReaderVersion.java (69%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfiguration.java (90%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfiguration.java (87%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/SshRuntimeSettings.java (58%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationConfiguration.java (97%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/config/WebConfiguration.java (100%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/workspace => server/src/main/java/com/alibaba/qwen/code/managedagent/config}/runtime/EmbeddedRuntimeBroker.java (68%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/persistence => server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance}/ResidentWorkspaceRetirementMain.java (84%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/persistence => server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceCsiHostedMain.java (88%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime => server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceCsiProtectionMain.java (90%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/persistence => server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceCsiRegistrationMain.java (91%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/persistence => server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceCsiRetirementMain.java (91%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/persistence => server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceCsiRuntimeMain.java (82%) create mode 100644 packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiSessionMain.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/persistence => server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceCsiWorkerAckMain.java (76%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/persistence => server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceMaintenanceRecoveryMain.java (86%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime => server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceMigrationMain.java (84%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime => server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceRecoveryCommand.java (92%) create mode 100644 packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceRecoveryMain.java rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/managedagent/persistence => server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceStorageRegistrationMain.java (92%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/preflight/HarnessBrokerUrl.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ManagedAgentPreflight.java (98%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ResolvedConfiguration.java (100%) rename packages/managed-agent-server/{ => server}/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ServiceAccount.java (100%) rename packages/managed-agent-server/{src/main/java/com/alibaba/qwen/code/runtimebroker/transport => server/src/main/java/com/alibaba/qwen/code/runtimebroker/http}/RuntimeBrokerHttpServer.java (99%) rename packages/managed-agent-server/{ => server}/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (100%) rename packages/managed-agent-server/{ => server}/src/main/resources/application-managed-local.yml (100%) rename packages/managed-agent-server/{ => server}/src/main/resources/application.yml (100%) delete mode 100644 packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactPolicy.java delete mode 100644 packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfiguration.java delete mode 100644 packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentConfiguration.java delete mode 100644 packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionMain.java delete mode 100644 packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryMain.java create mode 100644 packages/managed-agent-server/verification/pom.xml rename packages/managed-agent-server/{ => verification}/src/test/embedding-host/pom.xml (100%) rename packages/managed-agent-server/{ => verification}/src/test/embedding-host/src/test/java/example/EmbeddingHostTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/embedding-host/src/test/java/example/EnabledHostTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/browserfixture/WorkspaceBrowserFixtureMain.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/maintenance/WorkspaceRecoveryApplicationTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ActionJournal.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/AliyunToolPublicationObjectStoreTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/AutomationHarnessFake.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/BackfillWitnessesTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/BrokerSignatures.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ChannelContractTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ChildResultRelayStoreTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ContainerArtifactSelectionTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/EmbeddedRuntimeBrokerTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGate.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGateStatisticsTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportMultiNodeGate.java (91%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ExtensionRecordJournal.java (91%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HarnessEventProjectorTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HookAdmissionHistory.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationCrashFixtureMain.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationFaultGatesIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationSpringCrashIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedBackgroundPublicationGate.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedCancellationProbe.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelCrashFixtureMain.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelFaultGatesIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelGateSupport.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelProcessCrashIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedCleanupRetentionMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedCleanupRetentionTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedCommittedEventLineReplayIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedConcurrentTurnBurstMySqlIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedDurableWorkers.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamLivenessIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamRelay.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamRelayTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedExtensionsIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedGateTestSupport.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedGateTestSupportTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedHarnessMySqlIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookCancelFaultGatesIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookFaultGatesIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookMessageDisplayReloadIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookReplyLossFaultGatesIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookStreamGapIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedLifecycleRelay.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedLifecycleRelayTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpReplyLossIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpRestartFaultGatesIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpResultDeliveryIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStoreFaultGatesIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStreamGapIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunFaultGatesIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunProbe.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationActivationTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationExtension.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationExtensionTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedOperatorRecoveryProbe.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedOperatorRecoveryProbeTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashFixtureMain.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashIT.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessLiveness.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessLivenessTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderControlProbe.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbe.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbeTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedPublicWorkspaceIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedRecoveryWedgeGate.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedSessionRelayFaultGatesIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedSessionRelayForwardingProxy.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleFixtureMain.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleRecoveryIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellOutputProbe.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedSseGapProbe.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamLeadMySqlIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamMemberCommandMySqlIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowFaultGatesIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowProbe.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowRelayFaultGatesIT.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceColdLoadProbe.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceToolTurnIT.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180HardenedVerificationTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180InternalPortTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180SignedModeTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/JournalEventPublicationPointTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/LegacyCwdOperations.java (82%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/LegacyEvents.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedActionsTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentApiContractTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentDefinitionAdmissionTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentDefinitionTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentMySqlIT.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentPackagingIT.java (84%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentServerIntegrationTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentStoreLocaleTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalChangeMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalChangeTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalSelectionTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactApiIntegrationTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactReadIntegrationTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactSchedulingTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAutomationRecordContractTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelJdbcContract.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelJdbcContractTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelRecordContractTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildAcceptanceRecordContractTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildRunRecordContractTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedConfigInstallContractTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionMySqlIT.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionOperationTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedCwdChangeOperationTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedCwdOperationContractShapeTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventEnvelopeContractTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventIdentityMigrationTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionCommandVerifierTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionConfigurationAdmissionTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionConfigurationMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionCreationMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionProjectionContractTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordContractTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordStoreTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookAdmissionIndexTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookCatalogContractTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookRecordContractTest.java (87%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMaterializationDeferTest.java (91%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpCatalogContractTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpRecordContractTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpSettingsMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpSettingsStoreTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelContextTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelSelectionTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsMySqlIT.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedOperationSchemaUpgradeTest.java (89%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRenameReconciliationMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRenameReconciliationTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRuntimeRetentionRaceMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRuntimeRetentionRaceTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSchemaJournalAtomicityTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSchemaJournalMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCommandRecordContractTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCreationSkillsTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionLifecycleTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageRecordContractTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageStoreTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationMigrationTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationStoreTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOwnerTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionQueryMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionQueryTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreBindingTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreContractFixtureTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreIntegrationTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreProcessFixtureMain.java (90%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionToolProfileMigrationTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskCancelOperationTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskEventJournalTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamRecordContractTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamStoreTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedToolDetailsMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedToolStageContractTest.java (90%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationMySqlIT.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnQueryTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedUserMessageMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedUserMessageStoreTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWebShellRenameTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceAdmissionTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceRolesMigrationTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/MySqlForkRoutingListener.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/MySqlForkRoutingListenerTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/MySqlLockProbe.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/OpenApiContract.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/PlannedTaskContractTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/PublicationJournalFixture.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerBracketKeyBindingTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerConfigurationIntegrationTest.java (91%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerDefaultOnTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerFlywaySchemaTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/SchedulerHold.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/SchedulerHoldTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/SessionMessageRelayStoreTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/StandaloneDataSourceConfigurationTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/SurfaceAdmissionAcceptanceTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationConfigurationTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationContractTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLifecycleMySqlIT.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLockOrderMySqlIT.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationRecoveryMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationStoreTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/TurnEventLines.java (100%) create mode 100644 packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/VerificationFixtures.java rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceCreationProcessFixtureMain.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseMySqlIT.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionMySqlIT.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionTest.java (96%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent => verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters}/event/LiteTopicEventTransportTest.java (91%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent => verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters}/event/LiteTopicPublicEventTransportTest.java (97%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent/workspace => verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters}/runtime/ResidentWorkspaceProvisionerTest.java (89%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent/workspace => verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters}/runtime/WorkspaceCsiResourceGuardTest.java (96%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent/workspace => verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters}/runtime/WorkspaceCsiRuntimeIdentityTest.java (97%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent/workspace => verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters}/runtime/WorkspaceCsiRuntimeProvisionerTest.java (90%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent => verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters}/workspace/ChildWorktreeGitTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandlerTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSnapshotRestoreResponseTest.java (89%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSurfaceCapture.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSurfaceCaptureTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilterTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistry.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistryGateNegativeTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistryGateTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/api/TenantContextFilterTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationControllerTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/api/TrustedActorHeaderFilterTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationControllerTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/api/WorkspaceFileControllerTest.java (100%) create mode 100644 packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/architecture/ModuleArchitectureTest.java rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/artifact/AliyunToolPublicationObjectStoreTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactServiceTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerConcurrencyMySqlIT.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/automation/CronSlotsTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationServiceTest.java (83%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionServiceTest.java (91%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogServiceTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogServiceTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementServiceTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelServiceTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/BrokerSecurityTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfigurationTest.java (90%) create mode 100644 packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/HarnessConfigurationTest.java rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/HarnessCredentialsFileTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/InternalSurfaceConfigurationTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentEmbeddingTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalEmbeddingTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentPropertiesTest.java (89%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedArtifactConfigurationTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedLocalConfigurationTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSchemaConfigurationTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfigurationTest.java (92%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent/persistence => verification/src/test/java/com/alibaba/qwen/code/managedagent/config}/ManagedSessionReaderVersionTest.java (80%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfigurationTest.java (89%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfigurationTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/SessionStoreDefaultsTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationStorageConfigurationTest.java (88%) create mode 100644 packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/runtime/EmbeddedBrokerFixture.java rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent/workspace => verification/src/test/java/com/alibaba/qwen/code/managedagent/config}/runtime/ExternalWorkspaceFactoryTest.java (58%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent => verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts}/catalog/AgentDefinitionExtensionsTest.java (98%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent => verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts}/event/ManagedEventEnvelopesTest.java (99%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent => verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/AgentDefinitionCompilerTest.java (98%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent => verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/EventIdentityTest.java (96%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent => verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ExternalStorageRegistrationMySqlIT.java (61%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent => verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedHistoryMaintenanceRecordsTest.java (98%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent => verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts}/persistence/ManagedHostedRecoveryRecordsTest.java (99%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent => verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts}/security/WriterCredentialPolicyTest.java (97%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent => verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts}/workspace/WorkspaceSelectionTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisherTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerSqlTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerTest.java (87%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportNodeMain.java (82%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializerTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivatorTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageServiceTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnectorTest.java (98%) create mode 100644 packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/HarnessSdkValuesTest.java rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessCreateOrLoadPinningTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessPoolMySqlIT.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/harness/KubernetesHarnessDiscoveryTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessColdCancelRegressionTest.java (91%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnectorTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessNewSessionRegressionTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedMcpManagementAttachmentTest.java (99%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent/persistence => verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance}/ResidentWorkspaceRetirementMainMySqlIT.java (95%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent/persistence => verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance}/ResidentWorkspaceRetirementMainTest.java (97%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent/persistence => verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceCsiHostedMainTest.java (99%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent/persistence => verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceCsiRuntimeMainTest.java (93%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent/persistence => verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceCsiSessionMainTest.java (90%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent/persistence => verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceCsiWorkerAckMainTest.java (96%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime => verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance}/WorkspaceRecoveryCommandTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/model/RequestDigestsTest.java (100%) create mode 100644 packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentStateStoreProxyTransactionTest.java rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentStateStoreTransactionTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AliyunToolPublicationReadRetryTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AutomationCommandTimestampTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStoreTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStoreTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedActionStoreTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedChannelStoreTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedStoreFixture.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedTaskEventStoreTest.java (83%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/CsiRuntimeMarkerProbeTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExecutedQueryPlan.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExecutedQueryPlanTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationTest.java (91%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/FaultingExtensionRecordStore.java (79%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStoreTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStoreTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceConcurrencyIT.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceStorageGuardMySqlIT.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/IsolatedManagedSchemaMySqlTestDatabase.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageAliasMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageAliasTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStoreTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentCsiMutationMySqlIT.java (90%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentEventSequenceConcurrencyMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentEventSequenceConcurrencyTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStoreChildCreationFenceTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentTurnDeferralTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunchTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordLifecycleGateTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileMySqlIT.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessagePagingMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessagePagingTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessageSnapshotRemovalTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedModelContextRetentionTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeSnapshotRetirementTest.java (91%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceiptScopeTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceiptsTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuardMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuardTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaBusinessMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaBusinessStoreTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaJdbcTimeTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationLoadMySqlIT.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationStoreTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaRelayLoadMySqlIT.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionMySqlIT.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionStoreTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardConvoyIT.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardMappingTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionHistoryTest.java (89%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionNativeReadTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceMySqlIT.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStoreCredentialTest.java (88%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotNumericPagingTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotPoolTest.java (91%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultCaptureTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjectorTest.java (89%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedWorkspaceRolesMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4FaultProcess.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4FileObjects.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlDatabase.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlDatabaseTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlGate.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4OssGate.java (92%) create mode 100644 packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/PersistenceTestAccess.java create mode 100644 packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/PrivateCsiFixture.java rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/RecoverySettlementStoreMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/RecoverySettlementStoreTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentRetirementFixture.java (84%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceGraceDeadlineTest.java (85%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReclamationProcessMySqlIT.java (84%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementProcessMySqlIT.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStoreTest.java (84%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReuseConcurrencyMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReuseConcurrencyTest.java (89%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspacesTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollectorTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionRowLockOrderTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStoreTest.java (84%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SharedLocalProjectsMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SharedLocalProjectsTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStoreTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGate.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGateTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreModelsTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAcknowledgementMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAcknowledgementTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAsyncVerificationMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAsyncVerificationTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollectorMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollectorTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionClockFixtureMain.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStoreTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStoreTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStoreTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryTest.java (87%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventoryMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventoryTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStoreTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiHostedFilesTest.java (85%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiNativeActivationGateTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiNativeActivationRenewalMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStoreTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetainedArtifactsMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetainedArtifactsTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetirementStoreTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeConstructionFixture.java (69%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionGuardTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSqlWorkerMySqlIT.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStoreTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceDirectorySelectionMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStoreTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStoreTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleMySqlIT.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStoreTest.java (91%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceLinuxAcceptanceMain.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationMySqlIT.java (90%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStoreTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceNativeAcceptanceMain.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleMySqlIT.java (88%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceQueryScaleMySqlIT.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReaderAssertions.java (88%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStoreTest.java (90%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceSchemaConsolidationTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuardTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceTestStorage.java (89%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ExternalRuntimeConfigurationTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/preflight/HarnessBrokerUrlTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ManagedAgentPreflightTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ResolvedConfigurationTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ServiceAccountTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinatorTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/AdmittedTurnRetryTerminalStateTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ChildWorktreeGitVersionTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinatorTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/CoordinatorTestSupport.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/EventStreams.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinatorTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinatorTest.java (88%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/Issue13181QueryBudgetTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentReplayOutageTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceChildWorkspaceTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorkflowChildTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorktreeTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentSessionCapabilitiesTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentWorkspaceReplayTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedEventReplayTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamServiceTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachmentTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/MessageProjectionGapHealTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationReconcilerTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationRetryTerminalStateTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptionsTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/RecoverySettlementTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancerBacklogTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancerTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorRetractionTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorSchedulerTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/ResidentAcceptanceSnapshot.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionEventHubPinningTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionEventHubTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinatorTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleProtocolChoiceTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleServiceResumeTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelayTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryInitialStateTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryServiceTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinatorTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/WakeRecoveryTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/session/WorkspaceRecoveryTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelayTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceServiceTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeGitTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeServiceTest.java (88%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceServiceTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/HostedWorkspaceRecoveryWorkerIT.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentHostedRuntimeFixture.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinatorTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJobTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRecoveryContract.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeInstallProbeTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolutionPivotTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransportTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerProcess.java (88%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerRenewalPinningTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerValuesTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerVirtualThreadPinningTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/CarrierCount.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/CarrierCountTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ConcurrencyStorageFaultGateTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ContextInstallationFaultGateTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProofTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFileHistoryTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFinalOutputProofTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeTextProofTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolResultProofTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiOwnershipRevisionTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiRetirementSealTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/DelegatingBindingRepository.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/DelegatingToolExecutionRepository.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/DriverManagerDataSource.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableLocalRuntimeFaultGateTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableRuntimeRecoveryTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ExecutionTakeoverContract.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ExternalRuntimeWorkerFaultGateTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/FakeAttestationWorkerTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/FakeKubernetesRuntimeClient.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateControlTest.java (91%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateTransport.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultProxy.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnershipTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/InMemoryRepositoryTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183AdversarialTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183RegressionTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcPhysicalStorageReleaseTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositoryTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/KubernetesRuntimeWorkerTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/LocalRebootFaultGateTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/LostResponseFaultGateTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/MaintenanceProbeWaiterTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistoryMySqlIT.java (87%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedContextRecoveryTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiAcknowledgementProtocolTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiFilesHttpTransportTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiHttpTransportTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedExtensionExecutionProfileTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedSchemaMySqlIT.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedToolResultConformanceTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessCrashFaultGateTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessTrees.java (90%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ProviderRuntimeTransportTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/RecoverableProcessProvisioner.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerStorageAdmissionTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeDispatchAdmissionTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionMetadataControlTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeMaintenanceRecoveryTest.java (88%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeedTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryContract.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionTombstoneTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSshAcknowledgementTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/TakeoverReconciliationTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/TcpRelay.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecordFixtures.java (87%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/UnknownExecutionObservationTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceCreationSchemaMySqlIT.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceExecutionProfileTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceMigrationRepositoryTest.java (93%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker/transport => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter}/HttpRuntimeTransportTest.java (99%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker/transport => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter}/HttpRuntimeTransportTestAccess.java (85%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker/transport => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter}/ManagedCsiAcknowledgementHttpTransportTest.java (99%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker/transport => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter}/ManagedRuntimeAttestationConformanceTest.java (99%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter}/security/AesGcmSecretProtectorTest.java (98%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker/transport => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/http}/ManagedExtensionExecutionContractTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalogTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaMapping.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaProjectionTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaRowsTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTimeContractTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTimeTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ConstructorVisibilityTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBindingTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedContextEnvelopeConformanceTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedWorkspaceBindingConformanceTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TestWorkspaces.java (68%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TextFieldProbeTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceActorTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalogTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecordTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRelativePathTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceSelectionTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceViewTest.java (100%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/BindingExecutionInventoryTest.java (95%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/CsiApplicationProgressTest.java (97%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/CsiFilesRetirementProfileTest.java (94%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/CsiNativeReadContinuationProofTest.java (97%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/CsiNativeToolReservationTest.java (98%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/DispatchAuthorizationEvidenceTest.java (94%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/FaultGateRig.java (82%) create mode 100644 packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/InMemoryTestAccess.java rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcCsiBindingControlTest.java (95%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcPlacementGuardTest.java (96%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcRepositoryContract.java (98%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcRepositorySupportTest.java (95%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcRuntimeBrokerMySqlIT.java (94%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcRuntimeRetentionMySqlIT.java (93%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/JdbcRuntimeRetentionTest.java (97%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/ManagedCheckpointHistorySqlTest.java (90%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/ResidentWorkspaceAdmissionTest.java (91%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/ResidentWorkspacePlacementTest.java (91%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/RuntimeBrokerHttpServerTest.java (98%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/RuntimeBrokerTestAccess.java (64%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence}/SharedHarnessOwnershipTest.java (93%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/protocol}/ManagedContextProtocolTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/DurableLocalProcessRuntimeProvisionerTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalHttpRuntimeClientTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisionerTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceBootContractTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisionerTest.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/FaultGateBroker.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReaderTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClientTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisionerTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisionerTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessStopExecutorTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRebootTestSupport.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStoreTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/MacOsHostIdentityTest.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RoutingRuntimeProvisionerTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RuntimeHarnessDrainMySqlIT.java (100%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RuntimeHarnessDrainTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManagerTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeInteropTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisionerTest.java (95%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkerEnvironmentRoutingTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkspaceFileProcessTest.java (100%) create mode 100644 packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/service/BrokerServiceTestAccess.java rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/service}/RuntimeBrokerServiceBindingReadTest.java (91%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/service}/RuntimeBrokerServiceTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocolTest.java (96%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileHistoryProtocolTest.java (89%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocolTest.java (93%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocolTest.java (97%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocolTest.java (90%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocolTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionTransportTest.java (98%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocolTest.java (99%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocolTest.java (99%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/workspace}/ConfiguredWorkspaceRegistryTest.java (98%) rename packages/managed-agent-server/{src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace => verification/src/test/java/com/alibaba/qwen/code/runtimebroker/workspace}/GrantedWorkspaceAccessPolicyTest.java (94%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/managedprobes/ProbeAdvice.java (92%) rename packages/managed-agent-server/{ => verification}/src/test/java/com/alibaba/qwen/managedprobes/PublicationProbeWiring.java (75%) rename packages/managed-agent-server/{ => verification}/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/capability-parity/README.md (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/capability-parity/managed-surface.txt (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/csi-hosted-file-turns.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/csi-native-activation-fixture.json (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/csi-native-activation-generator.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/csi-native-final-output-fixture.json (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/csi-native-final-output-generator.mjs (98%) rename packages/managed-agent-server/{ => verification}/src/test/resources/csi-native-function-parts-baseline.json (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/csi-native-json-fixture.json (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/csi-native-json-generator.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/csi-native-read-continuation-fixture.json (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/csi-native-resource-schema-fixture.sql (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/csi-native-text-fixture.json (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/csi-native-text-turns.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/csi-sql-worker-acceptance.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/db/managed-schema/legacy-table-map.tsv (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/fake-attestation-worker.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/hosted-definition-census.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/hosted-extension-process.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/hosted-hook-fault-process.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/hosted-mcp-fault-process.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/managed-media-retention-generator.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/openapi/contract-known-gaps.txt (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/original-managed-table-inventory.tsv (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/recovery-initial-journal.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/resident-acceptance/hosted-and-model.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/resident-acceptance/resident-model.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/resident-acceptance/resident-tool-evidence.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/resident-acceptance/resident-tool-evidence.test.mjs (100%) rename packages/managed-agent-server/{ => verification}/src/test/resources/tool-v3-interop-server.mjs (100%) diff --git a/.github/scripts/ci-runner-routing.test.mjs b/.github/scripts/ci-runner-routing.test.mjs index 907b8d254cf..7f8518b95ac 100644 --- a/.github/scripts/ci-runner-routing.test.mjs +++ b/.github/scripts/ci-runner-routing.test.mjs @@ -871,7 +871,7 @@ describe('trusted-PR lanes moved off the hosted queue (#13245)', () => { assert.match(run('Build Java components'), /-Dmaven\.repo\.local=/); assert.match( run('Build Java components'), - /rm -rf -- "\$\{repo\}\/com\/alibaba\/qwencode-sdk" "\$\{repo\}\/com\/alibaba\/qwen-managed-agent-server"/, + /rm -rf -- "\$\{repo\}\/com\/alibaba\/qwencode-sdk" "\$\{repo\}\/com\/alibaba\/"qwen-managed-agent-\*/, ); assert.match( run('Assemble reference distribution'), diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96b6daeb048..af12ae5ec39 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1196,7 +1196,7 @@ jobs: PR_NUMBER: '${{ github.event.pull_request.number }}' run: |- set -euo pipefail - DOCUMENT='packages/managed-agent-server/src/main/resources/openapi/managed-agent-public-api.openapi.json' + DOCUMENT='packages/managed-agent-server/contracts/src/main/resources/openapi/managed-agent-public-api.openapi.json' gh api "repos/${REPO}/pulls/${PR_NUMBER}/files?per_page=100" --paginate --jq '.[] | .filename, .previous_filename // empty' > "${RUNNER_TEMP}/contract-pr-files.txt" if ! grep -qxF "${DOCUMENT}" "${RUNNER_TEMP}/contract-pr-files.txt"; then exit 0 @@ -1204,7 +1204,7 @@ jobs: BASE_DOCUMENT="${RUNNER_TEMP}/contract-base.json" git fetch --quiet --depth=1 --filter=blob:none origin "refs/heads/${BASE_REF}" if ! git show "FETCH_HEAD:${DOCUMENT}" > "${BASE_DOCUMENT}" 2>/dev/null; then - PREVIOUS_DOCUMENT="$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/files?per_page=100" --paginate --jq '.[] | select(.filename == "packages/managed-agent-server/src/main/resources/openapi/managed-agent-public-api.openapi.json") | .previous_filename // empty')" + PREVIOUS_DOCUMENT="$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/files?per_page=100" --paginate --jq '.[] | select(.filename == "packages/managed-agent-server/contracts/src/main/resources/openapi/managed-agent-public-api.openapi.json") | .previous_filename // empty')" if [ -n "${PREVIOUS_DOCUMENT}" ]; then git show "FETCH_HEAD:${PREVIOUS_DOCUMENT}" > "${BASE_DOCUMENT}" else diff --git a/.github/workflows/qwen-triage.yml b/.github/workflows/qwen-triage.yml index b969a416db7..efc4b7fa530 100644 --- a/.github/workflows/qwen-triage.yml +++ b/.github/workflows/qwen-triage.yml @@ -3226,7 +3226,7 @@ jobs: uses: 'actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830' # v4.3.0 with: path: '${{ runner.temp }}/verify-maven-repo' - key: "verify-maven-repo-${{ hashFiles('packages/sdk-java/*/pom.xml', 'packages/managed-agent-server/pom.xml') }}" + key: "verify-maven-repo-${{ hashFiles('packages/sdk-java/*/pom.xml', 'packages/managed-agent-server/**/pom.xml') }}" restore-keys: 'verify-maven-repo-' - name: 'Install and build PR app' diff --git a/.github/workflows/release-managed-agent-reference.yml b/.github/workflows/release-managed-agent-reference.yml index 4e5a394e67c..057e29b4f57 100644 --- a/.github/workflows/release-managed-agent-reference.yml +++ b/.github/workflows/release-managed-agent-reference.yml @@ -217,7 +217,7 @@ jobs: cache: "${{ runner.environment == 'github-hosted' && 'maven' || '' }}" cache-dependency-path: |- packages/sdk-java/qwencode/pom.xml - packages/managed-agent-server/pom.xml + packages/managed-agent-server/**/pom.xml settings-path: '${{ runner.temp }}/setup-java-m2' - name: 'Set up Maven (self-hosted)' @@ -326,7 +326,7 @@ jobs: # PR's build must never reach the host-shared ~/.m2. repo="${RUNNER_TEMP}/m2-repo" cp -aln "${HOME}/.m2/repository" "${repo}" 2>/dev/null || true - rm -rf -- "${repo}/com/alibaba/qwencode-sdk" "${repo}/com/alibaba/qwen-managed-agent-server" + rm -rf -- "${repo}/com/alibaba/qwencode-sdk" "${repo}/com/alibaba/"qwen-managed-agent-* MAVEN_ARGS="${MAVEN_ARGS} -Dmaven.repo.local=${repo}" fi mvn --batch-mode --no-transfer-progress -f packages/sdk-java/qwencode/pom.xml -DskipTests -Dgpg.skip=true clean install diff --git a/.github/workflows/release-managed-runtime.yml b/.github/workflows/release-managed-runtime.yml index e05a26f3c5b..6e8f3914241 100644 --- a/.github/workflows/release-managed-runtime.yml +++ b/.github/workflows/release-managed-runtime.yml @@ -119,7 +119,7 @@ jobs: cache: 'maven' cache-dependency-path: | packages/sdk-java/qwencode/pom.xml - packages/managed-agent-server/pom.xml + packages/managed-agent-server/**/pom.xml - name: 'Set up Node.js 22' uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0 @@ -172,8 +172,9 @@ jobs: - name: 'Verify the executable JAR and publication profile' run: |- mvn --batch-mode --no-transfer-progress -f packages/managed-agent-server/pom.xml clean verify -Pcentral-release -Dgpg.skip=true -DforkCount=1 -DreuseForks=false -Dproject.build.outputTimestamp="${BUILD_TIMESTAMP}" - runtime_jar="packages/managed-agent-server/target/qwen-managed-agent-server-${CLI_VERSION}-exec.jar" + runtime_jar="packages/managed-agent-server/server/target/qwen-managed-agent-server-${CLI_VERSION}-exec.jar" # Older release tags shipped the executable as the main artifact. + if [[ ! -f "${runtime_jar}" ]]; then runtime_jar="packages/managed-agent-server/target/qwen-managed-agent-server-${CLI_VERSION}-exec.jar"; fi if [[ ! -f "${runtime_jar}" ]]; then runtime_jar="packages/managed-agent-server/target/qwen-managed-agent-server-${CLI_VERSION}.jar"; fi unzip -p "${runtime_jar}" META-INF/MANIFEST.MF | tr -d '\r' | grep -Fx 'Main-Class: org.springframework.boot.loader.launch.JarLauncher' node .qwen/managed-release-tools/scripts/managed-runtime-release.js --jar "${runtime_jar}" --version "${CLI_VERSION}" --out-dir dist/managed-runtime diff --git a/.github/workflows/sdk-java-contract-open-prs.yml b/.github/workflows/sdk-java-contract-open-prs.yml index b3bf5746f68..45e30e6c5d6 100644 --- a/.github/workflows/sdk-java-contract-open-prs.yml +++ b/.github/workflows/sdk-java-contract-open-prs.yml @@ -21,7 +21,7 @@ on: branches: - 'main' paths: - - 'packages/managed-agent-server/src/main/resources/openapi/managed-agent-public-api.openapi.json' + - 'packages/managed-agent-server/contracts/src/main/resources/openapi/managed-agent-public-api.openapi.json' - 'scripts/check-contract-open-prs.js' - 'scripts/contract-version-utils.js' - '.github/workflows/sdk-java-contract-open-prs.yml' diff --git a/.github/workflows/sdk-java-flyway-open-prs.yml b/.github/workflows/sdk-java-flyway-open-prs.yml index dcaf8c80842..8719bde3d9d 100644 --- a/.github/workflows/sdk-java-flyway-open-prs.yml +++ b/.github/workflows/sdk-java-flyway-open-prs.yml @@ -17,9 +17,15 @@ on: branches: - 'main' paths: + - 'packages/sdk-java/*/persistence/src/main/resources/db/migration/**' + - 'packages/sdk-java/*/persistence/src/main/java/db/migration/**' + - 'packages/sdk-java/*/persistence/src/main/resources/db/managed-schema/**' - 'packages/sdk-java/*/src/main/resources/db/migration/**' - 'packages/sdk-java/*/src/main/java/db/migration/**' - 'packages/sdk-java/*/src/main/resources/db/managed-schema/**' + - 'packages/managed-agent-server/persistence/src/main/resources/db/migration/**' + - 'packages/managed-agent-server/persistence/src/main/java/db/migration/**' + - 'packages/managed-agent-server/persistence/src/main/resources/db/managed-schema/**' - 'packages/managed-agent-server/src/main/resources/db/migration/**' - 'packages/managed-agent-server/src/main/java/db/migration/**' - 'packages/managed-agent-server/src/main/resources/db/managed-schema/**' diff --git a/.github/workflows/sdk-java.yml b/.github/workflows/sdk-java.yml index bf2c6fcd60c..ecef9f70dc2 100644 --- a/.github/workflows/sdk-java.yml +++ b/.github/workflows/sdk-java.yml @@ -199,7 +199,7 @@ jobs: cache: "${{ runner.environment == 'github-hosted' && 'maven' || '' }}" cache-dependency-path: |- packages/sdk-java/qwencode/pom.xml - packages/managed-agent-server/pom.xml + packages/managed-agent-server/**/pom.xml settings-path: '${{ runner.temp }}/setup-java-m2' # The Broker process tests spawn the Node fixture worker. @@ -294,7 +294,14 @@ jobs: # Maven runs would rewrite through into the host cache. Drop them # so the install writes fresh inodes. rm -rf -- "${RUNNER_TEMP}/m2-repo/com/alibaba/qwencode-sdk" \ - "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-server" + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-parent" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-contracts" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-control-plane" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-runtime-broker" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-persistence" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-adapters" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-server" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-verification" # The suite resolves from the job-local copy above, so it does not # touch host-shared state and must not hold the per-host lock. # On 2026-10-22 run 39771949573's fast tier failed at its @@ -425,7 +432,7 @@ jobs: mkdir -p "${HOME}/.m2/repository" 9>&- cd "${RUNNER_TEMP}/m2-repo" write_back=0 - timeout 300 find . \( -path ./com/alibaba/qwencode-sdk -o -path ./com/alibaba/qwen-managed-agent-server -o -name '*.lastUpdated' -o -name 'resolver-status.properties' -o -name '_remote.repositories' -o -name '*.tmp' \) -prune -o -type f -links 1 -exec cp -ln --parents -t "${HOME}/.m2/repository" {} + 9>&- || write_back=$? + timeout 300 find . \( -path ./com/alibaba/qwencode-sdk -o -path ./com/alibaba/qwen-managed-agent-parent -o -path ./com/alibaba/qwen-managed-agent-contracts -o -path ./com/alibaba/qwen-managed-agent-control-plane -o -path ./com/alibaba/qwen-managed-agent-runtime-broker -o -path ./com/alibaba/qwen-managed-agent-persistence -o -path ./com/alibaba/qwen-managed-agent-adapters -o -path ./com/alibaba/qwen-managed-agent-server -o -path ./com/alibaba/qwen-managed-agent-verification -o -name '*.lastUpdated' -o -name 'resolver-status.properties' -o -name '_remote.repositories' -o -name '*.tmp' \) -prune -o -type f -links 1 -exec cp -ln --parents -t "${HOME}/.m2/repository" {} + 9>&- || write_back=$? if [ "${write_back}" -eq 124 ]; then echo "::warning::Maven cache write-back stopped after 300 s; the gate result above is unaffected" elif [ "${write_back}" -ne 0 ]; then @@ -466,8 +473,8 @@ jobs: path: | packages/sdk-java/qwencode/target/surefire-reports/*.txt packages/sdk-java/qwencode/target/surefire-reports/*.dumpstream - packages/managed-agent-server/target/surefire-reports/*.txt - packages/managed-agent-server/target/surefire-reports/*.dumpstream + packages/managed-agent-server/verification/target/surefire-reports/*.txt + packages/managed-agent-server/verification/target/surefire-reports/*.dumpstream if-no-files-found: 'ignore' retention-days: 7 @@ -776,7 +783,7 @@ jobs: cache: "${{ runner.environment == 'github-hosted' && 'maven' || '' }}" cache-dependency-path: |- packages/sdk-java/qwencode/pom.xml - packages/managed-agent-server/pom.xml + packages/managed-agent-server/**/pom.xml settings-path: '${{ runner.temp }}/setup-java-m2' - name: 'Point Maven at the pool mirror' @@ -927,7 +934,14 @@ jobs: # Maven runs would rewrite through into the host cache. Drop them # so the install writes fresh inodes. rm -rf -- "${RUNNER_TEMP}/m2-repo/com/alibaba/qwencode-sdk" \ - "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-server" + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-parent" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-contracts" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-control-plane" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-runtime-broker" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-persistence" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-adapters" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-server" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-verification" # Both Maven runs resolve from the job-local copy above, so they do # not touch host-shared state and must not hold the per-host lock: # a MySQL verify can run for hours under pool contention, and the @@ -1039,7 +1053,7 @@ jobs: mkdir -p "${HOME}/.m2/repository" 9>&- cd "${RUNNER_TEMP}/m2-repo" write_back=0 - timeout 300 find . \( -path ./com/alibaba/qwencode-sdk -o -path ./com/alibaba/qwen-managed-agent-server -o -name '*.lastUpdated' -o -name 'resolver-status.properties' -o -name '_remote.repositories' -o -name '*.tmp' \) -prune -o -type f -links 1 -exec cp -ln --parents -t "${HOME}/.m2/repository" {} + 9>&- || write_back=$? + timeout 300 find . \( -path ./com/alibaba/qwencode-sdk -o -path ./com/alibaba/qwen-managed-agent-parent -o -path ./com/alibaba/qwen-managed-agent-contracts -o -path ./com/alibaba/qwen-managed-agent-control-plane -o -path ./com/alibaba/qwen-managed-agent-runtime-broker -o -path ./com/alibaba/qwen-managed-agent-persistence -o -path ./com/alibaba/qwen-managed-agent-adapters -o -path ./com/alibaba/qwen-managed-agent-server -o -path ./com/alibaba/qwen-managed-agent-verification -o -name '*.lastUpdated' -o -name 'resolver-status.properties' -o -name '_remote.repositories' -o -name '*.tmp' \) -prune -o -type f -links 1 -exec cp -ln --parents -t "${HOME}/.m2/repository" {} + 9>&- || write_back=$? if [ "${write_back}" -eq 124 ]; then echo "::warning::Maven cache write-back stopped after 300 s; the gate result above is unaffected" elif [ "${write_back}" -ne 0 ]; then @@ -1057,10 +1071,10 @@ jobs: with: name: 'sdk-java-test-reports-mysql-integration-java-21' path: | - packages/managed-agent-server/target/surefire-reports/*.txt - packages/managed-agent-server/target/surefire-reports/*.dumpstream - packages/managed-agent-server/target/failsafe-reports/*.txt - packages/managed-agent-server/target/failsafe-reports/*.dumpstream + packages/managed-agent-server/verification/target/surefire-reports/*.txt + packages/managed-agent-server/verification/target/surefire-reports/*.dumpstream + packages/managed-agent-server/verification/target/failsafe-reports/*.txt + packages/managed-agent-server/verification/target/failsafe-reports/*.dumpstream if-no-files-found: 'ignore' retention-days: 7 @@ -1188,7 +1202,7 @@ jobs: cache: "${{ runner.environment == 'github-hosted' && 'maven' || '' }}" cache-dependency-path: |- packages/sdk-java/**/pom.xml - packages/managed-agent-server/pom.xml + packages/managed-agent-server/**/pom.xml settings-path: '${{ runner.temp }}/setup-java-m2' - name: 'Set up Node.js' uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0 @@ -1281,7 +1295,7 @@ jobs: run: 'node scripts/check-failsafe-reports.js hosted packages/managed-agent-server' - name: 'Check Hosted latency measurements' run: |- - test -s packages/managed-agent-server/target/hosted-latency-baseline.json + test -s packages/managed-agent-server/verification/target/hosted-latency-baseline.json cd integration-tests npx vitest run cli/hosted-latency-baseline.test.ts - name: 'Run Runtime Broker fault gates' @@ -1302,16 +1316,16 @@ jobs: with: name: 'hosted-harness-mysql-reports' path: | - packages/managed-agent-server/target/failsafe-reports/ - packages/managed-agent-server/target/hosted-latency-baseline.json - packages/managed-agent-server/target/fg7-cases/ - packages/managed-agent-server/target/fg8-cases/ - packages/managed-agent-server/target/fg9-cases/ - packages/managed-agent-server/target/fg10-cases/ - packages/managed-agent-server/target/fg11-cases/ - packages/managed-agent-server/target/fg12-cases/ - packages/managed-agent-server/target/fg13-cases/ - packages/managed-agent-server/target/l4-shell-lifecycle/ + packages/managed-agent-server/verification/target/failsafe-reports/ + packages/managed-agent-server/verification/target/hosted-latency-baseline.json + packages/managed-agent-server/verification/target/fg7-cases/ + packages/managed-agent-server/verification/target/fg8-cases/ + packages/managed-agent-server/verification/target/fg9-cases/ + packages/managed-agent-server/verification/target/fg10-cases/ + packages/managed-agent-server/verification/target/fg11-cases/ + packages/managed-agent-server/verification/target/fg12-cases/ + packages/managed-agent-server/verification/target/fg13-cases/ + packages/managed-agent-server/verification/target/l4-shell-lifecycle/ retention-days: 7 - name: 'Verify Hosted admission gates' @@ -1362,7 +1376,7 @@ jobs: uses: 'actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' # v7.0.1 with: name: 'o4-mysql-gate-reports' - path: 'packages/managed-agent-server/target/failsafe-reports/' + path: 'packages/managed-agent-server/verification/target/failsafe-reports/' retention-days: 7 hosted-failover-e2e: @@ -1391,7 +1405,7 @@ jobs: cache: "${{ runner.environment == 'github-hosted' && 'maven' || '' }}" cache-dependency-path: |- packages/sdk-java/**/pom.xml - packages/managed-agent-server/pom.xml + packages/managed-agent-server/**/pom.xml settings-path: '${{ runner.temp }}/setup-java-m2' - name: 'Set up Node.js' uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0 @@ -1710,7 +1724,7 @@ jobs: cache: "${{ runner.environment == 'github-hosted' && 'maven' || '' }}" cache-dependency-path: |- packages/sdk-java/**/pom.xml - packages/managed-agent-server/pom.xml + packages/managed-agent-server/**/pom.xml settings-path: '${{ runner.temp }}/setup-java-m2' - name: 'Point Maven at the pool mirror' if: "${{ runner.environment == 'self-hosted' }}" @@ -1773,7 +1787,14 @@ jobs: # Maven runs would rewrite through into the host cache. Drop them # so the install writes fresh inodes. rm -rf -- "${RUNNER_TEMP}/m2-repo/com/alibaba/qwencode-sdk" \ - "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-server" + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-parent" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-contracts" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-control-plane" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-runtime-broker" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-persistence" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-adapters" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-server" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-verification" mvn --batch-mode --no-transfer-progress -f packages/sdk-java/qwencode/pom.xml -DskipTests -Dgpg.skip=true -Dmaven.javadoc.skip=true install - name: 'Cache the RocketMQ distribution' uses: 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9' # v6.1.0 @@ -1816,9 +1837,9 @@ jobs: with: name: 'event-transport-broker-gate-reports' path: |- - packages/managed-agent-server/target/failsafe-reports/ - packages/managed-agent-server/target/event-transport-latency*.json - packages/managed-agent-server/target/event-transport-latency.jfr + packages/managed-agent-server/verification/target/failsafe-reports/ + packages/managed-agent-server/verification/target/event-transport-latency*.json + packages/managed-agent-server/verification/target/event-transport-latency.jfr ${{ runner.temp }}/rocketmq/logs/ ${{ runner.temp }}/rocketmq/home/logs/ retention-days: 7 @@ -1970,7 +1991,7 @@ jobs: cache: "${{ runner.environment == 'github-hosted' && 'maven' || '' }}" cache-dependency-path: |- packages/sdk-java/**/pom.xml - packages/managed-agent-server/pom.xml + packages/managed-agent-server/**/pom.xml settings-path: '${{ runner.temp }}/setup-java-m2' - name: 'Point Maven at the pool mirror' if: "${{ runner.environment == 'self-hosted' }}" @@ -2055,7 +2076,14 @@ jobs: # Maven runs would rewrite through into the host cache. Drop them # so the install writes fresh inodes. rm -rf -- "${RUNNER_TEMP}/m2-repo/com/alibaba/qwencode-sdk" \ - "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-server" + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-parent" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-contracts" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-control-plane" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-runtime-broker" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-persistence" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-adapters" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-server" \ + "${RUNNER_TEMP}/m2-repo/com/alibaba/qwen-managed-agent-verification" mvn --batch-mode --no-transfer-progress -f packages/sdk-java/qwencode/pom.xml -DskipTests -Dgpg.skip=true -Dmaven.javadoc.skip=true install - name: 'Install libfaketime' # The clock-skew row moves one node's whole wall clock. A pool host @@ -2152,8 +2180,8 @@ jobs: with: name: 'event-transport-multi-node-gate-reports' path: |- - packages/managed-agent-server/target/failsafe-reports/ - packages/managed-agent-server/target/mq3-multi-node/ + packages/managed-agent-server/verification/target/failsafe-reports/ + packages/managed-agent-server/verification/target/mq3-multi-node/ ${{ runner.temp }}/rocketmq/logs/ ${{ runner.temp }}/rocketmq/home/logs/ retention-days: 7 diff --git a/.github/workflows/verify-maven-repo.yml b/.github/workflows/verify-maven-repo.yml index 11541632947..83afd932253 100644 --- a/.github/workflows/verify-maven-repo.yml +++ b/.github/workflows/verify-maven-repo.yml @@ -12,7 +12,7 @@ on: branches: ['main'] paths: - 'packages/sdk-java/*/pom.xml' - - 'packages/managed-agent-server/pom.xml' + - 'packages/managed-agent-server/**/pom.xml' - '.github/workflows/verify-maven-repo.yml' workflow_dispatch: @@ -107,7 +107,14 @@ jobs: repo="${RUNNER_TEMP:?}/verify-maven-repo" STRIP_ARTIFACTS=( qwencode-sdk + qwen-managed-agent-parent + qwen-managed-agent-contracts + qwen-managed-agent-control-plane + qwen-managed-agent-runtime-broker + qwen-managed-agent-persistence + qwen-managed-agent-adapters qwen-managed-agent-server + qwen-managed-agent-verification acp-sdk ) for artifact in "${STRIP_ARTIFACTS[@]}"; do @@ -118,4 +125,4 @@ jobs: uses: 'actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830' # v4.3.0 with: path: '${{ runner.temp }}/verify-maven-repo' - key: "verify-maven-repo-${{ hashFiles('packages/sdk-java/*/pom.xml', 'packages/managed-agent-server/pom.xml') }}" + key: "verify-maven-repo-${{ hashFiles('packages/sdk-java/*/pom.xml', 'packages/managed-agent-server/**/pom.xml') }}" diff --git a/.prettierignore b/.prettierignore index 3841067e73e..49ca69a4063 100644 --- a/.prettierignore +++ b/.prettierignore @@ -42,3 +42,4 @@ packages/core/src/skills/bundled/browser-use/runtime/ # next sync. eslint.config.js already ignores packages/mobile-mcp/** for the # same reason; .prettierignore had cua-driver but not its sibling. packages/mobile-mcp/ +packages/managed-agent-server/**/target/ diff --git a/deploy/k2-resident/README.md b/deploy/k2-resident/README.md index e58e791b475..6a7cf7e7a68 100644 --- a/deploy/k2-resident/README.md +++ b/deploy/k2-resident/README.md @@ -59,11 +59,11 @@ bin/qwen-managed-agent-admin csi-protection \ /absolute/path/registration.json resident-protection resident-binding ``` -From a module build, run the same entry from the executable server jar: +From a module build, run the same entry from the repository root: ```bash -java -Dloader.main=com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceCsiProtectionMain \ - -cp target/qwen-managed-agent-server--exec.jar \ +java -Dloader.main=com.alibaba.qwen.code.managedagent.maintenance.WorkspaceCsiProtectionMain \ + -cp packages/managed-agent-server/server/target/qwen-managed-agent-server--exec.jar \ org.springframework.boot.loader.launch.PropertiesLauncher \ /absolute/path/registration.json resident-protection resident-binding ``` @@ -121,11 +121,11 @@ bin/qwen-managed-agent-admin resident-retirement \ --spring.config.additional-location=file:/absolute/path/application.yaml ``` -From a module build, use the executable server jar and the same arguments: +From a module build, run from the repository root with the same arguments: ```bash -java -Dloader.main=com.alibaba.qwen.code.managedagent.persistence.ResidentWorkspaceRetirementMain \ - -cp target/qwen-managed-agent-server--exec.jar \ +java -Dloader.main=com.alibaba.qwen.code.managedagent.maintenance.ResidentWorkspaceRetirementMain \ + -cp packages/managed-agent-server/server/target/qwen-managed-agent-server--exec.jar \ org.springframework.boot.loader.launch.PropertiesLauncher \ inspect /absolute/path/retirement.json \ --spring.config.additional-location=file:/absolute/path/application.yaml diff --git a/deploy/k2-resident/README.zh-CN.md b/deploy/k2-resident/README.zh-CN.md index 0965c85ebb4..4f8a72a74e8 100644 --- a/deploy/k2-resident/README.zh-CN.md +++ b/deploy/k2-resident/README.zh-CN.md @@ -46,11 +46,11 @@ bin/qwen-managed-agent-admin csi-protection \ /absolute/path/registration.json resident-protection resident-binding ``` -在模块构建中,从可执行的 server jar 运行同一个入口: +使用模块构建产物时,在仓库根目录运行同一个入口: ```bash -java -Dloader.main=com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceCsiProtectionMain \ - -cp target/qwen-managed-agent-server--exec.jar \ +java -Dloader.main=com.alibaba.qwen.code.managedagent.maintenance.WorkspaceCsiProtectionMain \ + -cp packages/managed-agent-server/server/target/qwen-managed-agent-server--exec.jar \ org.springframework.boot.loader.launch.PropertiesLauncher \ /absolute/path/registration.json resident-protection resident-binding ``` @@ -103,11 +103,11 @@ bin/qwen-managed-agent-admin resident-retirement \ --spring.config.additional-location=file:/absolute/path/application.yaml ``` -在模块构建中,使用可执行的 server jar 和相同的参数: +使用模块构建产物时,在仓库根目录运行,并使用相同的参数: ```bash -java -Dloader.main=com.alibaba.qwen.code.managedagent.persistence.ResidentWorkspaceRetirementMain \ - -cp target/qwen-managed-agent-server--exec.jar \ +java -Dloader.main=com.alibaba.qwen.code.managedagent.maintenance.ResidentWorkspaceRetirementMain \ + -cp packages/managed-agent-server/server/target/qwen-managed-agent-server--exec.jar \ org.springframework.boot.loader.launch.PropertiesLauncher \ inspect /absolute/path/retirement.json \ --spring.config.additional-location=file:/absolute/path/application.yaml diff --git a/docs/design/2026-10-15-managed-agent-server-layout.md b/docs/design/2026-10-15-managed-agent-server-layout.md index dc00223d1d2..9bc25b16a43 100644 --- a/docs/design/2026-10-15-managed-agent-server-layout.md +++ b/docs/design/2026-10-15-managed-agent-server-layout.md @@ -2,6 +2,8 @@ [English](2026-10-15-managed-agent-server-layout.md) | [简体中文](2026-10-15-managed-agent-server-layout.zh-CN.md) +Successor: [Maven module boundaries](2026-10-29-managed-agent-maven-modules.md). This document retains its historical decisions and validation record; the successor defines the current build layout. + Status: implemented and validated locally. Remote CI and real MySQL/Linux deployment acceptance have not been run for this change. ## Problem and current state diff --git a/docs/design/2026-10-15-managed-agent-server-layout.zh-CN.md b/docs/design/2026-10-15-managed-agent-server-layout.zh-CN.md index 90d3f341fd7..ed0e232c6e7 100644 --- a/docs/design/2026-10-15-managed-agent-server-layout.zh-CN.md +++ b/docs/design/2026-10-15-managed-agent-server-layout.zh-CN.md @@ -2,6 +2,8 @@ [English](2026-10-15-managed-agent-server-layout.md) | [简体中文](2026-10-15-managed-agent-server-layout.zh-CN.md) +后继设计:[Maven 多模块边界](2026-10-29-managed-agent-maven-modules.zh-CN.md)。本文保留当时的决策与验证记录;当前构建结构以多模块设计为准。 + 状态:已实现并完成本地验证。本次尚未运行远端 CI 或真实 MySQL/Linux 部署验收。 ## 问题与现状 diff --git a/docs/design/2026-10-19-managed-agent-package-boundaries.md b/docs/design/2026-10-19-managed-agent-package-boundaries.md index 98d1da15955..9c9b0e17bd2 100644 --- a/docs/design/2026-10-19-managed-agent-package-boundaries.md +++ b/docs/design/2026-10-19-managed-agent-package-boundaries.md @@ -2,6 +2,8 @@ [English](2026-10-19-managed-agent-package-boundaries.md) | [简体中文](2026-10-19-managed-agent-package-boundaries.zh-CN.md) +Successor: [Maven module boundaries](2026-10-29-managed-agent-maven-modules.md). This document retains its historical decisions and validation record; the successor defines the current build layout. + Status: implemented and verified locally on main base `f37e003d9450`. Remote CI and formal review are tracked on [PR #14710](https://github.com/QwenLM/qwen-code/pull/14710). The optional MySQL, Hosted and Linux fault profiles were not run locally for this package reorganization. ## Problem and current state diff --git a/docs/design/2026-10-19-managed-agent-package-boundaries.zh-CN.md b/docs/design/2026-10-19-managed-agent-package-boundaries.zh-CN.md index 4e9667c95da..7d152db3398 100644 --- a/docs/design/2026-10-19-managed-agent-package-boundaries.zh-CN.md +++ b/docs/design/2026-10-19-managed-agent-package-boundaries.zh-CN.md @@ -2,6 +2,8 @@ [English](2026-10-19-managed-agent-package-boundaries.md) | [简体中文](2026-10-19-managed-agent-package-boundaries.zh-CN.md) +后继设计:[Maven 多模块边界](2026-10-29-managed-agent-maven-modules.zh-CN.md)。本文保留当时的决策与验证记录;当前构建结构以多模块设计为准。 + 状态:已在主干基点 `f37e003d9450` 上实现并完成本地验证。远端 CI 与正式评审状态见 [PR #14710](https://github.com/QwenLM/qwen-code/pull/14710)。本次包调整未在本地运行可选的 MySQL、Hosted 和 Linux 故障专项 profile。 ## 问题与现状 diff --git a/docs/design/2026-10-23-managed-agent-admin-launcher.md b/docs/design/2026-10-23-managed-agent-admin-launcher.md index f486967a8f3..dd9d1e4f2ab 100644 --- a/docs/design/2026-10-23-managed-agent-admin-launcher.md +++ b/docs/design/2026-10-23-managed-agent-admin-launcher.md @@ -36,18 +36,18 @@ That form does not work for an operator who installed the reference distribution | Command | Entry point | | ------------------------ | ---------------------------------------------- | -| `storage-registration` | `persistence.WorkspaceStorageRegistrationMain` | -| `workspace-recovery` | `persistence.WorkspaceRecoveryMain` | -| `workspace-migration` | `workspace.runtime.WorkspaceMigrationMain` | -| `maintenance-recovery` | `persistence.WorkspaceMaintenanceRecoveryMain` | -| `operator-recovery` | `workspace.runtime.WorkspaceRecoveryCommand` | -| `csi-registration` | `persistence.WorkspaceCsiRegistrationMain` | -| `csi-protection` | `workspace.runtime.WorkspaceCsiProtectionMain` | -| `csi-session` | `persistence.WorkspaceCsiSessionMain` | -| `csi-runtime` | `persistence.WorkspaceCsiRuntimeMain` | -| `csi-retirement` | `persistence.WorkspaceCsiRetirementMain` | -| `csi-worker-ack` | `persistence.WorkspaceCsiWorkerAckMain` | -| `resident-retirement` | `persistence.ResidentWorkspaceRetirementMain` | +| `storage-registration` | `maintenance.WorkspaceStorageRegistrationMain` | +| `workspace-recovery` | `maintenance.WorkspaceRecoveryMain` | +| `workspace-migration` | `maintenance.WorkspaceMigrationMain` | +| `maintenance-recovery` | `maintenance.WorkspaceMaintenanceRecoveryMain` | +| `operator-recovery` | `maintenance.WorkspaceRecoveryCommand` | +| `csi-registration` | `maintenance.WorkspaceCsiRegistrationMain` | +| `csi-protection` | `maintenance.WorkspaceCsiProtectionMain` | +| `csi-session` | `maintenance.WorkspaceCsiSessionMain` | +| `csi-runtime` | `maintenance.WorkspaceCsiRuntimeMain` | +| `csi-retirement` | `maintenance.WorkspaceCsiRetirementMain` | +| `csi-worker-ack` | `maintenance.WorkspaceCsiWorkerAckMain` | +| `resident-retirement` | `maintenance.ResidentWorkspaceRetirementMain` | | `resolved-configuration` | `preflight.ResolvedConfiguration` | All classes are under `com.alibaba.qwen.code.managedagent`. diff --git a/docs/design/2026-10-23-managed-agent-admin-launcher.zh-CN.md b/docs/design/2026-10-23-managed-agent-admin-launcher.zh-CN.md index ee9289b3951..f7b62a4caf2 100644 --- a/docs/design/2026-10-23-managed-agent-admin-launcher.zh-CN.md +++ b/docs/design/2026-10-23-managed-agent-admin-launcher.zh-CN.md @@ -36,18 +36,18 @@ server 有十二个离线维护入口,包括 Workspace storage 注册、Worksp | 命令 | 入口 | | ------------------------ | ---------------------------------------------- | -| `storage-registration` | `persistence.WorkspaceStorageRegistrationMain` | -| `workspace-recovery` | `persistence.WorkspaceRecoveryMain` | -| `workspace-migration` | `workspace.runtime.WorkspaceMigrationMain` | -| `maintenance-recovery` | `persistence.WorkspaceMaintenanceRecoveryMain` | -| `operator-recovery` | `workspace.runtime.WorkspaceRecoveryCommand` | -| `csi-registration` | `persistence.WorkspaceCsiRegistrationMain` | -| `csi-protection` | `workspace.runtime.WorkspaceCsiProtectionMain` | -| `csi-session` | `persistence.WorkspaceCsiSessionMain` | -| `csi-runtime` | `persistence.WorkspaceCsiRuntimeMain` | -| `csi-retirement` | `persistence.WorkspaceCsiRetirementMain` | -| `csi-worker-ack` | `persistence.WorkspaceCsiWorkerAckMain` | -| `resident-retirement` | `persistence.ResidentWorkspaceRetirementMain` | +| `storage-registration` | `maintenance.WorkspaceStorageRegistrationMain` | +| `workspace-recovery` | `maintenance.WorkspaceRecoveryMain` | +| `workspace-migration` | `maintenance.WorkspaceMigrationMain` | +| `maintenance-recovery` | `maintenance.WorkspaceMaintenanceRecoveryMain` | +| `operator-recovery` | `maintenance.WorkspaceRecoveryCommand` | +| `csi-registration` | `maintenance.WorkspaceCsiRegistrationMain` | +| `csi-protection` | `maintenance.WorkspaceCsiProtectionMain` | +| `csi-session` | `maintenance.WorkspaceCsiSessionMain` | +| `csi-runtime` | `maintenance.WorkspaceCsiRuntimeMain` | +| `csi-retirement` | `maintenance.WorkspaceCsiRetirementMain` | +| `csi-worker-ack` | `maintenance.WorkspaceCsiWorkerAckMain` | +| `resident-retirement` | `maintenance.ResidentWorkspaceRetirementMain` | | `resolved-configuration` | `preflight.ResolvedConfiguration` | 所有类都位于 `com.alibaba.qwen.code.managedagent` 包下。 diff --git a/docs/design/2026-10-29-managed-agent-maven-modules.md b/docs/design/2026-10-29-managed-agent-maven-modules.md new file mode 100644 index 00000000000..dca511df68e --- /dev/null +++ b/docs/design/2026-10-29-managed-agent-maven-modules.md @@ -0,0 +1,281 @@ +# Managed Agent Server Maven modules + +[English](2026-10-29-managed-agent-maven-modules.md) | [简体中文](2026-10-29-managed-agent-maven-modules.zh-CN.md) + +Status: implemented in the working tree; final acceptance is pending CI. The +investigation used `523be857db`. This design supersedes the single-module +boundary in the [source layout](2026-10-15-managed-agent-server-layout.md) and +[Java package design](2026-10-19-managed-agent-package-boundaries.md). + +## Problem and constraints + +The baseline has 391 production Java sources, 614 test Java sources and 18 Maven +profiles. Lexical dependency analysis found cycles spanning 83 and 35 production +sources. Models import stores, orchestration recognizes concrete JDBC classes, +services contain SQL, and repositories call business services and external tools. +Configuration, test fixtures, launchers and CI also assume one source tree. + +The change establishes internal compile-time boundaries in one coordinated +migration. Modules share one version and release. Java source compatibility may +change; repository consumers migrate together. HTTP routes, JSON, configuration +keys and defaults, database bytes, transaction boundaries, identity, fencing and +recovery behavior remain unchanged. There is no database migration, service split, +new product feature, independent module versioning or unrelated large-class +decomposition. Product-specific integrations remain outside this application. + +## Modules and dependencies + +The parent is `com.alibaba:qwen-managed-agent-parent`, with `pom` packaging. +Production children use `qwen-managed-agent-`; `server` preserves +`com.alibaba:qwen-managed-agent-server`. All children inherit the parent version. + +| Directory | Responsibility | Internal production dependencies | +| ---------------- | ------------------------------------------------------------------------------------------ | -------------------------------- | +| `contracts` | Interfaces, values, errors, read-only settings and pure protocol rules | None | +| `control-plane` | Session, Workspace, task, lifecycle, automation, Channel and extension orchestration | `contracts` | +| `runtime-broker` | Runtime bindings, execution, leases, cancellation, draining and recovery | `contracts` | +| `persistence` | Control-plane and Broker JDBC/memory stores, schema and transactions | `contracts` | +| `adapters` | Harness SDK, physical providers, HTTP clients, OSS, MQ, Git and filesystem implementations | `contracts` | +| `server` | HTTP servers, Spring composition, startup, preflight and operator entry points | All other production modules | +| `verification` | Existing tests, fixtures, architectural checks and artifact acceptance; never published | All production modules | + +The four implementation modules do not depend on one another. The server injects +their implementations through contracts. A production Java package belongs to one +artifact. No sibling source directories, aggregate compilation classpath, shading, +generic service registry or catch-all common module bypasses these boundaries. + +Contracts may use the existing JSON, validation annotation and Commons libraries. +They must not reference Spring, JDBC, the Java SDK or cloud clients, or perform +database, network or process operations. Control-plane code may retain Spring +components, scheduling and transaction support, but not JDBC or concrete external +clients. Broker orchestration remains independent of Spring. + +Separate Maven artifacts make forbidden implementation imports fail during compilation: sibling implementations are absent from each module's compile classpath. Dependency rules and ArchUnit checks supplement that boundary; a single artifact would leave all implementations on the same classpath. The coordinated package migration gives each package one artifact owner without preserving cross-JAR package-private access. This is the agreed internal-isolation scope, with shared versioning and one service delivery. + +Source counts do not determine module ownership. Broker requests, identities, records and deterministic wire validators belong to contracts when multiple modules consume them without I/O. Runtime scheduling and recovery decisions belong to the Broker implementation. A small Broker implementation module is therefore expected; moving its shared protocol rules back would introduce implementation dependencies in consumers. + +## Contract and implementation changes + +Move existing storage, Harness, Runtime, event and object-store interfaces and +their shared values into contracts. Extract only interfaces needed at actual +module boundaries. Move externally used nested store records with their value +semantics; shared models no longer depend on store implementations. Represent +application HTTP status as an integer and translate it in the HTTP layer without +changing codes, details or causes. Preserve protocol strings, JSON names, nulls, +ordering, encodings and digest inputs. Move pure request-identity calculations and +provider-kind constants out of JDBC/provider classes. + +`ManagedAgentSettings` supplies read-only views needed outside server. Authentication, +internal listeners, the service account, trusted headers and credential-loading +state remain server settings without shared contract declarations. +`ManagedAgentProperties` remains server-owned and implements those views, retaining +binding annotations, duration units, environment access, credential-file reads and +startup checks. Consumers receive views or existing scalar values. This introduces +neither configuration snapshots nor dynamic refresh. + +The seven transactional default entry points on `AgentStateStore` receive explicit +overrides on the JDBC implementation with their existing transaction semantics. +Removing the interface annotations must not lose proxy interception on delegating +overloads. Existing Spring and native JDBC transaction mechanisms stay distinct. +Same-connection operations and package-private locking helpers remain together in +persistence; `Connection` does not escape into ordinary repository contracts. +Move each existing transaction body as a whole, never as independently committed +CRUD operations. + +`NativeCsiExecutionRepository` carries the existing private CSI preparation and +batch-read capabilities. The original JDBC binding store implements it; memory +stores do not. The Broker consumes the capability instead of concrete JDBC types. +Capability discovery never substitutes for transactional admission, original +DataSource identity, writer/generation checks or locking. Missing capabilities keep +the existing refusal behavior. A neutral store failure preserves genuine-outage +classification without exposing JDBC exception types. A Broker operations +interface includes the operations consumed outside its implementation module. +Convenience overloads without those consumers remain on the implementation only. + +Move recovery queries and SQL-backed evidence into persistence. Inject the +existing Channel repository interfaces rather than constructing JDBC repositories +inside the service. Separate extension transactions, archive access and business +coordination without changing their order. Extract user-message authorization and +input policy without moving authorization outside its original transaction or +introducing a policy-to-message-store dependency. Git and SSH work cross explicit +ports at the same call sites as before. + +Only adapters depend directly on the Java SDK and cloud clients. Map Harness +results and synchronous, asynchronous and callback errors into contract values. +Keep HTTP, transport, protocol, generation, capability and refusal distinctions, +including retry inputs, status, reason and cause. Delegate SDK payload-digest and +permission-rule algorithms through narrow ports instead of duplicating them. +Workspace scope and authorization wrappers stay in the control plane. Physical +provisioning and communication belong to adapters; construction and listeners +belong to server composition. + +CSI maintenance retains its durable sequence: lock and save the original attempt, +perform the bounded Kubernetes operation, then fence and save the receipt or +unknown outcome. Preserve UID/resourceVersion, request bytes and receipt digests. +A lost response remains UNKNOWN and does not authorize blind replay. Operator +entry points retain their isolated application context and do not start the public +server or business schedulers. + +## Build, resources and verification + +Keep JDK 21, Spring Boot 3.5.16 and existing build-plugin versions. The parent +manages versions; children declare their own libraries. The reactor builds all +modules, with Boot repackage only in `server`. Preserve the executable and operator +classifiers and published server coordinate; outputs move to `server/target`. +The SDK retains its existing separately installed build prerequisite. Publish the +parent and every production dependency at the same version; never publish the +verification module. + +Server composition preserves non-servlet embedding, explicitly enabled internal +callbacks and standalone serving. Keep named managed data sources, transaction +managers, mappers, executors and schedulers separate from host defaults. Restrict +component scans and maintain a single owner for closing each resource. + +| Resource | Owner | +| --------------------------------------------- | -------------- | +| OpenAPI and shared protocol schemas | `contracts` | +| CSI native-tool protocol declarations | `contracts` | +| V1 database schema | `persistence` | +| Application YAML and Spring registration | `server` | +| Test fixtures and JUnit service registrations | `verification` | + +Classpath resource names stay unchanged and each resource has one artifact owner. +The TypeScript tool-result schema remains authoritative. Update launchers, Docker, +distribution/version checks, WebShell contract reads, historical-path fallbacks +and CI consumers together. Resource moves do not increment protocol/schema versions. + +Move all existing tests and fixtures to verification, preserving their execution +families and all 18 profiles. Artifact tests run after server packaging and read +the server output directory. Keep MySQL fork routing, JUnit extensions, fault-test +environment and real-process parameters. Report completeness checks examine the +complete relocated test tree. Checkstyle and SpotBugs still cover every production +module. Aggregate coverage includes production classes and source, not only test +execution data. Verification directly depends on production modules at compile +scope for that aggregation, without becoming a published artifact. + +Tests keep `verification` as their working directory so local `src/test/resources` paths remain valid. Surefire and Failsafe pass `managed.module.root` for shared repository fixtures and `qwen.cli.entry` for the built CLI, including profile overrides. Repository paths are resolved from the module root instead of assuming the old process working directory. + +Use test-scoped `com.tngtech.archunit:archunit:1.5.1` under existing JUnit to enforce +the module matrix, forbidden dependencies, package ownership and nonempty scans. +No additional test engine is introduced. Framework/client types must not leak +through signatures, inheritance, annotations or exception references. + +## Implementation and acceptance + +Implement as one change: record source/test/resource/entry-point inventories; +extract contracts and remove cycles; move sources and define POMs; migrate +composition, tests and delivery tooling; verify and audit the complete result. +Maintain this design in both languages and update the development instructions. + +Acceptance covers independent module compilation; contract purity; unchanged +configuration and host isolation; rollback through all seven transaction entry +points; native CSI refusal and admission; restart, takeover and lost-response +recovery; SDK conversion and digest parity; HTTP/tenant/actor/Workspace security; +all ordinary/executable/operator artifacts and unique nested-JAR resources; and +unchanged CI test-family completeness. + +Follow the module AGENTS.md shared ten-minute local Java verification budget. +Prioritize architectural, transactional and composition regressions and affected +real integration paths. CI retains complete Java, MySQL, Hosted, fault and MQ/OSS +gates. Cross-language edits also require repository build, typecheck and affected +script/meta-tests. Do not convert skipped or unavailable integrations into passes. +Audit the full diff and evidence until two consecutive clean passes. Report local, +CI, skipped and pending results separately. Rollback replaces application and +distribution artifacts and does not rewrite persistent data. + +## Implementation notes and local evidence + +The seven-module reactor, contract extraction, composition changes, relocated +tests/resources and delivery-script updates are implemented together. All 614 +original test source files and all 18 profile names are retained. The six original +production resources retain their bytes and each has one production owner. New +regressions cover compiled module boundaries, transactional default-method +overrides, SDK failure conversion and single-instance Harness composition. + +The Harness SDK boundary wraps both single-instance and pooled connectors. The +single-instance factory explicitly supplies skills, extension records and the +managed mapper to the wrapped connector; Spring cannot inject fields into a +delegate hidden behind a returned wrapper. The settings views preserve existing +collection references and read timing. Existing filesystem identity/admission +checks remain at their original business/transaction positions; physical Git, +SSH, provider and storage-identity operations cross the extracted ports. + +Local evidence was collected on macOS with JDK 21, Maven 3.9.14, an isolated Maven +repository, Node 26.12.1 and Bash 5 for workflow shell fixtures. It does not establish +Linux, remote-cloud or production behavior. + +| Area | Observed local result | Limit | +| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Production reactor | All six production modules compiled with their declared dependencies; production Checkstyle and SpotBugs passed; ordinary, executable and six operator JARs were built | These complete quality/package results precede the last composition and collection-reference fixes | +| Architecture and transactions | Bytecode architecture check passed; all seven former default transaction entries committed and rolled back through a real Spring proxy and dedicated H2 transaction manager; 44 lock-order cases passed | H2 and proxy evidence does not establish MySQL concurrency behavior | +| Configuration and embedding | 31 property-binding cases, non-servlet embedding and explicit internal-callback tests passed | The independent Maven embedding-host fixture remains unrun | +| Broker and lifecycle | 199 Broker cases, 33 resident-retirement cases and 47 cancellation/command/automation cases passed | Full restart, Hosted, failure and MySQL profiles remain pending | +| SDK, HTTP and packaging | SDK conversion, 23 API-contract cases and three packaging cases passed; inspected executable archives contain the five internal dependency JARs with no duplicate production classes/resources | The packaged artifacts predate the final audit fixes | +| Final composition fix | The two changed production classes and the new Harness wiring regression compiled; the regression method ran successfully | This focused check was not a full Maven/Surefire run; the subsequent collection-reference-only fix still needs compilation on CI | +| TypeScript and scripts | Repository build and typecheck passed; affected CLI tests passed 288 cases and integration-helper tests passed 22; focused ESLint and affected script/workflow rechecks passed | The broad script run initially failed under macOS Bash 3.2; Bash 5 rechecks resolved those failures, while one unchanged 600 ms timing case passed only in isolation | + +The shared local Java ledger totals about 598 seconds. An earlier quality run +stopped on verification-module formatting, which was corrected and rechecked. +The configuration failures in a later selected run were corrected and all 31 +cases reran successfully. The final `mvn -DskipTests verify` attempt reached server +SpotBugs but hit its bounded timeout; it is **not** a passing full-reactor result. +No further Java runs were started after the final focused composition check. + +Acceptance still requires the final source revision to pass the unfiltered Java +reactor, aggregate coverage generation, independent embedding-host fixture and +the existing MySQL, Hosted, fault, MQ and OSS CI gates. New Java test formatting +and the final collection-reference fix also await that run. CI was not dispatched, +Central publication was not attempted, and no deployment or database migration +was performed. Rebuild the deliverables from the final revision before using or +publishing them; local intermediate JARs are verification artifacts only. + +## PR integration baseline + +PR preparation incorporates `main@68f181b885` on top of the original investigation. +The newer bounded-history queries and models remain in the repository contracts +and persistence implementation. The upstream cleanup retry coordinator depends on +a cleanup-debt repository contract and neutral Harness failures; its SQL scan and +unchanged keyset/backoff behavior remain intact. The resolved-configuration entry +point and upgrade packaging stay in server composition. All 619 current-main test +sources plus the two embedding-fixture sources are retained, including the five +tests added since the investigation. All six production resources are byte-identical +to this newer main revision; the refactor adds no further protocol/schema change. + +After integration, repository build and typecheck passed again. Reference packaging +tests passed 25 cases with one existing skip, the five affected script suites +passed 196 cases, CLI consumers passed 288 cases, and selected Maven/workflow checks +passed 22 cases. The earlier Java results above predate this integration. The shared +598-second budget was not reset: compilation, style checks and Java regressions for +the integrated revision remain pending CI, including the new repository boundary +and migrated upstream tests. Final acceptance requires those checks to pass. + +## Boilerplate reduction + +The server configuration classes use Lombok `@Getter` and `@Setter` for 349 simple +accessors. Keep normalization, defensive copies, fallback values and the +`EnvironmentAware` callback explicit. Internal credential-loading state remains +excluded from generated accessors. Existing records, field defaults and +`@DurationUnit` annotations remain unchanged; `@Data` is not used. Lombok is an +optional provided dependency of `server`, with its version inherited from Spring +Boot and an explicit compiler annotation processor. Exclude it from every Boot +executable and operator archive; other production modules do not require it. + +The SDK wrapper retains explicit interface implementations and delegates repeated +runtime-exception conversion to two private helpers for value and void calls. +Its size falls from 693 to 422 lines without reflection or a proxy framework. +Context-compression submission stays deferred, and stream epoch reads, event +reads, recovery checks and close operations still convert SDK failures after +stream creation. Preserve result identity, arguments, call order, unrelated +exceptions and `Error` propagation. + +Regression coverage adds nested configuration binding, hidden internal state, +normalization and collection semantics, immediate and deferred SDK failures, both +stream entry points and archive exclusion. Standalone Harness composition is checked +through session creation, resource publication and the resulting SDK request, rather +than connector private fields. Unused test-factory overloads and private service +forwarders are removed; existing behavioral test cases are retained. +Source comparison confirms unchanged JavaBean signatures and SDK delegate call +sites. These new Java regressions have not +run locally: the shared budget remains exhausted, and earlier Java passes do not +validate this increment. Final compilation, quality checks and regressions remain +pending CI. diff --git a/docs/design/2026-10-29-managed-agent-maven-modules.zh-CN.md b/docs/design/2026-10-29-managed-agent-maven-modules.zh-CN.md new file mode 100644 index 00000000000..2091b5cf3a3 --- /dev/null +++ b/docs/design/2026-10-29-managed-agent-maven-modules.zh-CN.md @@ -0,0 +1,207 @@ +# Managed Agent Server Maven 多模块 + +[English](2026-10-29-managed-agent-maven-modules.md) | [简体中文](2026-10-29-managed-agent-maven-modules.zh-CN.md) + +状态:已在工作树实现,最终验收待 CI。调研基线为 `523be857db`。本设计取代 +[源码布局](2026-10-15-managed-agent-server-layout.zh-CN.md)及 +[Java 包设计](2026-10-19-managed-agent-package-boundaries.zh-CN.md)中的单模块边界。 + +## 问题与约束 + +基线包含 391 个生产 Java 源文件、614 个测试 Java 源文件和 18 个 Maven profile。 +词法依赖分析发现分别涉及 83 和 35 个生产文件的循环。模型引用仓储,编排识别具体 JDBC +类,服务包含 SQL,仓储调用业务服务和外部工具。配置、测试夹具、启动器和 CI 也假定 +只有一个源码目录。 + +本次通过一次完整迁移建立工程内部的编译边界。模块统一版本和发布。允许调整 Java +源码接口,仓库内使用方同步迁移。HTTP 路由、JSON、配置键及默认值、数据库字节、事务 +边界、身份、隔离栅栏和恢复行为保持不变。不迁移数据库、不拆服务、不增加产品功能、 +不独立维护模块版本,也不附带拆解大类。产品专用集成继续放在本应用之外。 + +## 模块与依赖 + +父工程为 `com.alibaba:qwen-managed-agent-parent`,使用 `pom` packaging。生产子模块 +使用 `qwen-managed-agent-<目录名>`;`server` 保留 +`com.alibaba:qwen-managed-agent-server`。所有子模块继承父工程版本。 + +| 目录 | 职责 | 内部生产依赖 | +| ---------------- | ----------------------------------------------------------------- | ---------------- | +| `contracts` | 接口、值类型、异常、只读配置与纯协议规则 | 无 | +| `control-plane` | Session、Workspace、任务、生命周期、自动化、Channel 和扩展编排 | `contracts` | +| `runtime-broker` | Runtime 绑定、执行、租约、取消、排空及恢复 | `contracts` | +| `persistence` | 控制面和 Broker 的 JDBC/内存仓储、schema 与事务 | `contracts` | +| `adapters` | Harness SDK、物理供应器、HTTP 客户端、OSS、MQ、Git 和文件系统实现 | `contracts` | +| `server` | HTTP 服务端、Spring 装配、启动、预检与运维入口 | 其他所有生产模块 | +| `verification` | 现有测试、夹具、架构检查和产物验收;不发布 | 所有生产模块 | + +四个实现模块不相互依赖,由 server 通过契约注入实现。一个生产 Java package 只属于 +一个产物。不通过兄弟源码目录、聚合编译 classpath、shading、通用服务注册框架或 +大而全的 common 模块绕过边界。 + +契约可以使用现有 JSON、校验注解和 Commons 库,不能引用 Spring、JDBC、Java SDK 或 +云产品客户端,也不能执行数据库、网络或进程操作。控制面可以保留 Spring 组件、调度 +和事务支持,但不能引用 JDBC 或具体外部客户端。Broker 编排继续独立于 Spring。 + +独立 Maven 产物让违规实现引用在编译阶段失败:各模块的编译 classpath 中不存在兄弟实现模块。依赖规则和 ArchUnit 检查补充这一边界;单一产物仍会把所有实现放在同一个 classpath 上。同步迁移包名让每个包只有一个产物所有者,不保留跨 JAR 的包内访问。这是已确认的内部隔离范围,继续统一版本并作为一个服务交付。 + +模块归属不按源码数量决定。多个模块共同使用、且不执行 I/O 的 Broker 请求、身份、记录和确定性协议校验归契约;运行时调度与恢复决策归 Broker 实现。因此 Broker 实现模块较小是预期结果,把共享协议规则移回实现模块反而会让使用方依赖实现。 + +## 契约与实现调整 + +将现有存储、Harness、Runtime、事件和对象存储接口及共享值类型迁入契约。只在真实 +模块边界提取接口。跨模块使用的仓储嵌套记录类型连同值语义迁移,共享模型不再依赖 +仓储实现。应用 HTTP 状态改用整数,在 HTTP 层转换,保持错误码、详情和 cause。 +保留协议字符串、JSON 名称、空值、顺序、编码和摘要输入。将纯请求身份计算和 provider +kind 常量移出 JDBC/provider 类。 + +`ManagedAgentSettings` 只为 server 外的使用方提供只读视图。认证、内部监听、服务账号、 +可信请求头和凭据加载状态保留为 server 配置,不声明在共享契约中。`ManagedAgentProperties` 仍归 server +并实现这些视图,保留绑定注解、时间单位、环境访问、凭据文件读取和启动检查。使用方 +接收视图或原有标量,不引入配置快照或动态刷新。 + +在 JDBC 实现中显式覆盖 `AgentStateStore` 的七个默认事务入口,保持原事务语义。 +移除接口注解不能丢失委托重载入口的代理拦截。现有 Spring 与原生 JDBC 事务机制分别 +保留。同连接操作和包内锁辅助方法留在 persistence,普通仓储契约不暴露 `Connection`。 +每个原事务体整体迁移,不拆成分别提交的 CRUD 操作。 + +`NativeCsiExecutionRepository` 承接现有私有 CSI prepare 和 batch read 能力。原 JDBC +binding 仓储实现它,内存仓储不实现。Broker 使用能力接口而非具体 JDBC 类型。能力 +发现不替代事务 admission、原 DataSource 身份、writer/generation 校验及锁。缺失能力 +保持原拒绝行为。中立存储异常保留真实存储故障分类,不暴露 JDBC 异常类型。Broker +操作接口只包含实现模块外的实际消费操作;没有此类使用方的便利重载仅保留在实现类中。 + +恢复查询和 SQL 证据读取进入 persistence。Channel 服务注入现有仓储接口,不自行 +创建 JDBC 仓储。分离扩展事务、归档访问和业务协调,保持原顺序。提取用户消息授权及 +输入策略,不将授权移出原事务,也不引入策略对消息仓储的依赖。Git 和 SSH 在原调用 +位置通过明确的接口访问。 + +仅 adapters 直接依赖 Java SDK 和云产品客户端。将 Harness 结果以及同步、异步和回调 +异常映射为契约值。保留 HTTP、传输、协议、generation、能力不匹配和拒绝的区别,包含 +重试所需输入、状态、原因和 cause。SDK payload digest 与 permission rules 算法通过 +小接口委托复用,不复制实现。Workspace 范围和授权 wrapper 留在控制面,物理供应及 +通信进入适配器,实例创建和监听器归 server 装配。 + +CSI 维护保留持久化顺序:锁定并保存原操作尝试,执行限定的 Kubernetes 操作,然后 +通过栅栏校验保存回执或未知结果。保留 UID/resourceVersion、请求字节及回执摘要。 +响应丢失保持 UNKNOWN,不允许盲目重放。运维入口保留独立应用上下文,不启动公共服务 +或业务调度器。 + +## 构建、资源与验证 + +保留 JDK 21、Spring Boot 3.5.16 和既有构建插件版本。父工程管理版本,各子模块声明 +自身依赖。reactor 构建全部模块,仅 `server` 执行 Boot repackage。保留可执行及运维 +classifier 和服务发布坐标,产物移至 `server/target`。SDK 保留现有单独安装的构建前提。 +父 POM 与全部生产依赖同版本发布,verification 不发布。 + +server 装配保留无 Servlet 嵌入、显式内部回调和独立服务三种方式。专用数据源、事务 +管理器、mapper、执行器和调度器保留名称,与宿主默认 Bean 隔离。限定组件扫描,并让 +每项资源只有一个关闭责任方。 + +| 资源 | 归属 | +| ------------------------- | -------------- | +| OpenAPI 和共享协议 schema | `contracts` | +| CSI 原生工具协议声明 | `contracts` | +| V1 数据库 schema | `persistence` | +| 应用 YAML 与 Spring 注册 | `server` | +| 测试夹具与 JUnit 服务注册 | `verification` | + +classpath 资源名保持不变,每份资源只有一个产物所有者。TypeScript 工具结果 schema +仍为权威来源。同步更新启动器、Docker、分发和版本检查、WebShell 契约读取、历史路径 +回退及 CI 使用方。资源移动不增加协议/schema 版本。 + +将全部现有测试和夹具迁至 verification,保留执行族和全部 18 个 profile。产物测试 +在 server 打包之后读取其输出目录。保留 MySQL fork 路由、JUnit 扩展、故障测试环境及 +真实进程参数。报告完整性检查核对完整的新测试树。Checkstyle 与 SpotBugs 继续覆盖 +每个生产模块。聚合覆盖率包含生产类和源码,而不只包含测试执行数据。verification +为此以 compile scope 直接依赖生产模块,自身仍不发布。 + +测试继续以 `verification` 为工作目录,使本地 `src/test/resources` 路径保持有效。Surefire 和 Failsafe 传入用于共享仓库夹具的 `managed.module.root` 以及指向已构建 CLI 的 `qwen.cli.entry`,包括覆盖插件配置的 profile。仓库路径从模块根目录解析,不再假定旧进程工作目录。 + +使用测试范围的 `com.tngtech.archunit:archunit:1.5.1`,在现有 JUnit 下验证模块矩阵、 +禁止依赖、包归属及非空扫描,不增加测试引擎。框架/客户端类型不得从签名、继承、注解 +或异常引用中泄漏。 + +## 实施与验收 + +一次整体交付:记录源码、测试、资源和入口清单;提取契约、解除循环;移动源码并建立 +POM;迁移装配、测试和交付工具;验证并自审完整结果。同步维护双语设计与开发说明。 + +验收覆盖独立模块编译、契约纯度、配置及宿主隔离、七个事务入口回滚、原生 CSI 拒绝和 +admission、重启/接管/响应丢失恢复、SDK 转换和摘要一致性、HTTP/tenant/actor/Workspace +安全、全部普通/可执行/运维产物与唯一嵌套 JAR 资源,以及原 CI 测试族完整性。 + +本地 Java 验证遵守模块 AGENTS.md 共享十分钟预算,优先架构、事务和装配回归及受影响 +的真实集成路径。完整 Java、MySQL、Hosted、故障和 MQ/OSS 门禁由 CI 承担。跨语言 +修改还需仓库 build、typecheck 及相关脚本/meta-test。跳过或不可用的集成不能记为通过。 +完整 diff 和证据需连续两遍干净自审。分别报告本地、CI、跳过和待验证结果。回退替换 +应用及分发产物,不重写持久化数据。 + +## 实现说明与本地证据 + +七模块 reactor、契约提取、装配调整、测试/资源迁移及交付脚本更新作为同一批次实现。 +原有 614 个测试源文件和 18 个 profile 名称全部保留。六份原有生产资源保持字节不变, +各有唯一生产模块归属。新增回归覆盖编译后模块边界、默认事务方法的实现覆盖、SDK +异常转换及单实例 Harness 装配。 + +Harness SDK 边界同时包装单实例和池化 connector。单实例工厂显式向被包装的 connector +提供 skills、扩展记录和专用 mapper;Spring 无法向返回 wrapper 后隐藏的 delegate +注入字段。配置视图保留原有集合引用和读取时机。现有文件系统身份/admission 检查留在 +原业务/事务位置;物理 Git、SSH、provider 和存储身份操作通过提取的接口调用。 + +本地证据来自 macOS、JDK 21、Maven 3.9.14、隔离的 Maven 仓库、Node 26.12.1,以及 +用于工作流 Shell 夹具的 Bash 5,不能证明 Linux、远端云环境或生产行为。 + +| 范围 | 本地观测结果 | 限制 | +| ----------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| 生产 reactor | 六个生产模块均按声明依赖编译;生产 Checkstyle 与 SpotBugs 通过;普通、可执行及六个运维 JAR 已构建 | 这组完整质量/打包结果早于最后的装配与集合引用修正 | +| 架构与事务 | 字节码架构检查通过;七个原默认事务入口经真实 Spring 代理和专用 H2 事务管理器完成提交及回滚;44 个锁顺序用例通过 | H2 与代理证据不能证明 MySQL 并发行为 | +| 配置与嵌入 | 31 个属性绑定用例、无 Servlet 嵌入及显式内部回调测试通过 | 独立 Maven embedding-host 夹具尚未运行 | +| Broker 与生命周期 | 199 个 Broker、33 个驻留 Workspace 退休,以及 47 个取消/命令/自动化用例通过 | 完整重启、Hosted、故障及 MySQL profile 待验收 | +| SDK、HTTP 与打包 | SDK 转换、23 个 API 契约及三个打包用例通过;检查过的可执行包包含五个内部依赖 JAR,无重复生产类/资源 | 这些产物早于最终自审修正 | +| 最后装配修正 | 两个改动的生产类和新增 Harness 装配回归完成编译,回归方法执行成功 | 该定向检查不是完整 Maven/Surefire 执行;随后仅涉及集合引用的修正仍需 CI 编译 | +| TypeScript 与脚本 | 仓库 build 和 typecheck 通过;受影响 CLI 测试 288 项、集成辅助测试 22 项通过;定向 ESLint 及相关脚本/工作流复查通过 | 首次较广脚本执行因 macOS Bash 3.2 失败,Bash 5 复查解决了这些失败;其中一个未修改的 600 ms 时序用例仅在隔离执行时通过 | + +共享本地 Java 账本累计约 598 秒。较早的质量检查在验证模块格式处停止,修正后已复查。 +后续定向执行中的配置失败已修正,31 个用例全部重跑通过。最后一次 +`mvn -DskipTests verify` 到达 server SpotBugs 后触发限定超时,**不能记为完整 reactor +通过**。最后的定向装配检查后未再启动 Java 验证。 + +验收仍要求最终源码版本通过未过滤的 Java reactor、聚合覆盖率生成、独立 +embedding-host 夹具及现有 MySQL、Hosted、故障、MQ 和 OSS CI 门禁。新增 Java 测试 +格式和最后的集合引用修正也等待该次执行。尚未派发 CI、尝试 Central 发布、部署服务或 +迁移数据库。使用或发布前须从最终版本重新构建交付物;本地中间 JAR 仅是验证产物。 + +## PR 整合基线 + +准备 PR 时,在原调研基础上接入了 `main@68f181b885`。较新的有界历史查询及模型保留在 +仓储契约和持久化实现中。主干新增的清理重试协调器依赖清理债务仓储契约及中立 Harness +异常;SQL 扫描和原有 keyset/退避行为保持不变。配置解析入口和升级打包留在 server +装配层。当前主干的 619 个测试源文件及两个嵌入夹具源文件全部保留,包括调研后新增的 +五个测试。六份生产资源与这一较新的主干版本字节一致,重构不再附加协议/schema 变更。 + +整合后,仓库 build 和 typecheck 再次通过。参考包测试通过 25 项、保留一个既有跳过项, +五组相关脚本测试通过 196 项、CLI 使用方测试通过 288 项、定向 Maven/工作流检查通过 +22 项。上文的 Java 证据早于本次整合。共享 598 秒预算没有重置: +整合版本的编译、格式检查及 Java 回归仍待 CI,包括新增仓储边界和迁入的主干测试。 +最终验收要求这些检查通过。 + +## 样板代码精简 + +server 配置类使用 Lombok `@Getter` 和 `@Setter` 替代 349 个简单访问方法。 +规范化、防御性复制、默认值回退及 `EnvironmentAware` 回调保持显式实现。凭据加载 +内部状态排除在生成访问方法之外。现有 record、字段默认值和 `@DurationUnit` 注解 +不变,不使用 `@Data`。Lombok 仅作为 `server` 的 optional provided 依赖,版本继承 +Spring Boot,并显式配置编译器注解处理器。全部 Boot 可执行包和运维包排除 Lombok; +其他生产模块无需依赖它。 + +SDK 包装保留显式接口实现,将重复的运行时异常转换集中到两个私有辅助方法,分别处理 +有返回值和无返回值的调用。文件从 693 行缩减至 422 行,不引入反射或代理框架。 +上下文压缩提交仍延迟执行;流创建后的 epoch 读取、事件读取、恢复检查及关闭操作 +继续转换 SDK 异常。保留返回对象身份、参数、调用顺序、无关异常和 `Error` 传播。 + +回归覆盖新增嵌套配置绑定、内部状态不可绑定、规范化与集合语义、同步和延迟 SDK +异常、两个流入口及产物排除。单实例 Harness 装配通过创建会话、资源发布和最终 SDK +请求检查,不再检查连接器私有字段。删除未使用的测试工厂重载和业务服务私有转发, +保留现有行为测试用例。源码比对确认 JavaBean 方法签名及 SDK 委托调用点保持不变。 +这些新增 Java 回归尚未在本地运行:共享预算已用尽,较早的 Java 通过记录不能证明 +本次增量通过。最终编译、质量检查及回归仍待 CI。 diff --git a/docs/users/managed-local-serve.md b/docs/users/managed-local-serve.md index 14a7c6f130d..4a29fa577d0 100644 --- a/docs/users/managed-local-serve.md +++ b/docs/users/managed-local-serve.md @@ -174,7 +174,7 @@ mvn -f packages/sdk-java/qwencode/pom.xml -DskipTests -Dgpg.skip=true install mvn -f packages/managed-agent-server/pom.xml verify npm run package:managed-runtime -- \ --target darwin-arm64 \ - --server-jar packages/managed-agent-server/target/qwen-managed-agent-server-0.1.0-alpha-exec.jar \ + --server-jar packages/managed-agent-server/server/target/qwen-managed-agent-server-0.1.0-alpha-exec.jar \ --jre-home /verified/java-21-home \ --out-dir /output/managed-runtime ``` @@ -182,7 +182,7 @@ npm run package:managed-runtime -- \ The runtime builder only needs the matching server JAR, Java runtime and CLI version from this checkout. It does not require built CLI assets or a Node archive. By default it writes to -`packages/managed-agent-server/target/managed-runtime`, outside the CLI's +`packages/managed-agent-server/server/target/managed-runtime`, outside the CLI's `dist/` assets. It emits the runtime archive and a per-archive `.sha256` file. The normal CLI is built/released separately. @@ -192,7 +192,7 @@ instead of `--jre-home`: ```sh npm run package:managed-runtime -- \ --target darwin-arm64 \ - --server-jar packages/managed-agent-server/target/qwen-managed-agent-server-0.1.0-alpha-exec.jar \ + --server-jar packages/managed-agent-server/server/target/qwen-managed-agent-server-0.1.0-alpha-exec.jar \ --without-jre \ --out-dir /output/managed-runtime ``` @@ -204,7 +204,7 @@ The lightweight builder needs no Java input. For an optional complete distributi npm run package:standalone -- \ --target darwin-arm64 \ --node-archive /verified/node-v22-darwin-arm64.tar.gz \ - --managed-server-jar packages/managed-agent-server/target/qwen-managed-agent-server-0.1.0-alpha-exec.jar \ + --managed-server-jar packages/managed-agent-server/server/target/qwen-managed-agent-server-0.1.0-alpha-exec.jar \ --managed-jre-home /verified/java-21-home \ --out-dir /output/managed-local ``` diff --git a/docs/users/managed-local-serve.zh-CN.md b/docs/users/managed-local-serve.zh-CN.md index d9a6994ca7e..a40b828134e 100644 --- a/docs/users/managed-local-serve.zh-CN.md +++ b/docs/users/managed-local-serve.zh-CN.md @@ -147,14 +147,14 @@ mvn -f packages/sdk-java/qwencode/pom.xml -DskipTests -Dgpg.skip=true install mvn -f packages/managed-agent-server/pom.xml verify npm run package:managed-runtime -- \ --target darwin-arm64 \ - --server-jar packages/managed-agent-server/target/qwen-managed-agent-server-0.1.0-alpha-exec.jar \ + --server-jar packages/managed-agent-server/server/target/qwen-managed-agent-server-0.1.0-alpha-exec.jar \ --jre-home /verified/java-21-home \ --out-dir /output/managed-runtime ``` runtime 构建器只要求匹配的服务 JAR、Java runtime 和本 checkout 中的 CLI 版本, 不依赖编译好的 CLI 资源或 Node archive。默认输出到 -`packages/managed-agent-server/target/managed-runtime`,位于 CLI 的 `dist/` 资源树 +`packages/managed-agent-server/server/target/managed-runtime`,位于 CLI 的 `dist/` 资源树 之外。输出 runtime archive 及单独的 `.sha256` 文件。普通 CLI 独立构建和发行。 @@ -163,7 +163,7 @@ runtime 构建器只要求匹配的服务 JAR、Java runtime 和本 checkout 中 ```sh npm run package:managed-runtime -- \ --target darwin-arm64 \ - --server-jar packages/managed-agent-server/target/qwen-managed-agent-server-0.1.0-alpha-exec.jar \ + --server-jar packages/managed-agent-server/server/target/qwen-managed-agent-server-0.1.0-alpha-exec.jar \ --without-jre \ --out-dir /output/managed-runtime ``` @@ -175,7 +175,7 @@ npm run package:managed-runtime -- \ npm run package:standalone -- \ --target darwin-arm64 \ --node-archive /verified/node-v22-darwin-arm64.tar.gz \ - --managed-server-jar packages/managed-agent-server/target/qwen-managed-agent-server-0.1.0-alpha-exec.jar \ + --managed-server-jar packages/managed-agent-server/server/target/qwen-managed-agent-server-0.1.0-alpha-exec.jar \ --managed-jre-home /verified/java-21-home \ --out-dir /output/managed-local ``` diff --git a/eslint.config.js b/eslint.config.js index 47235e2e519..0ca70a527fc 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -78,6 +78,7 @@ export default tseslint.config( 'packages/desktop/runtime/**', 'packages/core/src/skills/bundled/browser-use/runtime/**', 'packages/desktop/src-tauri/target/**', + 'packages/managed-agent-server/**/target/**', 'packages/live-host/**', // standalone Electron app with its own Node test conventions 'packages/cua-driver/**', // vendored trycua/cua driver (Rust + scripts); not qwen-code TS 'packages/mobile-mcp/**', // vendored mobile-next/mobile-mcp; has own eslint config @@ -506,7 +507,7 @@ export default tseslint.config( 'packages/*/scripts/**/*.js', 'packages/*/scripts/**/*.mjs', 'packages/*/build.mjs', - 'packages/managed-agent-server/src/test/resources/*.mjs', + 'packages/managed-agent-server/verification/src/test/resources/**/*.mjs', // web-templates' export-html template build scripts also run with `node`. 'packages/*/src/export-html/*.mjs', // Verification reproducer scripts under docs/ also run with `node`. diff --git a/integration-tests/helpers/hosted-latency-driver.ts b/integration-tests/helpers/hosted-latency-driver.ts index 8c5eb56324e..3c6f12a8c7a 100644 --- a/integration-tests/helpers/hosted-latency-driver.ts +++ b/integration-tests/helpers/hosted-latency-driver.ts @@ -45,7 +45,7 @@ const baselinePath = path.resolve( 'integration-tests/baselines/hosted-latency.json', ); const reportPath = path.resolve( - 'packages/managed-agent-server/target/hosted-latency-baseline.json', + 'packages/managed-agent-server/verification/target/hosted-latency-baseline.json', ); const samples: HostedLatencySample[] = []; let sample: HostedLatencySample; @@ -366,7 +366,7 @@ try { 'integration-tests/helpers/hosted-harness-process.ts', 'integration-tests/helpers/hosted-relay-headers.ts', 'integration-tests/fake-openai-server.ts', - 'packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceToolTurnIT.java', + 'packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceToolTurnIT.java', ]; const sourceHashes = Object.fromEntries( await Promise.all( diff --git a/packages/cli/src/serve/capability-parity.test.ts b/packages/cli/src/serve/capability-parity.test.ts index 5186855304a..684d2c820c4 100644 --- a/packages/cli/src/serve/capability-parity.test.ts +++ b/packages/cli/src/serve/capability-parity.test.ts @@ -34,7 +34,7 @@ import { const REPO = resolve(import.meta.dirname, '../../../..'); const MANAGED_SURFACE_FILE = resolve( REPO, - 'packages/managed-agent-server/src/test/resources/capability-parity/managed-surface.txt', + 'packages/managed-agent-server/verification/src/test/resources/capability-parity/managed-surface.txt', ); const ROUTE_METHODS: ReadonlySet = new Set([ 'GET', diff --git a/packages/cli/src/serve/hosted-csi-tool-turn.test.ts b/packages/cli/src/serve/hosted-csi-tool-turn.test.ts index 4c9076bb35d..da57f8206c8 100644 --- a/packages/cli/src/serve/hosted-csi-tool-turn.test.ts +++ b/packages/cli/src/serve/hosted-csi-tool-turn.test.ts @@ -551,7 +551,7 @@ it('fixes full Parts, original IDs, exact input bytes and final definitions befo const pinned = JSON.parse( await readFile( new URL( - '../../../managed-agent-server/src/main/resources/csi-native-file-declarations.json', + '../../../managed-agent-server/contracts/src/main/resources/csi-native-file-declarations.json', import.meta.url, ), 'utf8', diff --git a/packages/cli/src/serve/hosted-input.test.ts b/packages/cli/src/serve/hosted-input.test.ts index 6fe80c3f8ca..92101ff845e 100644 --- a/packages/cli/src/serve/hosted-input.test.ts +++ b/packages/cli/src/serve/hosted-input.test.ts @@ -30,7 +30,7 @@ describe('Hosted immutable attachment input', () => { const spec = JSON.parse( readFileSync( new URL( - '../../../managed-agent-server/src/main/resources/openapi/managed-agent-public-api.openapi.json', + '../../../managed-agent-server/contracts/src/main/resources/openapi/managed-agent-public-api.openapi.json', import.meta.url, ), 'utf8', diff --git a/packages/managed-agent-server/.gitignore b/packages/managed-agent-server/.gitignore index b83d22266ac..f4ceea78560 100644 --- a/packages/managed-agent-server/.gitignore +++ b/packages/managed-agent-server/.gitignore @@ -1 +1 @@ -/target/ +**/target/ diff --git a/packages/managed-agent-server/AGENTS.md b/packages/managed-agent-server/AGENTS.md index ccf350ede11..ac2def6d4a9 100644 --- a/packages/managed-agent-server/AGENTS.md +++ b/packages/managed-agent-server/AGENTS.md @@ -86,7 +86,14 @@ cross-language changes. ### Maven and MySQL checks -Use JDK 21 and Maven 3.8.9 or newer; run Maven from this directory. +Use JDK 21 and Maven 3.8.9 or newer; run Maven from this directory. This is the +reactor parent. Production modules compile independently; all tests and fixtures +live in `verification`. Preserve the dependency matrix in the +[Maven modules design](../../docs/design/2026-10-29-managed-agent-maven-modules.md). +Checkstyle and SpotBugs apply to every production module. Test reports and +aggregate coverage live under `verification/target`; executable artifacts live +under `server/target`. The verification module must never be installed or +published. Before testing, check the toolchain and that the installed sibling SDK matches the source being tested. Reuse a valid build; otherwise install it once into @@ -144,11 +151,9 @@ work follows this module's budget; CI retains the full cross-language gates. Documentation-only changes need formatting and reference checks and do not invalidate unchanged Java test results. -Maven report output under `target/` (javadoc `reports/apidocs`, JaCoCo -`site/jacoco`) contains generated JavaScript that the repository-root ESLint -sweep does not ignore, so a root `npm run lint` fails while those directories -exist. Run `mvn clean` or delete the `target/` directories before running root -checks in the same checkout. +Maven output under every module's `target/` is ignored by Git, ESLint and +Prettier. Keep these exclusions when adding modules so generated reports and +packaged fixtures are not linted or committed as source. ## Code Review @@ -160,7 +165,7 @@ checks in the same checkout. idempotency and writer lease/generation checks across restarts: a stale owner must not commit durable state or release a newer owner's resources. - Server and Broker use the - [shared schema baseline](src/main/resources/db/managed-schema/V1__managed_schema.sql). + [shared schema baseline](persistence/src/main/resources/db/managed-schema/V1__managed_schema.sql). Until the first formal Managed Agent release, edit this V1 baseline in place; do not add V2+ migrations or increment the database schema version. Recreate development catalogs after baseline changes rather than maintaining upgrades. @@ -169,7 +174,7 @@ checks in the same checkout. migrations or use Flyway repair/history edits to bypass that refusal. Follow the README's current schema policy rather than assuming an upgrade path. - Public and WebShell API changes must keep the - [OpenAPI contract](src/main/resources/openapi/managed-agent-public-api.openapi.json), + [OpenAPI contract](contracts/src/main/resources/openapi/managed-agent-public-api.openapi.json), mapped routes, `SurfaceRegistry`, and contract tests consistent. Run `ManagedAgentApiContractTest` and `SurfaceRegistryGateTest` for those changes. Regenerate WebShell types from the repository root with diff --git a/packages/managed-agent-server/Dockerfile b/packages/managed-agent-server/Dockerfile index 3cc603ffc47..d19503baf94 100644 --- a/packages/managed-agent-server/Dockerfile +++ b/packages/managed-agent-server/Dockerfile @@ -17,7 +17,7 @@ RUN mvn --batch-mode --no-transfer-progress \ # exists. sh has no nullglob, so a zero-match glob yields the pattern # itself — break on it so the count check below still fails, and name # the expectation loudly instead of dying at cp. - && cd packages/managed-agent-server/target \ + && cd packages/managed-agent-server/server/target \ && main="" && count=0 \ && for f in qwen-managed-agent-server-*-exec.jar; do \ [ -e "$f" ] || break; \ diff --git a/packages/managed-agent-server/QWEN.md b/packages/managed-agent-server/QWEN.md index 2825e6cc403..84c07bfa0ca 100644 --- a/packages/managed-agent-server/QWEN.md +++ b/packages/managed-agent-server/QWEN.md @@ -17,7 +17,7 @@ alive. The Broker reconciles and adopts bindings through recovery-capable provisioners. The embedding service owns those provisioners, reprovisioning policy, and Session rebind policy. -Broker and Server sources, resources and tests share this Maven module. Keep their Java packages separate. The server depends on the Java SDK at `../sdk-java/qwencode`; install that SDK before building. +The parent builds six production Maven modules: `contracts`, `control-plane`, `runtime-broker`, `persistence`, `adapters`, and `server`. Tests live in `verification`. Keep each production Java package in one module. Only `adapters` depends on the Java SDK at `../sdk-java/qwencode`; install that SDK before building. Follow [AGENTS.md](AGENTS.md) for focused verification and the shared local budget. Use JDK 21 or later to build and run this module. @@ -25,7 +25,7 @@ Build and test with: ```bash mvn test -mvn checkstyle:check checkstyle:check@runtime-broker-checkstyle +mvn checkstyle:check mvn verify ``` diff --git a/packages/managed-agent-server/README.md b/packages/managed-agent-server/README.md index 43e9936ea07..675733c81ac 100644 --- a/packages/managed-agent-server/README.md +++ b/packages/managed-agent-server/README.md @@ -1,6 +1,6 @@ # Qwen Managed Agent Server -The application lives in `packages/managed-agent-server`, alongside the Java SDK. Runtime Broker sources, resources and tests are part of this Maven module; see [Runtime Broker internals](docs/runtime-broker.md). Install the Java SDK before building this application; no separate Broker artifact is needed. +The application lives in `packages/managed-agent-server`, alongside the Java SDK. It builds as one Maven reactor with six production modules and a verification module. The executable remains one Spring Boot service. Install the Java SDK before building the reactor; see [Runtime Broker internals](docs/runtime-broker.md). Standalone Spring Boot control plane for the Qwen Code Hosted Harness. It has no DataWorks dependency. The default loopback listen address keeps the @@ -91,20 +91,32 @@ adoption only. Linux operators may explicitly enable trusted-host contract. Missing or conflicting physical identity markers remain closed to execution; startup does not re-register or repair them. -## Java package layout +## Maven modules and Java packages -Business components live in `managedagent.session`, `workspace`, `artifact`, -`channel`, `automation`, `catalog` and `event`. Controllers and HTTP filters -remain in `managedagent.api`; shared contracts and application errors live in -`managedagent.model` and `exception`. The shared SQL aggregate remains in -`managedagent.persistence` so its transaction and locking helpers stay -package-private. Broker providers, HTTP adapters and credential protection live -in `runtimebroker.provisioning`, `transport` and `security` respectively. The -separate `maintenance` configuration remains outside the server's component scan. +| Module | Responsibility | Internal dependencies | +| ---------------- | --------------------------------------------------------------------------------------- | -------------------------- | +| `contracts` | Service and repository interfaces, shared values, settings views and protocol rules | None | +| `control-plane` | Session, Workspace, catalog, lifecycle, Channel, automation and extension orchestration | `contracts` | +| `runtime-broker` | Runtime execution, binding, leases, cancellation and recovery coordination | `contracts` | +| `persistence` | JDBC/memory repositories, schema and atomic database operations | `contracts` | +| `adapters` | Harness SDK, Runtime providers, HTTP clients, OSS, MQ, Git and filesystem operations | `contracts` | +| `server` | HTTP endpoints, Spring assembly, startup and maintenance entry points | All five modules | +| `verification` | Unit, integration, architecture and packaging tests; not published | All six production modules | -These are packages in one Maven module. See the package-boundary design: -[English](../../docs/design/2026-10-19-managed-agent-package-boundaries.md) | -[简体中文](../../docs/design/2026-10-19-managed-agent-package-boundaries.zh-CN.md). +The parent coordinate is `com.alibaba:qwen-managed-agent-parent`. All modules share its version; the server keeps `com.alibaba:qwen-managed-agent-server`. Each production Java package belongs to one module. Keep implementation modules independent of one another: their collaboration uses contracts injected by server configuration. SQL helpers sharing a connection remain in `managedagent.persistence` or `runtimebroker.persistence`. The Broker service lives in `runtimebroker.service`; physical clients and providers live in the adapter module. Maintenance applications retain their separate component scan. + +Only `server` creates executable JARs. Its ordinary library JAR and the existing `exec` and maintenance classifiers are under `server/target/`. Dependencies are ordinary JARs nested under `BOOT-INF/lib` in executable artifacts. Install the reactor before using the server POM directly or embedding the library in another Maven project: + +```bash +mvn -pl server -am -DskipTests install +mvn -f server/pom.xml spring-boot:run +``` + +Embedding imports `ManagedAgentConfiguration`; internal callbacks additionally import `ManagedAgentInternalConfiguration`. The standalone application also enables the public web configuration. Add the ordinary server artifact as the dependency; Maven resolves the other five modules transitively. The verification module is never installed or deployed. The standalone application YAML is excluded from the ordinary library JAR. + +Server configuration accessors use Lombok through the server POM's annotation processor; enable annotation processing when compiling through an IDE. Keep accessors with normalization, copying or fallback logic explicit. Lombok is excluded from executable and maintenance JARs and is not a runtime dependency of embedding applications. + +See the complete Maven boundary design: [English](../../docs/design/2026-10-29-managed-agent-maven-modules.md) | [简体中文](../../docs/design/2026-10-29-managed-agent-maven-modules.zh-CN.md). ## Integration status @@ -119,10 +131,10 @@ results below do not establish completion of later slices. ## API contract -`src/main/resources/openapi/managed-agent-public-api.openapi.json` is the +`contracts/src/main/resources/openapi/managed-agent-public-api.openapi.json` is the single source for the public and WebShell routes. `ManagedAgentApiContractTest` compares the mapped routes, the `ApiModels` records and real responses with it; -`src/test/resources/openapi/contract-known-gaps.txt` lists the differences that +`verification/src/test/resources/openapi/contract-known-gaps.txt` lists the differences that a later slice still has to close; none remain after D4. The WebShell client types are generated from the same file by `npm run generate:managed-agent-api` in `packages/web-shell`. The test-tree `SurfaceRegistry` names every mounted route, internal ones @@ -335,7 +347,8 @@ export QWEN_MANAGED_AGENT_HARNESS_TOKEN='replace-me' export QWEN_MANAGED_AGENT_CAPABILITY_DIGEST='sha256:replace-with-64-hex-characters' export QWEN_MANAGED_AGENT_WORKSPACE_ID='local-dev-workspace' -mvn spring-boot:run +mvn -pl server -am -DskipTests install +mvn -f server/pom.xml spring-boot:run ``` Instead of the token and digest, `QWEN_MANAGED_AGENT_HARNESS_CREDENTIALS_FILE` @@ -719,7 +732,8 @@ source export SPRING_DATASOURCE_URL='jdbc:mysql://127.0.0.1:3306/qwen_managed_agent' export SPRING_DATASOURCE_USERNAME='qwen' export SPRING_DATASOURCE_PASSWORD='replace-me' -mvn -f packages/managed-agent-server/pom.xml spring-boot:run +mvn -f packages/managed-agent-server/pom.xml -pl server -am -DskipTests install +mvn -f packages/managed-agent-server/server/pom.xml spring-boot:run ``` The daemon, Harness and Java URLs print at startup with the `spring.env` @@ -1268,7 +1282,7 @@ Sessions. Persisted bound Sessions use the private Workspace execution path below. Managed Server and Runtime Broker share the single 38-table baseline in -[`src/main/resources/db/managed-schema/V1__managed_schema.sql`](src/main/resources/db/managed-schema/V1__managed_schema.sql). +[`persistence/src/main/resources/db/managed-schema/V1__managed_schema.sql`](persistence/src/main/resources/db/managed-schema/V1__managed_schema.sql). Until the first formal Managed Agent release, schema changes update this V1 baseline in place. Do not add V2+ migrations or increment the database schema version before that release. Recreate development catalogs when the baseline changes; pre-release @@ -1431,8 +1445,9 @@ crash. Database credentials come from `W1_JDBC_URL`, `W1_JDBC_USER` and `W1_JDBC_PASSWORD` environment variables: ```bash -mvn -q -DskipTests compile exec:java \ - -Dexec.mainClass=com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageRegistrationMain \ +mvn -q -pl server -am -DskipTests -Dgpg.skip=true install +mvn -q -pl server exec:java \ + -Dexec.mainClass=com.alibaba.qwen.code.managedagent.maintenance.WorkspaceStorageRegistrationMain \ -Dexec.args='register tenant-a storage-a /absolute/canonical/workspace-a --offline-confirmed' ``` @@ -1443,7 +1458,7 @@ wraps. The same entry is available from a shipped Spring Boot fat jar, without t ```bash java -cp /path/to/app.jar \ - -Dloader.main=com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageRegistrationMain \ + -Dloader.main=com.alibaba.qwen.code.managedagent.maintenance.WorkspaceStorageRegistrationMain \ org.springframework.boot.loader.launch.PropertiesLauncher \ register tenant-a storage-a /absolute/canonical/workspace-a '' --offline-confirmed ``` @@ -1542,7 +1557,7 @@ networks whose peers you trust. ## Managed Session Store verification -Unit and H2 contract tests run with the normal Maven test phase. `mvn verify` +Run Maven from the parent directory. Unit and H2 tests run in `verification` with the normal test phase; all original profile names and test families remain available from the parent reactor. Focused commands such as `mvn -Dtest=ModuleArchitectureTest,ManagedAgentEmbeddingTest test` build required production modules first. Test reports are under `verification/target/{surefire-reports,failsafe-reports}` and aggregate coverage under `verification/target/site/jacoco-aggregate`. Do not run the verification POM alone against stale installed same-version artifacts. `mvn clean verify` checks the compiled module boundaries, the existing test families and packaged server artifacts. The embedding-host fixture is under `verification/src/test/embedding-host`; after installing the production reactor, run its normal and `embedded` profiles to verify an independent host. `mvn verify` additionally runs the SpotBugs high-confidence gate (Maven 3.8.9+): a new warning fails the build, and a false positive goes into `spotbugs-excludes.xml` with a justification in the PR. To run the static @@ -1639,7 +1654,7 @@ QWEN_MANAGED_HOOK_CGROUP_ROOT=/sys/fs/cgroup/your-delegated-root \ -Dmysql.user=root -Dmysql.password= verify ``` -Missing Linux/MySQL/cgroup prerequisites are not acceptance. This selector verifies only the extension class; it does not establish that the complete Hosted profile passed. Evidence is saved before forced fixture teardown under `target/failsafe-reports/hosted-extension-evidence/` and is included in the existing CI report upload. The interrupted MCP case leaves its public Turn `RUNNING` with durable `ma_turn.error_code = hosted_runtime_recovery_blocked` and ordinary execution `UNKNOWN`, rejects new input and close with `409 turn_active`, and does not replay the physical effect; successful cleanup is established by the separate healthy-close case. This gate uses a controlled loopback model and local stdio transport, so it does not establish upstream-model, remote transport, reboot, cross-machine, SSH MCP/Hook, or production acceptance. See the complete [English design](../../docs/design/2026-10-16-managed-extension-acceptance.md) and [Chinese design](../../docs/design/2026-10-16-managed-extension-acceptance.zh-CN.md). +Missing Linux/MySQL/cgroup prerequisites are not acceptance. This selector verifies only the extension class; it does not establish that the complete Hosted profile passed. Evidence is saved before forced fixture teardown under `verification/target/failsafe-reports/hosted-extension-evidence/` and is included in the existing CI report upload. The interrupted MCP case leaves its public Turn `RUNNING` with durable `ma_turn.error_code = hosted_runtime_recovery_blocked` and ordinary execution `UNKNOWN`, rejects new input and close with `409 turn_active`, and does not replay the physical effect; successful cleanup is established by the separate healthy-close case. This gate uses a controlled loopback model and local stdio transport, so it does not establish upstream-model, remote transport, reboot, cross-machine, SSH MCP/Hook, or production acceptance. See the complete [English design](../../docs/design/2026-10-16-managed-extension-acceptance.md) and [Chinese design](../../docs/design/2026-10-16-managed-extension-acceptance.zh-CN.md). ## Real-model end-to-end check diff --git a/packages/managed-agent-server/adapters/pom.xml b/packages/managed-agent-server/adapters/pom.xml new file mode 100644 index 00000000000..2143f254487 --- /dev/null +++ b/packages/managed-agent-server/adapters/pom.xml @@ -0,0 +1,87 @@ + + 4.0.0 + + com.alibaba + qwen-managed-agent-parent + 0.1.0-alpha + ../pom.xml + + qwen-managed-agent-adapters + Qwen Managed Agent Adapters + + ${project.basedir}/.. + + + + com.alibaba + qwen-managed-agent-contracts + + + com.alibaba + qwencode-sdk + + + com.aliyun.oss + aliyun-sdk-oss + + + org.apache.rocketmq + rocketmq-client-java + + + org.springframework + spring-context + + + org.springframework + spring-web + + + org.springframework.boot + spring-boot-autoconfigure + + + jakarta.annotation + jakarta.annotation-api + + + org.slf4j + slf4j-api + + + com.fasterxml.jackson.core + jackson-databind + + + org.apache.commons + commons-lang3 + + + commons-codec + commons-codec + + + com.alibaba.fastjson2 + fastjson2 + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + com.github.spotbugs + spotbugs-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/AliyunToolPublicationStorageAutoConfiguration.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/artifact/AliyunObjectStoreClient.java similarity index 53% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/AliyunToolPublicationStorageAutoConfiguration.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/artifact/AliyunObjectStoreClient.java index a88e6def2de..82b54d6c83e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/AliyunToolPublicationStorageAutoConfiguration.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/artifact/AliyunObjectStoreClient.java @@ -1,7 +1,5 @@ -package com.alibaba.qwen.code.managedagent.config; +package com.alibaba.qwen.code.managedagent.adapters.artifact; -import com.alibaba.qwen.code.managedagent.artifact.AliyunToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.aliyun.oss.ClientBuilderConfiguration; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; @@ -9,26 +7,26 @@ import com.aliyun.oss.common.auth.CredentialsProvider; import com.aliyun.oss.common.auth.CredentialsProviderFactory; import com.aliyun.oss.common.comm.SignVersion; import org.apache.commons.lang3.StringUtils; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; import java.net.URI; - -@AutoConfiguration -@ConditionalOnProperty(prefix = "qwen.managed-agent.tool-publication", - name = "enabled", havingValue = "true") -@ConditionalOnBean(ToolPublicationConfiguration.class) -@ConditionalOnMissingBean(ToolPublicationObjectStore.class) -public class AliyunToolPublicationStorageAutoConfiguration { - @Bean(destroyMethod = "shutdown") - public OSS toolPublicationOss(ManagedAgentProperties properties) throws Exception { - return buildOss(properties, CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider()); +public final class AliyunObjectStoreClient implements AutoCloseable { + private final OSS client; + public AliyunObjectStoreClient(OSS client) { this.client = client; } + public static AliyunObjectStoreClient environment(com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings settings) throws Exception { + return new AliyunObjectStoreClient(buildOss(settings, CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider())); } - - public static OSS buildOss(ManagedAgentProperties properties, CredentialsProvider credentials) { + public static AliyunObjectStoreClient regional(URI endpoint, String region) throws Exception { + var configuration = new ClientBuilderConfiguration(); + configuration.setSignatureVersion(SignVersion.V4); + AliyunToolPublicationObjectStore.configureClientRetries(configuration); + return new AliyunObjectStoreClient(OSSClientBuilder.create().endpoint(endpoint.toString()).region(region) + .credentialsProvider(CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider()) + .clientConfiguration(configuration).build()); + } + public AliyunToolPublicationObjectStore objects(String bucket) { return new AliyunToolPublicationObjectStore(client, bucket); } + public void shutdown() { client.shutdown(); } + public void close() { shutdown(); } + public static OSS buildOss(com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings properties, CredentialsProvider credentials) { var settings = properties.getToolPublication(); URI endpoint = URI.create(required(settings.getOssEndpoint(), "OSS endpoint")); String region = required(settings.getOssRegion(), "OSS region"); @@ -48,13 +46,6 @@ public class AliyunToolPublicationStorageAutoConfiguration { .clientConfiguration(client).build(); } - @Bean - public ToolPublicationObjectStore toolPublicationObjects(OSS toolPublicationOss, - ManagedAgentProperties properties) { - return new AliyunToolPublicationObjectStore(toolPublicationOss, - properties.getToolPublication().getOssBucket()); - } - private static String required(String value, String label) { if (StringUtils.isBlank(value)) { throw new IllegalStateException("Tool publication " + label + " is required"); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/AliyunToolPublicationObjectStore.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/artifact/AliyunToolPublicationObjectStore.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/AliyunToolPublicationObjectStore.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/artifact/AliyunToolPublicationObjectStore.java index d1d0f709e50..b0a525b6d4b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/AliyunToolPublicationObjectStore.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/artifact/AliyunToolPublicationObjectStore.java @@ -1,5 +1,7 @@ -package com.alibaba.qwen.code.managedagent.artifact; +package com.alibaba.qwen.code.managedagent.adapters.artifact; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationStorageException; import com.aliyun.oss.ClientConfiguration; import com.aliyun.oss.ClientException; import com.aliyun.oss.HttpMethod; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/LiteTopicEventTransport.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/LiteTopicEventTransport.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/LiteTopicEventTransport.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/LiteTopicEventTransport.java index 61285bfd0da..6233609dcff 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/LiteTopicEventTransport.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/LiteTopicEventTransport.java @@ -1,5 +1,8 @@ -package com.alibaba.qwen.code.managedagent.event; +package com.alibaba.qwen.code.managedagent.adapters.event; +import com.alibaba.qwen.code.managedagent.contracts.event.JournalEventTransport; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelope; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelopes; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.rocketmq.client.apis.ClientServiceProvider; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/LiteTopicPublicEventTransport.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/LiteTopicPublicEventTransport.java similarity index 87% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/LiteTopicPublicEventTransport.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/LiteTopicPublicEventTransport.java index f2b22e8fe68..6089dcf1d7b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/LiteTopicPublicEventTransport.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/LiteTopicPublicEventTransport.java @@ -1,5 +1,8 @@ -package com.alibaba.qwen.code.managedagent.event; +package com.alibaba.qwen.code.managedagent.adapters.event; +import com.alibaba.qwen.code.managedagent.contracts.event.EventTransport; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelope; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelopes; import com.alibaba.qwen.code.managedagent.model.StoreModels.EventRecord; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.rocketmq.client.apis.ClientServiceProvider; @@ -63,9 +66,10 @@ public class LiteTopicPublicEventTransport implements EventTransport, Closeable ClientServiceProvider provider, String topic, ObjectMapper mapper, BiFunction> workspaces) { this(producer, provider, topic, mapper, workspaces, - TransportExecutors.bounded(Thread.ofPlatform().daemon() - .name("event-transport-public").factory(), - 1, QUEUE_CAPACITY)); + new java.util.concurrent.ThreadPoolExecutor(1, 1, 0, java.util.concurrent.TimeUnit.MILLISECONDS, + new java.util.concurrent.ArrayBlockingQueue<>(QUEUE_CAPACITY), + Thread.ofPlatform().daemon().name("event-transport-public").factory(), + new java.util.concurrent.ThreadPoolExecutor.AbortPolicy())); } // The executor is injectable so a test can distribute synchronously. @@ -84,7 +88,14 @@ public class LiteTopicPublicEventTransport implements EventTransport, Closeable /** Stops the distribution thread; the shared producer is the config's. */ @Override public void close() { - TransportExecutors.close(executor); + if (executor instanceof java.util.concurrent.ExecutorService service) { + service.shutdown(); + try { + service.awaitTermination(2, java.util.concurrent.TimeUnit.SECONDS); + } catch (InterruptedException interrupted) { + Thread.currentThread().interrupt(); + } + } } @Override diff --git a/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/RocketMqEventClients.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/RocketMqEventClients.java new file mode 100644 index 00000000000..a68edd9c3bb --- /dev/null +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/RocketMqEventClients.java @@ -0,0 +1,146 @@ +package com.alibaba.qwen.code.managedagent.adapters.event; + +import com.alibaba.qwen.code.managedagent.contracts.event.EventMessageListener; +import com.alibaba.qwen.code.managedagent.contracts.event.EventSubscriptions; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.rocketmq.client.apis.ClientConfiguration; +import org.apache.rocketmq.client.apis.ClientException; +import org.apache.rocketmq.client.apis.ClientServiceProvider; +import org.apache.rocketmq.client.apis.consumer.ConsumeResult; +import org.apache.rocketmq.client.apis.consumer.LitePushConsumer; +import org.apache.rocketmq.client.apis.producer.Producer; +public final class RocketMqEventClients { + public static final String CAPABILITY_PROBE_LITE = "capability-probe"; + private final ClientServiceProvider provider; + public RocketMqEventClients() { this(ClientServiceProvider.loadService()); } + public RocketMqEventClients(ClientServiceProvider provider) { this.provider = provider; } + public static final class Publisher implements java.io.Closeable { + private final Producer producer; + private Publisher(Producer producer) { this.producer = producer; } + public void close() throws java.io.IOException { producer.close(); } + } + public LiteTopicEventTransport journal(Publisher publisher, ManagedAgentSettings properties) { + var config = properties.getEventTransport(); + return new LiteTopicEventTransport(publisher.producer, provider, config.getTopic(), config.getWakeBootstrapLiteTopic()); + } + public LiteTopicPublicEventTransport publicEvents(Publisher publisher, ManagedAgentSettings properties, + ObjectMapper json, java.util.function.BiFunction> workspaceOf) { + return new LiteTopicPublicEventTransport(publisher.producer, provider, properties.getEventTransport().getTopic(), json, workspaceOf); + } + public Publisher producer(ManagedAgentSettings properties) throws ClientException { + var config = properties.getEventTransport(); + ClientConfiguration clientConfig = clientConfiguration(config); + verifyLiteTopicCapability(provider, clientConfig, config.getTopic(), + config.getCapabilityProbeGroup()); + Producer producer = provider.newProducerBuilder() + .setClientConfiguration(clientConfig) + .setTopics(config.getTopic()) + .build(); + try { + verifyTopicAcceptsLiteMessages(provider, producer, + config.getTopic()); + } catch (RuntimeException failure) { + closeQuietly(producer, failure); + throw failure; + } + return new Publisher(producer); + } + + public EventSubscriptions owner( + ManagedAgentSettings properties, + EventMessageListener listener, + java.util.function.Consumer bind) + throws ClientException { + var config = properties.getEventTransport(); + LitePushConsumer consumer = provider.newLitePushConsumerBuilder() + .setClientConfiguration(clientConfiguration(config)) + .setConsumerGroup(config.getOwnerGroup()) + .bindTopic(config.getTopic()) + .setMessageListener(message -> com.alibaba.qwen.code.managedagent.adapters.event.RocketMqMessages.consume(listener, message)) + .build(); + bind.accept(com.alibaba.qwen.code.managedagent.adapters.event.RocketMqMessages.subscriptions(consumer)); + return RocketMqMessages.subscriptions(consumer); + } + + public EventSubscriptions wake(ManagedAgentSettings properties, EventMessageListener listener) + throws ClientException { + var config = properties.getEventTransport(); + LitePushConsumer consumer = provider.newLitePushConsumerBuilder() + .setClientConfiguration(clientConfiguration(config)) + .setConsumerGroup(config.getWakeBootstrapGroup()) + .bindTopic(config.getTopic()) + .setMessageListener(message -> com.alibaba.qwen.code.managedagent.adapters.event.RocketMqMessages.consume(listener, message)) + .build(); + try { + consumer.subscribeLite(config.getWakeBootstrapLiteTopic()); + } catch (ClientException | RuntimeException failure) { + closeQuietly(consumer, failure); + throw new IllegalStateException("the event-transport wake" + + " bootstrap group " + config.getWakeBootstrapGroup() + + " could not subscribe " + + config.getWakeBootstrapLiteTopic() + " on topic " + + config.getTopic() + ": pre-create it as a LITE group" + + " bound to the topic (mqadmin updateSubGroup -c" + + " -g " + config.getWakeBootstrapGroup() + + " -o true --attributes +lite.bind.topic=" + + config.getTopic() + ")", failure); + } + return RocketMqMessages.subscriptions(consumer); + } + private static ClientConfiguration clientConfiguration( + ManagedAgentSettings.EventTransport config) { + return ClientConfiguration.newBuilder() + .setEndpoints(config.getEndpoints()) + .build(); + } + + private static void closeQuietly(java.io.Closeable closeable, + Throwable failure) { + try { + closeable.close(); + } catch (Exception suppressed) { + failure.addSuppressed(suppressed); + } + } + + private static void verifyLiteTopicCapability(ClientServiceProvider provider, + ClientConfiguration clientConfig, String topic, String group) { + try (LitePushConsumer probe = provider.newLitePushConsumerBuilder() + .setClientConfiguration(clientConfig) + .setConsumerGroup(group) + .bindTopic(topic) + .setMessageListener(messageView -> ConsumeResult.SUCCESS) + .build()) { + probe.subscribeLite(CAPABILITY_PROBE_LITE); + probe.unsubscribeLite(CAPABILITY_PROBE_LITE); + } catch (Exception failure) { + throw new IllegalStateException("the event-transport LiteTopic" + + " capability probe failed on topic " + topic + ": the" + + " broker must serve LiteTopics (RocketMQ 5.5.0+ with" + + " enableLmq) and the probe group " + group + + " must be pre-created as a LITE group bound to the topic" + + " (mqadmin updateSubGroup -c -g " + group + + " -o true --attributes +lite.bind.topic=" + topic + + "); check qwen.managed-agent.event-transport.endpoints", + failure); + } + } + + private static void verifyTopicAcceptsLiteMessages( + ClientServiceProvider provider, Producer producer, String topic) { + try { + producer.send(provider.newMessageBuilder() + .setTopic(topic) + .setLiteTopic(CAPABILITY_PROBE_LITE) + .setKeys(CAPABILITY_PROBE_LITE) + .setBody("{}".getBytes(java.nio.charset.StandardCharsets.UTF_8)) + .build()); + } catch (ClientException | RuntimeException failure) { + throw new IllegalStateException("the event-transport topic " + + topic + " refused a LiteTopic message: create it as a" + + " LITE topic (mqadmin updateTopic -c -t " + + topic + " -a +message.type=LITE)", failure); + } + } +} diff --git a/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/RocketMqMessages.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/RocketMqMessages.java new file mode 100644 index 00000000000..512903c2496 --- /dev/null +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/event/RocketMqMessages.java @@ -0,0 +1,28 @@ +package com.alibaba.qwen.code.managedagent.adapters.event; + +import com.alibaba.qwen.code.managedagent.contracts.event.EventMessageListener; +import com.alibaba.qwen.code.managedagent.contracts.event.EventSubscriptions; +import org.apache.rocketmq.client.apis.consumer.ConsumeResult; +import org.apache.rocketmq.client.apis.consumer.LitePushConsumer; +import org.apache.rocketmq.client.apis.message.MessageView; + +import java.io.IOException; +import java.util.Set; +public final class RocketMqMessages { + private RocketMqMessages() { } + public static ConsumeResult consume(EventMessageListener listener, MessageView message) { + var buffer = message.getBody().duplicate(); + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + listener.consume(bytes, message.getMessageId().toString()); + return ConsumeResult.SUCCESS; + } + public static EventSubscriptions subscriptions(LitePushConsumer consumer) { + return new EventSubscriptions() { + public Set getLiteTopicSet() { return consumer.getLiteTopicSet(); } + public void subscribeLite(String topic) throws Exception { consumer.subscribeLite(topic); } + public void unsubscribeLite(String topic) throws Exception { consumer.unsubscribeLite(topic); } + public void close() throws IOException { consumer.close(); } + }; + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionArchiveStore.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/extensions/ManagedExtensionArchiveStore.java similarity index 84% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionArchiveStore.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/extensions/ManagedExtensionArchiveStore.java index ede3051e83b..0daab988244 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionArchiveStore.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/extensions/ManagedExtensionArchiveStore.java @@ -1,11 +1,11 @@ -package com.alibaba.qwen.code.managedagent.extensions; +package com.alibaba.qwen.code.managedagent.adapters.extensions; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import java.io.IOException; @@ -19,10 +19,10 @@ import java.nio.file.StandardOpenOption; import java.util.UUID; @Component -public class ManagedExtensionArchiveStore { - private final ManagedAgentProperties properties; +public class ManagedExtensionArchiveStore implements com.alibaba.qwen.code.managedagent.contracts.extensions.ExtensionArchives { + private final ManagedAgentSettings properties; - public ManagedExtensionArchiveStore(ManagedAgentProperties properties) { + public ManagedExtensionArchiveStore(ManagedAgentSettings properties) { this.properties = properties; } @@ -44,7 +44,7 @@ public class ManagedExtensionArchiveStore { while ((count = source.read(buffer)) != -1) { length += count; if (length > ManagedExtensionPackages.MAX_ARCHIVE_BYTES) { - throw new ManagedAgentException(HttpStatus.PAYLOAD_TOO_LARGE, + throw new ManagedAgentException(413, "extension_package_too_large", "Extension archive exceeds 100 MiB."); } hash.update(buffer, 0, count); @@ -52,7 +52,7 @@ public class ManagedExtensionArchiveStore { } } if (length == 0 || !digest.equals(org.apache.commons.codec.binary.Hex.encodeHexString(hash.digest()))) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "extension_package_digest_mismatch", "Extension archive digest does not match its contents."); } try (FileChannel file = FileChannel.open(temporary, StandardOpenOption.WRITE)) { @@ -90,7 +90,7 @@ public class ManagedExtensionArchiveStore { throw unavailable(); } if (digest == null || !digest.matches("[a-f0-9]{64}")) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", "Invalid extension digest."); + throw new ManagedAgentException(400, "invalid_request", "Invalid extension digest."); } Path root = Path.of(properties.getExtensions().getArtifactDirectory()).toAbsolutePath().normalize(); if (Files.exists(root, LinkOption.NOFOLLOW_LINKS) && Files.isSymbolicLink(root)) { @@ -115,7 +115,7 @@ public class ManagedExtensionArchiveStore { } private static ManagedAgentException unavailable() { - return new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, + return new ManagedAgentException(503, "extension_package_unavailable", "The saved extension archive is unavailable."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentWorkspaceProvisioner.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/runtime/ResidentWorkspaceProvisioner.java similarity index 76% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentWorkspaceProvisioner.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/runtime/ResidentWorkspaceProvisioner.java index e2759fd48a9..a1fd0fadc4e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentWorkspaceProvisioner.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/runtime/ResidentWorkspaceProvisioner.java @@ -1,12 +1,11 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.adapters.runtime; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.ResidentWorkspace; -import com.alibaba.qwen.code.managedagent.persistence.ResidentWorkspaces; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentWorkspaceCatalog; +import com.alibaba.qwen.code.managedagent.repository.WorkspaceCsiReservations; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker.ResidentWorkspace; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeObservation; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; @@ -15,8 +14,6 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesHttpRuntimeClient; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.transaction.PlatformTransactionManager; import java.net.URI; import java.nio.file.Path; @@ -25,27 +22,26 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.CompletionStage; -final class ResidentWorkspaceProvisioner implements RuntimeProvisioner { - private final ResidentWorkspaces residents; +public final class ResidentWorkspaceProvisioner implements RuntimeProvisioner { + private final ResidentWorkspaceCatalog residents; private final Map providers = new LinkedHashMap<>(); - ResidentWorkspaceProvisioner(ManagedAgentProperties properties, JdbcTemplate jdbc, - PlatformTransactionManager manager, JdbcRuntimeBindingRepository bindings) { + public ResidentWorkspaceProvisioner(ManagedAgentSettings properties, WorkspaceCsiReservations storage, + RuntimeBindingRepository bindings) { var config = properties.getRuntimeBroker(); - residents = new ResidentWorkspaces(config.getResidentWorkspaces()); + residents = new ResidentWorkspaceCatalog(config.getResidentWorkspaces()); if (config.getResidentWorkspaces().isEmpty() || !"workspace".equals(config.getIsolationClass()) || config.isTrustedLocalRebootRecovery() || config.isVerifiedWorkspaceRecoveryEnabled() || config.isChildWorkspacesEnabled() || !config.getWorkspaceMounts().isEmpty() || !properties.getHarness().isWorkspaceFilesEnabled() || properties.getHarness().isWorkspaceShellEnabled() || config.getResidentKubernetesApiUrl().isBlank() || config.getResidentKubernetesTokenFile().isBlank() - || config.getResidentKubernetesCaFile().isBlank() || !bindings.usesDataSource(jdbc.getDataSource())) { + || config.getResidentKubernetesCaFile().isBlank() || !storage.usesSameDataSource(bindings)) { throw new IllegalStateException("Resident Workspace requires exclusive CSI/files configuration"); } var api = new KubernetesHttpRuntimeClient(URI.create(config.getResidentKubernetesApiUrl()), Path.of(config.getResidentKubernetesTokenFile()), Path.of(config.getResidentKubernetesCaFile())); - var storage = new WorkspaceCsiReservationStore(jdbc, manager, new com.fasterxml.jackson.databind.ObjectMapper()); for (var selected : config.getResidentWorkspaces()) { - ResidentWorkspaces.requireRegistration(jdbc, selected); + storage.requireRegistration(selected); var guard = new WorkspaceCsiResourceGuard(api, selected.registration().clusterDomain(), selected.registration(), selected.protection()); providers.put(selected, new WorkspaceCsiRuntimeProvisioner(storage, bindings, selected.registration(), @@ -57,19 +53,19 @@ final class ResidentWorkspaceProvisioner implements RuntimeProvisioner { var mapping = residents.require(request.getScope()); if (request.getIsolationKey() != null || !mapping.registration().storageId().equals(request.getStorageId()) || !kind().equals(request.getProvisionerKind())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return providers.get(mapping); } @Override - public String kind() { return WorkspaceCsiReservationStore.PROVISIONER_KIND; } + public String kind() { return WorkspaceCsiReservations.PROVISIONER_KIND; } @Override public RuntimeProvisionRequest createRequest(RuntimeScope scope, String isolationKey) { var mapping = residents.require(scope); if (isolationKey != null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return new RuntimeProvisionRequest(scope, null, kind(), mapping.registration().storageId()); } @@ -106,7 +102,7 @@ final class ResidentWorkspaceProvisioner implements RuntimeProvisioner { @Override public CompletionStage release(RuntimeProvisionRequest request, RuntimeLease lease) { - return java.util.concurrent.CompletableFuture.failedFuture(WorkspaceExecutionStore.unavailable()); + return java.util.concurrent.CompletableFuture.failedFuture(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()); } @Override diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReclamation.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/runtime/ResidentWorkspaceReclamation.java similarity index 84% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReclamation.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/runtime/ResidentWorkspaceReclamation.java index 9ba2364d5ab..eb89ebca813 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReclamation.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/runtime/ResidentWorkspaceReclamation.java @@ -1,37 +1,30 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.adapters.runtime; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.ResidentWorkspace; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentReclamation.*; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker.ResidentWorkspace; import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesHttpRuntimeClient; +import com.alibaba.qwen.code.runtimebroker.spi.KubernetesMaintenanceClient; import java.time.Instant; import java.util.List; import java.util.Map; +import static com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentReclamation.DATA_POLICY; import static com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity.digest; import static com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity.map; import static com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity.text; /** Normal platform cleanup inside an exclusive Managed namespace; never an exceptional fence. */ -final class ResidentWorkspaceReclamation { - static final String DATA_POLICY = "retain-same-tenant-admin-handoff/1"; - - record Baseline(String finalizer, String bootId, String nodeResourceVersion, String nodeHeartbeat, - String leaseUid, String leaseResourceVersion, String leaseRenewTime, String attachmentUid) { } - - record Receipt(String resourceIdentity, String baselineDigest, String podResourceVersion, - String deletionTimestamp, String finishedAt, String nodeResourceVersion, String nodeHeartbeat, - String leaseResourceVersion, String leaseRenewTime, String observedAt, String dataPolicy) { } - +public final class ResidentWorkspaceReclamation { private record Node(Map value, String bootId, String resourceVersion, Instant heartbeat) { } private record Lease(String uid, String resourceVersion, Instant renewed) { } private ResidentWorkspaceReclamation() { } - static Baseline prepare(RuntimeBindingRecord original, ResidentWorkspace selected, - String finalizer, KubernetesHttpRuntimeClient api, Instant now) { + public static Baseline prepare(RuntimeBindingRecord original, ResidentWorkspace selected, + String finalizer, KubernetesMaintenanceClient api, Instant now) { var placement = map(original.getResourceHandle().getValue().get("placement")); var node = node(placement, api, now, false); var lease = lease(placement, api, now); @@ -48,8 +41,8 @@ final class ResidentWorkspaceReclamation { lease.uid(), lease.resourceVersion(), lease.renewed().toString(), text(metadata, "uid", 128)); } - static Receipt complete(RuntimeBindingRecord original, ResidentWorkspace selected, Baseline baseline, - Map pod, KubernetesHttpRuntimeClient api, Instant now) { + public static Receipt complete(RuntimeBindingRecord original, ResidentWorkspace selected, Baseline baseline, + Map pod, KubernetesMaintenanceClient api, Instant now) { var handle = original.getResourceHandle().getValue(); var placement = map(handle.get("placement")); require(pod != null); @@ -92,15 +85,7 @@ final class ResidentWorkspaceReclamation { lease.resourceVersion(), lease.renewed().toString(), now.toString(), DATA_POLICY); } - static void verifyReceipt(RuntimeBindingRecord original, Baseline baseline, Receipt receipt) { - require(receipt != null && receipt.resourceIdentity().equals(original.getResourceHandle().getValue().get("identity")) - && receipt.baselineDigest().equals(digest(baseline)) && DATA_POLICY.equals(receipt.dataPolicy()) - && receipt.observedAt() != null && receipt.finishedAt() != null && receipt.deletionTimestamp() != null - && !Instant.parse(receipt.observedAt()).isBefore(Instant.parse(receipt.finishedAt()))); - Instant.parse(receipt.deletionTimestamp()); - } - - private static Node node(Map placement, KubernetesHttpRuntimeClient api, Instant now, boolean fresh) { + private static Node node(Map placement, KubernetesMaintenanceClient api, Instant now, boolean fresh) { var node = api.getCluster("nodes", (String) placement.get("nodeName")).toCompletableFuture().join(); require(node != null); var metadata = map(node.get("metadata")); @@ -122,7 +107,7 @@ final class ResidentWorkspaceReclamation { text(metadata, "resourceVersion", 256), heartbeat); } - private static Lease lease(Map placement, KubernetesHttpRuntimeClient api, Instant now) { + private static Lease lease(Map placement, KubernetesMaintenanceClient api, Instant now) { var lease = api.getNodeLease((String) placement.get("nodeName")).toCompletableFuture().join(); require(lease != null); var metadata = map(lease.get("metadata")); @@ -140,7 +125,7 @@ final class ResidentWorkspaceReclamation { return new Lease(text(metadata, "uid", 128), text(metadata, "resourceVersion", 256), renewed); } - private static void exclusive(RuntimeBindingRecord original, ResidentWorkspace selected, KubernetesHttpRuntimeClient api) { + private static void exclusive(RuntimeBindingRecord original, ResidentWorkspace selected, KubernetesMaintenanceClient api) { var registration = selected.registration(); var physical = api.listPersistentVolumes().toCompletableFuture().join().stream().filter(pv -> { var spec = map(pv.get("spec")); @@ -159,7 +144,7 @@ final class ResidentWorkspaceReclamation { && placement.get("podName").equals(map(consumers.getFirst().get("metadata")).get("name"))); } - private static List> attachments(ResidentWorkspace selected, KubernetesHttpRuntimeClient api) { + private static List> attachments(ResidentWorkspace selected, KubernetesMaintenanceClient api) { return api.listVolumeAttachments().toCompletableFuture().join().stream().filter(value -> { var spec = map(value.get("spec")); if (!selected.registration().pvName().equals(map(spec.get("source")).get("persistentVolumeName"))) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiResourceGuard.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiResourceGuard.java similarity index 90% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiResourceGuard.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiResourceGuard.java index 4937bc73fde..6a896deb327 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiResourceGuard.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiResourceGuard.java @@ -1,8 +1,10 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.adapters.runtime; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity; +import com.alibaba.qwen.code.managedagent.contracts.runtime.Receipt; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesRuntimeClient; +import com.alibaba.qwen.code.runtimebroker.spi.KubernetesRuntimeClient; import java.util.LinkedHashMap; import java.util.List; @@ -20,24 +22,6 @@ public final class WorkspaceCsiResourceGuard { private final WorkspaceCsiRegistration registration; private final ProtectionIdentity protection; - public record ProtectionIdentity(String namespaceUid, String policyName, String policyUid, - long policyGeneration, String bindingName, String bindingUid, long bindingGeneration) { - public ProtectionIdentity { - for (String value : List.of(namespaceUid, policyName, policyUid, bindingName, bindingUid)) { - if (value.isBlank() || value.length() > 253 || value.codePoints().anyMatch(point -> point < 33)) { - throw new IllegalArgumentException("Invalid CSI protection identity"); - } - } - if (policyGeneration < 1 || bindingGeneration < 1) { - throw new IllegalArgumentException("Invalid CSI protection generation"); - } - } - } - - public record Receipt(String physicalKey, long registrationRevision, String namespaceUid, - String pvcResourceVersion, String pvResourceVersion, ProtectionIdentity protection) { - } - public WorkspaceCsiResourceGuard(KubernetesRuntimeClient api, String trustedClusterDomain, WorkspaceCsiRegistration registration, ProtectionIdentity protection) { this.api = Objects.requireNonNull(api); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiRuntimeProvisioner.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiRuntimeProvisioner.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiRuntimeProvisioner.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiRuntimeProvisioner.java index e67e0615b2e..bb35c0908c2 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiRuntimeProvisioner.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiRuntimeProvisioner.java @@ -1,9 +1,10 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.adapters.runtime; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRegistration; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.runtime.WorkerArtifact; +import com.alibaba.qwen.code.managedagent.repository.WorkspaceCsiReservations; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeObservation; @@ -14,13 +15,11 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; -import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesRuntimeClient; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.spi.KubernetesRuntimeClient; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiFilesProtocol; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; -import org.springframework.dao.DataAccessException; -import org.springframework.transaction.TransactionException; import java.nio.charset.StandardCharsets; import java.time.Duration; @@ -61,8 +60,8 @@ import static com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity.uu /** Private CSI placement; ambiguous creation never authorizes adoption or replacement. */ public final class WorkspaceCsiRuntimeProvisioner implements RuntimeProvisioner { private static final String BOOT_PATH = "/var/run/qwen-runtime/boot.json"; - private final WorkspaceCsiReservationStore storage; - private final JdbcRuntimeBindingRepository bindings; + private final WorkspaceCsiReservations storage; + private final RuntimeBindingRepository bindings; private final WorkspaceCsiRegistration registration; private final KubernetesRuntimeClient api; private final WorkspaceCsiResourceGuard guard; @@ -77,49 +76,41 @@ public final class WorkspaceCsiRuntimeProvisioner implements RuntimeProvisioner private final Map claims = new ConcurrentHashMap<>(); private final Map placements = new ConcurrentHashMap<>(); - public record WorkerArtifact(String configMapName, String uid, String sha256) { - public WorkerArtifact { - dns(Objects.requireNonNull(configMapName)); - uuid(Objects.requireNonNull(uid)); - require(sha256 != null && sha256.matches("[0-9a-f]{64}")); - } - } - - public WorkspaceCsiRuntimeProvisioner(WorkspaceCsiReservationStore storage, - JdbcRuntimeBindingRepository bindings, WorkspaceCsiRegistration registration) { + public WorkspaceCsiRuntimeProvisioner(WorkspaceCsiReservations storage, + RuntimeBindingRepository bindings, WorkspaceCsiRegistration registration) { this(storage, bindings, registration, null, null, null, List.of(), Duration.ofSeconds(30), List.of(), new HttpRuntimeTransport()); } - public WorkspaceCsiRuntimeProvisioner(WorkspaceCsiReservationStore storage, - JdbcRuntimeBindingRepository bindings, WorkspaceCsiRegistration registration, KubernetesRuntimeClient api, + public WorkspaceCsiRuntimeProvisioner(WorkspaceCsiReservations storage, + RuntimeBindingRepository bindings, WorkspaceCsiRegistration registration, KubernetesRuntimeClient api, WorkspaceCsiResourceGuard guard, String image, List command, Duration timeout) { this(storage, bindings, registration, api, guard, image, command, timeout, List.of()); } - public WorkspaceCsiRuntimeProvisioner(WorkspaceCsiReservationStore storage, - JdbcRuntimeBindingRepository bindings, WorkspaceCsiRegistration registration, KubernetesRuntimeClient api, + public WorkspaceCsiRuntimeProvisioner(WorkspaceCsiReservations storage, + RuntimeBindingRepository bindings, WorkspaceCsiRegistration registration, KubernetesRuntimeClient api, WorkspaceCsiResourceGuard guard, String image, List command, Duration timeout, List artifacts) { this(storage, bindings, registration, Objects.requireNonNull(api), Objects.requireNonNull(guard), image, command, timeout, artifacts, new HttpRuntimeTransport()); } - public WorkspaceCsiRuntimeProvisioner(WorkspaceCsiReservationStore storage, - JdbcRuntimeBindingRepository bindings, WorkspaceCsiRegistration registration, KubernetesRuntimeClient api, + public WorkspaceCsiRuntimeProvisioner(WorkspaceCsiReservations storage, + RuntimeBindingRepository bindings, WorkspaceCsiRegistration registration, KubernetesRuntimeClient api, WorkspaceCsiResourceGuard guard, String image, List command, Duration timeout, List artifacts, String authorityOrigin) { this(storage, bindings, registration, Objects.requireNonNull(api), Objects.requireNonNull(guard), image, command, timeout, artifacts, new HttpRuntimeTransport(), ManagedCsiFilesProtocol.authority(authorityOrigin)); } - WorkspaceCsiRuntimeProvisioner(WorkspaceCsiReservationStore storage, - JdbcRuntimeBindingRepository bindings, WorkspaceCsiRegistration registration, KubernetesRuntimeClient api, + WorkspaceCsiRuntimeProvisioner(WorkspaceCsiReservations storage, + RuntimeBindingRepository bindings, WorkspaceCsiRegistration registration, KubernetesRuntimeClient api, WorkspaceCsiResourceGuard guard, String image, List command, Duration timeout, List artifacts, RuntimeTransport transport) { this(storage, bindings, registration, api, guard, image, command, timeout, artifacts, transport, null); } - WorkspaceCsiRuntimeProvisioner(WorkspaceCsiReservationStore storage, - JdbcRuntimeBindingRepository bindings, WorkspaceCsiRegistration registration, KubernetesRuntimeClient api, + WorkspaceCsiRuntimeProvisioner(WorkspaceCsiReservations storage, + RuntimeBindingRepository bindings, WorkspaceCsiRegistration registration, KubernetesRuntimeClient api, WorkspaceCsiResourceGuard guard, String image, List command, Duration timeout, List artifacts, RuntimeTransport transport, Map authority) { this.storage = Objects.requireNonNull(storage); @@ -145,7 +136,7 @@ public final class WorkspaceCsiRuntimeProvisioner implements RuntimeProvisioner @Override public String kind() { - return WorkspaceCsiReservationStore.PROVISIONER_KIND; + return WorkspaceCsiReservations.PROVISIONER_KIND; } @Override @@ -264,7 +255,7 @@ public final class WorkspaceCsiRuntimeProvisioner implements RuntimeProvisioner } catch (RuntimeException error) { forgetPlacement(request, seed, handle); return error instanceof RuntimeBrokerException failure && failure.isRetryable() - || error instanceof DataAccessException || error instanceof TransactionException + || error instanceof com.alibaba.qwen.code.runtimebroker.StoreFailureException ? RuntimeObservation.unknown(handle) : RuntimeObservation.conflict(handle); } }); @@ -523,7 +514,7 @@ public final class WorkspaceCsiRuntimeProvisioner implements RuntimeProvisioner } } - private Map storageTuple(WorkspaceCsiReservationStore.Reservation reservation) { + private Map storageTuple(WorkspaceCsiReservations.Reservation reservation) { var result = new LinkedHashMap(); result.putAll(Map.of("clusterDomain", registration.clusterDomain(), "namespace", registration.namespace(), "pvcUid", registration.pvcUid(), "pvUid", registration.pvUid(), "driver", registration.driver(), @@ -534,7 +525,7 @@ public final class WorkspaceCsiRuntimeProvisioner implements RuntimeProvisioner return result; } - private Map protection(WorkspaceCsiResourceGuard.Receipt receipt) { + private Map protection(com.alibaba.qwen.code.managedagent.contracts.runtime.Receipt receipt) { require(registration.physicalKey().equals(receipt.physicalKey()) && registration.revision() == receipt.registrationRevision()); var pin = receipt.protection(); return Map.of("namespaceUid", pin.namespaceUid(), "policyName", pin.policyName(), "policyUid", pin.policyUid(), diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorktreeGit.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/ChildWorktreeGit.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorktreeGit.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/ChildWorktreeGit.java index 11fae0550bb..d8e39052e74 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorktreeGit.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/ChildWorktreeGit.java @@ -1,5 +1,6 @@ -package com.alibaba.qwen.code.managedagent.workspace; +package com.alibaba.qwen.code.managedagent.adapters.workspace; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorkspaceException; import org.apache.commons.lang3.StringUtils; import java.io.IOException; @@ -41,11 +42,9 @@ import java.util.regex.Pattern; * repository's own config was read as data and found free of keys that * name a program (docs/design/2026-10-09-managed-child-workspace.md). */ -public final class ChildWorktreeGit { +public final class ChildWorktreeGit implements com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorktreeGit { /** The reserved directory below the storage root that holds every child Workspace. */ - public static final String CONTAINER = ".qwen-child-workspaces"; - static final String PIN_PREFIX = "refs/qwen/child-workspaces/"; - static final int MAX_CONFLICT_PATHS = 100; + private static final int MIN_MAJOR = 2; private static final int MIN_MINOR = 40; private static final String GITLINK = "160000"; @@ -102,19 +101,10 @@ public final class ChildWorktreeGit { } /** The located repository a step works in, every path real and inside the storage root. */ - public record Repository(Path top, Path gitDir, Path commonDir) { - } /** Where the parent's repository lies, relative to the storage root and to its own top. */ - public record Layout(String repositoryRelative, String offset) { - } /** The three-way merge of decision 5: a clean tree, or the conflicting paths. */ - public record Merge(String parentTree, String mergedTree, List conflicts) { - public boolean clean() { - return conflicts.isEmpty(); - } - } /** * The Git version, refused below 2.40, the first release whose @@ -175,15 +165,8 @@ public final class ChildWorktreeGit { } /** The child Workspace's directory, relative to the storage root. */ - public static String childDirectory(String childWorkspaceId) { - return CONTAINER + "/" + childWorkspaceId; - } /** The child Session's working directory, relative to the storage root. */ - public static String childCwd(String childWorkspaceId, String offset) { - return ".".equals(offset) ? childDirectory(childWorkspaceId) - : childDirectory(childWorkspaceId) + "/" + offset; - } /** * Locates the repository of the parent's working directory and checks @@ -1558,4 +1541,8 @@ public final class ChildWorktreeGit { // Scratch only. } } + @Override + public T runWhileLive(BooleanSupplier live, Supplier work) { + return whileLive(live, work); + } } diff --git a/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/LocalRecoveryWorker.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/LocalRecoveryWorker.java new file mode 100644 index 00000000000..b7ac5126ba2 --- /dev/null +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/LocalRecoveryWorker.java @@ -0,0 +1,84 @@ +package com.alibaba.qwen.code.managedagent.adapters.workspace; + +import com.alibaba.qwen.code.managedagent.contracts.workspace.RecoveryWorker; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.io.BufferedInputStream; +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import static com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.check; +import static com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.text; +public final class LocalRecoveryWorker implements RecoveryWorker { + private static final com.fasterxml.jackson.databind.ObjectMapper JSON = new com.fasterxml.jackson.databind.ObjectMapper(); + + public void execute(JsonNode request, Session store, + java.util.function.BiFunction calls) throws Exception { + var command = new ProcessBuilder(text(request, "nodeExecutable"), text(request, "cliEntry"), + "--workspace-recovery-worker").redirectError(ProcessBuilder.Redirect.INHERIT); + command.environment().keySet().removeIf(key -> key.startsWith("W1_") || key.startsWith("OSS_")); + Process worker; + try { + worker = command.start(); + } catch (java.io.IOException error) { + store.workerFailed(); + throw error; + } + try (var output = new BufferedInputStream(worker.getInputStream()); + var replies = new BufferedWriter(new OutputStreamWriter(worker.getOutputStream(), StandardCharsets.UTF_8))) { + long lastId = 0; + byte[] line; + while ((line = message(output)) != null) { + JsonNode call = com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.parse(line); + long callId = com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.positive(call, "id"); + check(callId == lastId + 1, "worker_protocol_error"); + lastId = callId; + ObjectNode response = JSON.createObjectNode().put("id", callId); + try { + response.set("result", calls.apply(text(call, "method"), call.path("params"))); + } catch (RuntimeException error) { + String code = error instanceof com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.RecoveryFailure failure + ? failure.code : "recovery_read_failed"; + if (!(error instanceof com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.RecoveryFailure)) { + store.workerFailed(); + } + response.putObject("error").put("code", code).put("message", "Workspace recovery: " + code); + } + replies.write(response.toString()); + replies.newLine(); + replies.flush(); + } + check(worker.waitFor(30, TimeUnit.SECONDS) && worker.exitValue() == 0, "worker_failed"); + JsonNode receipt = store.inspect(); + check(Set.of("SEALED", "VERIFIED").contains(receipt.path("state").asText()), "worker_incomplete"); + } catch (Exception error) { + store.workerFailed(); + throw error; + } finally { + if (worker.isAlive()) { + worker.destroyForcibly(); + } + } + } + + private static byte[] message(InputStream input) throws java.io.IOException { + var bytes = new ByteArrayOutputStream(); + int next; + while ((next = input.read()) != -1 && next != '\n') { + check(bytes.size() < 16 * 1024 * 1024, "worker_message_too_large"); + bytes.write(next); + } + check(next != -1 || bytes.size() == 0, "worker_protocol_error"); + return next == -1 ? null : bytes.toByteArray(); + } + + public void validateStateDirectory(java.nio.file.Path directory) throws java.io.IOException { + com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner.validateStateDirectory(directory); + } +} diff --git a/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/LocalWorktreeFiles.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/LocalWorktreeFiles.java new file mode 100644 index 00000000000..96f3b96a9a0 --- /dev/null +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/LocalWorktreeFiles.java @@ -0,0 +1,11 @@ +package com.alibaba.qwen.code.managedagent.adapters.workspace; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles; +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Plan; + +import java.nio.file.Path; +public final class LocalWorktreeFiles implements WorktreeFiles { + public String verify(Plan plan, String savedIdentity) { return SessionWorktreeGit.verify(plan, savedIdentity); } + public void requireRoot(Plan plan) { SessionWorktreeGit.requireRoot(plan); } + public String currentBranch(String verifiedIdentity) { return SessionWorktreeGit.currentBranch(verifiedIdentity); } + public String identity(Path path) { return SessionWorktreeGit.identity(path); } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeGit.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/SessionWorktreeGit.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeGit.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/SessionWorktreeGit.java index 882d06e69b2..688e9e99b41 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeGit.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/adapters/workspace/SessionWorktreeGit.java @@ -1,9 +1,9 @@ -package com.alibaba.qwen.code.managedagent.workspace; +package com.alibaba.qwen.code.managedagent.adapters.workspace; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorkspaceException; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore.Plan; +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Plan; import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.http.HttpStatus; import java.io.IOException; import java.nio.ByteBuffer; @@ -16,7 +16,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.List; import java.util.Map; -public final class SessionWorktreeGit implements AutoCloseable { +public final class SessionWorktreeGit implements com.alibaba.qwen.code.managedagent.contracts.workspace.SessionWorktreeOperations { public static final String CONTAINER = ".qwen-managed-worktrees"; private static final ObjectMapper JSON = new ObjectMapper() .enable(com.fasterxml.jackson.databind.DeserializationFeature.USE_LONG_FOR_INTS); @@ -27,9 +27,6 @@ public final class SessionWorktreeGit implements AutoCloseable { git.requireSupportedVersion(); } - public record Source(String repositoryRelative, String offset, String commonDirectory, - String commonIdentity, String commit, List branches) { } - public Source source(Path root, String cwd, String baseRef) { if (cwd.split("/")[0].equals(CONTAINER)) { throw error("worktree_source_private", "Select the original project directory."); @@ -318,11 +315,12 @@ public final class SessionWorktreeGit implements AutoCloseable { } public static ManagedAgentException error(String code, String message) { - return new ManagedAgentException(HttpStatus.CONFLICT, code, message); + return new ManagedAgentException(409, code, message); } @Override public void close() { git.close(); } + public T runWhileLive(java.util.function.BooleanSupplier live, java.util.function.Supplier work) { return git.runWhileLive(live, work); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessDiscovery.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessDiscovery.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessDiscovery.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessDiscovery.java diff --git a/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessNegotiation.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessNegotiation.java new file mode 100644 index 00000000000..7b273ffe805 --- /dev/null +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessNegotiation.java @@ -0,0 +1,33 @@ +package com.alibaba.qwen.code.managedagent.harness; + +import com.alibaba.qwen.code.daemon.HostedHarnessClient; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessCapabilities; + +import java.net.URI; +import java.time.Duration; +public final class HarnessNegotiation implements AutoCloseable { + private final HostedHarnessClient client; + public HarnessNegotiation(URI uri, String token, String digest) { + this(uri, token, digest, null); + } + public HarnessNegotiation(URI uri, String token, String digest, Duration timeout) { + try { + var builder = HostedHarnessClient.builder().baseUri(uri).bearerToken(token).capabilityDigest(digest); + if (timeout != null) { + builder.connectTimeout(timeout).requestTimeout(timeout); + } + client = builder.build(); + } catch (RuntimeException failure) { + throw HarnessSdkValues.runtime(failure); + } + } + public HarnessCapabilities capabilities() { + try { + var value = client.capabilities(); + return new HarnessCapabilities(value.getCurrentProtocolVersion(), value.getSupportedProtocolVersions(), value.getBootId(), value.getInstanceId(), value.getCapabilityDigest(), value.getLifecycleProtocolVersion(), value.getFeatures(), value.getTitleProtocolVersion(), value.getLifecycleV2Profiles()); + } catch (RuntimeException failure) { + throw HarnessSdkValues.runtime(failure); + } + } + public void close() { client.close(); } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnector.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnector.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnector.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnector.java index aa1fe970e3f..2f5469a9b42 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnector.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnector.java @@ -6,16 +6,16 @@ import com.alibaba.qwen.code.daemon.HostedHarnessClient; import com.alibaba.qwen.code.daemon.HostedHarnessGenerationException; import com.alibaba.qwen.code.daemon.HostedHarnessPlacement; import com.alibaba.qwen.code.daemon.MutationOutcomeUnknownException; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; -import com.alibaba.qwen.code.managedagent.persistence.HarnessPlacementStore; -import com.alibaba.qwen.code.managedagent.persistence.HarnessPlacementStore.Placement; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionSkills; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; +import com.alibaba.qwen.code.managedagent.repository.HarnessPlacementStore; +import com.alibaba.qwen.code.managedagent.repository.HarnessPlacementStore.Placement; +import com.alibaba.qwen.code.managedagent.repository.ManagedSessionSkills; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.fasterxml.jackson.databind.JsonNode; -import org.springframework.http.HttpStatus; import javax.net.ssl.SSLContext; @@ -33,7 +33,7 @@ import java.util.function.Function; /** Sticky routing over independently fenced Hosted process incarnations. */ public class HarnessPoolConnector implements HarnessConnector { - private final ManagedAgentProperties properties; + private final ManagedAgentSettings properties; private final HarnessPlacementStore placements; private final WriterCredentialPolicy credentials; private final HarnessDiscovery discovery; @@ -82,13 +82,13 @@ public class HarnessPoolConnector implements HarnessConnector { private record Selected(Placement placement, Member member, QwenHostedHarnessConnector.PoolCall call) { } - public HarnessPoolConnector(ManagedAgentProperties properties, HarnessPlacementStore placements, + public HarnessPoolConnector(ManagedAgentSettings properties, HarnessPlacementStore placements, HarnessDiscovery discovery, Function connectors, ManagedSessionSkills skills) { this(properties, placements, discovery, connectors, skills, HarnessTls.poolContext(properties.getHarness().getPool()), null); } - HarnessPoolConnector(ManagedAgentProperties properties, HarnessPlacementStore placements, + HarnessPoolConnector(ManagedAgentSettings properties, HarnessPlacementStore placements, HarnessDiscovery discovery, Function connectors, ManagedSessionSkills skills, SSLContext tls, Function clients) { @@ -270,7 +270,7 @@ public class HarnessPoolConnector implements HarnessConnector { : placements.takeoverForLifecycle(operation, placement, candidate.endpoint.instanceId(), candidate.owner().bootId()); } } catch (ManagedAgentException refusal) { - if (!quiescingOwner || !member.reachable || refusal.getStatus() != HttpStatus.CONFLICT + if (!quiescingOwner || !member.reachable || refusal.getStatus() != 409 || !"harness_owner_still_live".equals(refusal.getCode())) { throw refusal; } @@ -294,7 +294,7 @@ public class HarnessPoolConnector implements HarnessConnector { } private static ManagedAgentException unavailable() { - return new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, "harness_pool_unavailable", "No compatible Harness owner is available"); + return new ManagedAgentException(503, "harness_pool_unavailable", "No compatible Harness owner is available"); } private T call(String tenant, String session, Function action) { diff --git a/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessSdkValues.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessSdkValues.java new file mode 100644 index 00000000000..876179216bb --- /dev/null +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessSdkValues.java @@ -0,0 +1,106 @@ +package com.alibaba.qwen.code.managedagent.harness; + +import com.alibaba.qwen.code.managedagent.harness.contracts.*; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.CompletionException; + +final class HarnessSdkValues { + private static final ObjectMapper JSON = new ObjectMapper(); + private HarnessSdkValues() { } + + static HarnessRuntimeRecovery recovery(com.alibaba.qwen.code.daemon.HarnessRuntimeRecovery value) { + return value == null ? null : new HarnessRuntimeRecovery(value.getPhase(), value.getCheckpointId(), + value.getActivationId(), value.getExecutions().stream().map(execution -> + new HarnessRuntimeRecovery.Execution(execution.getFunctionCallId(), execution.getToolName(), + execution.getExecutionCallId(), execution.getRuntimeSessionId(), execution.getProgressCursor(), + execution.getOutcome(), execution.getStatus())).toList(), + value.hasUnknownOutcome(), value.isContinuationReady(), value.isCancellationReady()); + } + + static Throwable failure(Throwable error) { + if (error == null) { + return null; + } + Throwable cause = failure(error.getCause()); + Throwable[] suppressed = java.util.Arrays.stream(error.getSuppressed()) + .map(HarnessSdkValues::failure).toArray(Throwable[]::new); + boolean changed = cause != error.getCause() + || !java.util.Arrays.equals(suppressed, error.getSuppressed()); + Throwable mapped = mapFailure(error, cause); + if (mapped == error && changed) { + mapped = error instanceof CompletionException + ? new CompletionException(error.getMessage(), cause) + : error instanceof java.util.concurrent.ExecutionException + ? new java.util.concurrent.ExecutionException(error.getMessage(), cause) + : new HarnessFailure(error.getMessage(), cause); + } + if (mapped != error) { + mapped.setStackTrace(error.getStackTrace()); + for (Throwable failure : suppressed) { + mapped.addSuppressed(failure); + } + } + return mapped; + } + + private static Throwable mapFailure(Throwable error, Throwable cause) { + if (error instanceof com.alibaba.qwen.code.daemon.DaemonHttpException value) { + return new HarnessHttpFailure(value.getMessage(), cause, value.getStatusCode(), value.getResponseBody(), bodyFields(value)); + } + if (error instanceof com.alibaba.qwen.code.daemon.DaemonProtocolException value) { + return new HarnessProtocolFailure(value.getMessage(), cause); + } + if (error instanceof com.alibaba.qwen.code.daemon.DaemonTransportException value) { + return new HarnessTransportFailure(value.getMessage(), cause); + } + if (error instanceof com.alibaba.qwen.code.daemon.HostedHarnessGenerationException value) { + return new HarnessGenerationFailure(value.getMessage(), cause, value.getExpectedBootId(), value.getActualBootId(), value.getCode()); + } + if (error instanceof com.alibaba.qwen.code.daemon.HarnessSessionRefusedException value) { + return new HarnessSessionRefusedFailure(value.getMessage(), cause, value.getStatusCode(), value.getCode()); + } + if (error instanceof com.alibaba.qwen.code.daemon.HostedHarnessCapabilityMismatchException value) { + return new HarnessCapabilityFailure(value.getMessage(), cause, value.getExpectedDigest(), value.getActualDigest(), value.getCode()); + } + if (error instanceof com.alibaba.qwen.code.daemon.DaemonClientCapacityException value) { + return new HarnessCapacityFailure(value.getMessage(), cause); + } + if (error instanceof com.alibaba.qwen.code.daemon.MutationOutcomeUnknownException value) { + return new HarnessMutationUnknownFailure(value.getMessage(), cause, value.getOperation()); + } + if (error instanceof com.alibaba.qwen.code.daemon.HostedHarnessSettleAnsweredException value) { + return new HarnessSettleAnsweredFailure(value.getMessage(), cause); + } + if (error instanceof com.alibaba.qwen.code.daemon.DaemonException value) { + return new HarnessFailure(value.getMessage(), cause); + } + return error; + } + + static RuntimeException runtime(RuntimeException error) { + return (RuntimeException) failure(error); + } + + private static Map bodyFields(com.alibaba.qwen.code.daemon.DaemonHttpException error) { + Map fields = new LinkedHashMap<>(); + try { + var body = JSON.readTree(error.getResponseBody()); + if (body != null && body.isObject()) { + body.fieldNames() + .forEachRemaining( + name -> { + String value = error.getBodyField(name); + if (value != null) { + fields.put(name, value); + } + }); + } + } catch (java.io.IOException | RuntimeException malformed) { + return Map.of(); + } + return fields; + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessTls.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessTls.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessTls.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessTls.java index 64b259b4a57..427df475866 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessTls.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessTls.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.harness; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; @@ -18,7 +18,7 @@ public final class HarnessTls { private HarnessTls() { } - public static SSLContext poolContext(ManagedAgentProperties.HarnessPool properties) { + public static SSLContext poolContext(ManagedAgentSettings.HarnessPool properties) { if (properties.getKeyStoreFile().isBlank() && properties.getTrustStoreFile().isBlank()) { return null; } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/KubernetesHarnessDiscovery.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/KubernetesHarnessDiscovery.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/KubernetesHarnessDiscovery.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/KubernetesHarnessDiscovery.java index fe63d86233f..e9cc078c8f7 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/KubernetesHarnessDiscovery.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/KubernetesHarnessDiscovery.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.harness; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -29,7 +29,7 @@ import java.util.concurrent.atomic.AtomicReference; /** A bounded namespace-scoped list/watch; uncertainty never creates placement candidates. */ public final class KubernetesHarnessDiscovery implements HarnessDiscovery { private static final int RESPONSE_LIMIT = 2 * 1024 * 1024; - private final ManagedAgentProperties.HarnessPool properties; + private final ManagedAgentSettings.HarnessPool properties; private final ObjectMapper json; private final URI origin; private final HttpClient client; @@ -43,13 +43,13 @@ public final class KubernetesHarnessDiscovery implements HarnessDiscovery { private volatile Snapshot snapshot = new Snapshot(List.of(), false); private volatile boolean closed; - public KubernetesHarnessDiscovery(ManagedAgentProperties.HarnessPool properties, ObjectMapper json) { + public KubernetesHarnessDiscovery(ManagedAgentSettings.HarnessPool properties, ObjectMapper json) { this(properties, json, secureOrigin(properties.getKubernetesApiUrl()), HttpClient.newBuilder().sslContext(HarnessTls.kubernetesContext(Path.of(properties.getKubernetesCaFile()))) .connectTimeout(Duration.ofSeconds(5)).followRedirects(HttpClient.Redirect.NEVER).build()); } - KubernetesHarnessDiscovery(ManagedAgentProperties.HarnessPool properties, ObjectMapper json, URI origin, HttpClient client) { + KubernetesHarnessDiscovery(ManagedAgentSettings.HarnessPool properties, ObjectMapper json, URI origin, HttpClient client) { label(properties.getNamespace()); label(properties.getServiceName()); label(properties.getPortName()); @@ -192,7 +192,7 @@ public final class KubernetesHarnessDiscovery implements HarnessDiscovery { } } - static List endpoints(ManagedAgentProperties.HarnessPool properties, Iterable slices) throws IOException { + static List endpoints(ManagedAgentSettings.HarnessPool properties, Iterable slices) throws IOException { Map result = new LinkedHashMap<>(); for (JsonNode slice : slices) { JsonNode metadata = slice.path("metadata"); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnector.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnector.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnector.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnector.java index c8ba9473946..487488f3c0e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnector.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnector.java @@ -17,19 +17,21 @@ import com.alibaba.qwen.code.daemon.PromptReceipt; import com.alibaba.qwen.code.daemon.SessionCreationOutcomeUnknownException; import com.alibaba.qwen.code.daemon.StreamHarnessEvents; import com.alibaba.qwen.code.daemon.SubmitHarnessTurn; -import com.alibaba.qwen.code.managedagent.catalog.AgentDefinitionExtensions; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.catalog.AgentDefinitionExtensions; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HostedHarnessRecoveryDeclinedException; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.model.StoreModels; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionSkills; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.SshWorkspaceStore; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; +import com.alibaba.qwen.code.managedagent.repository.ManagedActionStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedSessionSkills; +import com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore; +import com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore; +import com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; @@ -48,10 +50,10 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; public class QwenHostedHarnessConnector implements HarnessConnector { - private final ManagedAgentProperties settings; - private final ManagedAgentProperties.Harness properties; - private final ManagedAgentProperties.SessionStore sessionStore; - private final ManagedAgentProperties.ToolPublication toolPublication; + private final ManagedAgentSettings settings; + private final ManagedAgentSettings.Harness properties; + private final ManagedAgentSettings.SessionStore sessionStore; + private final ManagedAgentSettings.ToolPublication toolPublication; private final String workspaceId; private final AgentStateStore sessions; private final WorkspaceExecutionStore workspaceExecution; @@ -64,7 +66,7 @@ public class QwenHostedHarnessConnector implements HarnessConnector { @org.springframework.beans.factory.annotation.Autowired private com.fasterxml.jackson.databind.ObjectMapper extensionJson; @org.springframework.beans.factory.annotation.Autowired - private com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore extensions; + private com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore extensions; private final WriterCredentialPolicy credentials; private final SshWorkspaceStore ssh; /** #13753 I2: this control plane serves child Workspaces (startup checks the shape). */ @@ -88,12 +90,16 @@ public class QwenHostedHarnessConnector implements HarnessConnector { this.client = negotiated; } - public void configureExtensionResources(com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore records, + public void configureExtensionResources(com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore records, com.fasterxml.jackson.databind.ObjectMapper json) { this.extensions = records; this.extensionJson = json; } + public void configureSessionSkills(ManagedSessionSkills skills) { + this.sessionSkills = skills; + } + T withPlacement(PoolCall call, Supplier action) { PoolCall previous = poolCall.get(); poolCall.set(call); @@ -150,27 +156,27 @@ public class QwenHostedHarnessConnector implements HarnessConnector { private final Map attachmentLocks = new ConcurrentHashMap<>(); - public QwenHostedHarnessConnector(ManagedAgentProperties properties, + public QwenHostedHarnessConnector(ManagedAgentSettings properties, AgentStateStore sessions, WorkspaceExecutionStore workspaceExecution) { this(properties, sessions, workspaceExecution, null); } public QwenHostedHarnessConnector( - ManagedAgentProperties properties, + ManagedAgentSettings properties, AgentStateStore sessions, WorkspaceExecutionStore workspaceExecution, ManagedActionStore actions) { this(properties, sessions, workspaceExecution, actions, null, null); } - public QwenHostedHarnessConnector(ManagedAgentProperties properties, + public QwenHostedHarnessConnector(ManagedAgentSettings properties, AgentStateStore sessions, WorkspaceExecutionStore workspaceExecution, ManagedActionStore actions, ManagedAgentDefinitionStore definitions, ManagedSessionStore resources) { this(properties, sessions, workspaceExecution, actions, definitions, resources, null); } - public QwenHostedHarnessConnector(ManagedAgentProperties properties, + public QwenHostedHarnessConnector(ManagedAgentSettings properties, AgentStateStore sessions, WorkspaceExecutionStore workspaceExecution, ManagedActionStore actions, ManagedAgentDefinitionStore definitions, ManagedSessionStore resources, SshWorkspaceStore ssh) { @@ -589,7 +595,7 @@ public class QwenHostedHarnessConnector implements HarnessConnector { throw new IllegalStateException("Hosted Harness generation moved during passive load"); } return new Attachment(attached.getHarnessBootId(), - attached.getRuntimeRecovery(), + HarnessSdkValues.recovery(attached.getRuntimeRecovery()), attached.getHarnessLastEventId(), attached.getHarnessEventEpoch()); } @@ -815,7 +821,7 @@ public class QwenHostedHarnessConnector implements HarnessConnector { var preparedClient = client(); if (!preparedClient.capabilities().getFeatures().contains("context-compression/1")) { throw new com.alibaba.qwen.code.managedagent.exception.ManagedAgentException( - org.springframework.http.HttpStatus.CONFLICT, "unsupported_feature", + 409, "unsupported_feature", "Hosted context compression is unavailable."); } return () -> { @@ -1846,7 +1852,7 @@ public class QwenHostedHarnessConnector implements HarnessConnector { return attached; }); return new Attachment(attached.getHarnessBootId(), - pendingRecovery.contains(key) ? attached.getRuntimeRecovery() + pendingRecovery.contains(key) ? HarnessSdkValues.recovery(attached.getRuntimeRecovery()) : null, attached.getHarnessLastEventId(), attached.getHarnessEventEpoch()); @@ -1932,7 +1938,7 @@ public class QwenHostedHarnessConnector implements HarnessConnector { // handed out again only until its continue or cancel has been // admitted; after that a re-entered Turn just resumes its stream. return new Attachment(cached.getHarnessBootId(), - pendingRecovery.contains(key) ? cached.getRuntimeRecovery() + pendingRecovery.contains(key) ? HarnessSdkValues.recovery(cached.getRuntimeRecovery()) : null, cached.getHarnessLastEventId(), cached.getHarnessEventEpoch()); diff --git a/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/SdkHarnessBoundary.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/SdkHarnessBoundary.java new file mode 100644 index 00000000000..8d08c46f808 --- /dev/null +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/SdkHarnessBoundary.java @@ -0,0 +1,422 @@ +package com.alibaba.qwen.code.managedagent.harness; + +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; +import com.fasterxml.jackson.databind.JsonNode; + +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +/** Converts SDK failures at the adapter boundary, including deferred stream reads. */ +public final class SdkHarnessBoundary implements HarnessConnector { + private final HarnessConnector delegate; + + public SdkHarnessBoundary(HarnessConnector delegate) { + this.delegate = delegate; + } + + @Override + public boolean isAvailable() { + return call(delegate::isAvailable); + } + + @Override + public void recoverForWake(String tenantId, String sessionId) { + run(() -> delegate.recoverForWake(tenantId, sessionId)); + } + + @Override + public boolean supportsPublicChildAgents() { + return call(delegate::supportsPublicChildAgents); + } + + @Override + public String closeSession(OperationRecord operation) { + return call(() -> delegate.closeSession(operation)); + } + + @Override + public void runLifecycleChildOperation(OperationRecord operation, Map body) { + run(() -> delegate.runLifecycleChildOperation(operation, body)); + } + + @Override + public Map drainInstance(String instanceId, String operationId) { + return call(() -> delegate.drainInstance(instanceId, operationId)); + } + + @Override + public Map releaseOwnership(String tenantId, String sessionId, + String operationId, long expectedPlacementEpoch) { + return call(() -> delegate.releaseOwnership(tenantId, sessionId, operationId, expectedPlacementEpoch)); + } + + @Override + public boolean supportsApprovalSelection() { + return call(delegate::supportsApprovalSelection); + } + + @Override + public boolean supportsLiveApprovalSelection() { + return call(delegate::supportsLiveApprovalSelection); + } + + @Override + public boolean supportsAutoApproval() { + return call(delegate::supportsAutoApproval); + } + + @Override + public boolean supportsPermissionRules() { + return call(delegate::supportsPermissionRules); + } + + @Override + public boolean supportsAgentDefinitionApprovalSelection() { + return call(delegate::supportsAgentDefinitionApprovalSelection); + } + + @Override + public boolean supportsModelSelection() { + return call(delegate::supportsModelSelection); + } + + @Override + public boolean supportsUserMessageQueue() { + return call(delegate::supportsUserMessageQueue); + } + + @Override + public boolean supportsContextCompression() { + return call(delegate::supportsContextCompression); + } + + @Override + public ContextCompressionSubmission prepareContextCompression(String tenantId, String sessionId, + Map body) { + return call(() -> { + var prepared = delegate.prepareContextCompression(tenantId, sessionId, body); + return () -> call(prepared::submit); + }); + } + + @Override + public Map runContextCompression(String tenantId, String sessionId, + Map body) { + return call(() -> delegate.runContextCompression(tenantId, sessionId, body)); + } + + @Override + public Map readContextCompression(String tenantId, String sessionId, + String operationId) { + return call(() -> delegate.readContextCompression(tenantId, sessionId, operationId)); + } + + @Override + public boolean supportsPlanMode() { + return call(delegate::supportsPlanMode); + } + + @Override + public boolean supportsInputAttachments() { + return call(delegate::supportsInputAttachments); + } + + @Override + public boolean supportsSessionSkills() { + return call(delegate::supportsSessionSkills); + } + + @Override + public boolean supportsMcpManagement() { + return call(delegate::supportsMcpManagement); + } + + @Override + public Map mcpManagement(String tenantId, String sessionId) { + return call(() -> delegate.mcpManagement(tenantId, sessionId)); + } + + @Override + public Map manageMcp(String tenantId, String sessionId, Map command) { + return call(() -> delegate.manageMcp(tenantId, sessionId, command)); + } + + @Override + public Map mcpManagementOperation(String tenantId, String sessionId, String operationId) { + return call(() -> delegate.mcpManagementOperation(tenantId, sessionId, operationId)); + } + + @Override + public boolean supportsSessionExtensions() { + return call(delegate::supportsSessionExtensions); + } + + @Override + public Map applyExtensions(String tenantId, String sessionId, Map command) { + return call(() -> delegate.applyExtensions(tenantId, sessionId, command)); + } + + @Override + public Map continueExtensionOperation(String tenantId, String sessionId, String operationId, boolean recoverPrevious) { + return call(() -> delegate.continueExtensionOperation(tenantId, sessionId, operationId, recoverPrevious)); + } + + @Override + public boolean isWorkspaceFilesAvailable() { + return call(delegate::isWorkspaceFilesAvailable); + } + + @Override + public boolean supportsLifecycle() { + return call(delegate::supportsLifecycle); + } + + @Override + public boolean supportsLifecycle(String profile) { + return call(() -> delegate.supportsLifecycle(profile)); + } + + @Override + public void prepareLifecycleResources(OperationRecord operation) { + run(() -> delegate.prepareLifecycleResources(operation)); + } + + @Override + public void settleLifecycleResources(OperationRecord operation) { + run(() -> delegate.settleLifecycleResources(operation)); + } + + @Override + public boolean supportsFencedTitles() { + return call(delegate::supportsFencedTitles); + } + + @Override + public JsonNode settleLifecycle(OperationRecord operation) { + return call(() -> delegate.settleLifecycle(operation)); + } + + @Override + public void detachLifecycle(OperationRecord operation) { + run(() -> delegate.detachLifecycle(operation)); + } + + @Override + public Attachment createOrLoad(String tenantId, String sessionId, + boolean loadExisting) { + return call(() -> delegate.createOrLoad(tenantId, sessionId, loadExisting)); + } + + @Override + public Attachment createOrLoad(String tenantId, String sessionId, + boolean loadExisting, boolean passiveManagedRuntimeRecovery) { + return call(() -> delegate.createOrLoad(tenantId, sessionId, loadExisting, passiveManagedRuntimeRecovery)); + } + + @Override + public Attachment recoverManagedRuntime(String tenantId, String sessionId, + boolean cancellation) { + return call(() -> delegate.recoverManagedRuntime(tenantId, sessionId, cancellation)); + } + + @Override + public Attachment recoverManagedRuntime(String tenantId, String sessionId, + boolean cancellation, String promptId) { + return call(() -> delegate.recoverManagedRuntime(tenantId, sessionId, cancellation, promptId)); + } + + @Override + public Attachment recoverManagedCancellation(String tenantId, + String sessionId) { + return call(() -> delegate.recoverManagedCancellation(tenantId, sessionId)); + } + + @Override + public Attachment recoverManagedCancellation(String tenantId, + String sessionId, String promptId) { + return call(() -> delegate.recoverManagedCancellation(tenantId, sessionId, promptId)); + } + + @Override + public Attachment recoverManagedFailureCancellation(String tenantId, + String sessionId, String turnId, String dispatchOwner) { + return call(() -> delegate.recoverManagedFailureCancellation(tenantId, sessionId, turnId, dispatchOwner)); + } + + @Override + public Attachment recoverManagedFailureCancellation(String tenantId, + String sessionId, String turnId, String dispatchOwner, String promptId) { + return call(() -> delegate.recoverManagedFailureCancellation(tenantId, sessionId, turnId, dispatchOwner, promptId)); + } + + @Override + public void discardManagedRecovery(String tenantId, String sessionId) { + run(() -> delegate.discardManagedRecovery(tenantId, sessionId)); + } + + @Override + public RecoveryStatus inspectRecovery(String tenantId, String sessionId, String promptId) { + return call(() -> delegate.inspectRecovery(tenantId, sessionId, promptId)); + } + + @Override + public Attachment cachedRecoveryAttachment(String tenantId, String sessionId) { + return call(() -> delegate.cachedRecoveryAttachment(tenantId, sessionId)); + } + + @Override + public boolean recheckRecoveryCleanup(String tenantId, String sessionId, String promptId) { + return call(() -> delegate.recheckRecoveryCleanup(tenantId, sessionId, promptId)); + } + + @Override + public Admission submit(String tenantId, String sessionId, String promptId, + List> input, String payloadDigest) { + return call(() -> delegate.submit(tenantId, sessionId, promptId, input, payloadDigest)); + } + + @Override + public Admission continueManagedRuntime(String tenantId, + String sessionId, String promptId, String checkpointId, + String activationId) { + return call(() -> delegate.continueManagedRuntime(tenantId, sessionId, promptId, checkpointId, activationId)); + } + + @Override + public Admission cancelManagedRuntime(String tenantId, String sessionId, + String promptId, String checkpointId, String activationId) { + return call(() -> delegate.cancelManagedRuntime(tenantId, sessionId, promptId, checkpointId, activationId)); + } + + @Override + public SourceStream stream(String tenantId, String sessionId, long lastEventId, + String eventEpoch) { + return call(() -> streamBoundary(delegate.stream(tenantId, sessionId, lastEventId, eventEpoch))); + } + + @Override + public SourceStream observeStream(String tenantId, String sessionId, String bootId, + long lastEventId, String eventEpoch) { + return call(() -> streamBoundary(delegate.observeStream(tenantId, sessionId, bootId, lastEventId, eventEpoch))); + } + + @Override + public void resolveAction( + String tenantId, + String sessionId, + String actionId, + JsonNode response) { + run(() -> delegate.resolveAction(tenantId, sessionId, actionId, response)); + } + + @Override + public void cancel(String tenantId, String sessionId) { + run(() -> delegate.cancel(tenantId, sessionId)); + } + + @Override + public void cancelOriginalTurn(String tenantId, String sessionId, String promptId) { + run(() -> delegate.cancelOriginalTurn(tenantId, sessionId, promptId)); + } + + @Override + public Map runAutomationOperation(String tenantId, + String sessionId, Map body) { + return call(() -> delegate.runAutomationOperation(tenantId, sessionId, body)); + } + + @Override + public void runChildOperation(String tenantId, String sessionId, + Map body) { + run(() -> delegate.runChildOperation(tenantId, sessionId, body)); + } + + @Override + public Map runMessageOperation(String tenantId, + String sessionId, Map body) { + return call(() -> delegate.runMessageOperation(tenantId, sessionId, body)); + } + + @Override + public Map runChannelOperation(String tenantId, + String sessionId, Map body) { + return call(() -> delegate.runChannelOperation(tenantId, sessionId, body)); + } + + @Override + public void rename(String tenantId, String sessionId, String title) { + run(() -> delegate.rename(tenantId, sessionId, title)); + } + + @Override + public void rename(String tenantId, String sessionId, String title, long revision) { + run(() -> delegate.rename(tenantId, sessionId, title, revision)); + } + + @Override + public String closeSession(String tenantId, String sessionId) { + return call(() -> delegate.closeSession(tenantId, sessionId)); + } + + @Override + public List attachedSessions() { + return call(delegate::attachedSessions); + } + + @Override + public String writerIdentity() { + return call(delegate::writerIdentity); + } + + @Override + public boolean activateForWake(String tenantId, String sessionId) { + return call(() -> delegate.activateForWake(tenantId, sessionId)); + } + + @Override + public void close() { + run(delegate::close); + } + + private static T call(Supplier operation) { + try { + return operation.get(); + } catch (RuntimeException error) { + throw HarnessSdkValues.runtime(error); + } + } + + private static void run(Runnable operation) { + try { + operation.run(); + } catch (RuntimeException error) { + throw HarnessSdkValues.runtime(error); + } + } + + private static SourceStream streamBoundary(SourceStream stream) { + return new SourceStream() { + @Override + public String eventEpoch() { + return call(stream::eventEpoch); + } + + @Override + public SourceEvent next() { + return call(stream::next); + } + + @Override + public boolean recoveryBlocked(long afterEventId) { + return call(() -> stream.recoveryBlocked(afterEventId)); + } + + @Override + public void close() { + run(stream::close); + } + }; + } +} diff --git a/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/SdkHarnessRules.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/SdkHarnessRules.java new file mode 100644 index 00000000000..5375ae5608b --- /dev/null +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/SdkHarnessRules.java @@ -0,0 +1,25 @@ +package com.alibaba.qwen.code.managedagent.harness; + +import com.alibaba.qwen.code.daemon.HostedPermissionRules; +import com.alibaba.qwen.code.daemon.SubmitHarnessTurn; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRules; + +import java.util.List; +import java.util.Map; + +public final class SdkHarnessRules implements HarnessRules { + @Override + public String payloadDigest(List> input) { + return SubmitHarnessTurn.computePayloadDigest(input); + } + + @Override + public Map> permissionRules(Map rules) { + return HostedPermissionRules.copyOf(rules); + } + + @Override + public Map> nonEmptyPermissionRules(Map> rules) { + return HostedPermissionRules.nonEmpty(rules); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/UnavailableHarnessConnector.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/UnavailableHarnessConnector.java similarity index 90% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/UnavailableHarnessConnector.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/UnavailableHarnessConnector.java index 964ec5c6a11..adc119b3b39 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/UnavailableHarnessConnector.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/managedagent/harness/UnavailableHarnessConnector.java @@ -1,5 +1,8 @@ package com.alibaba.qwen.code.managedagent.harness; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessDisabledException; + import java.util.List; import java.util.Map; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/HttpRuntimeTransport.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/adapter/HttpRuntimeTransport.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/HttpRuntimeTransport.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/adapter/HttpRuntimeTransport.java index 5768d5ac307..a3f815d58ce 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/HttpRuntimeTransport.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/adapter/HttpRuntimeTransport.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.transport; +package com.alibaba.qwen.code.runtimebroker.adapter; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONReader; @@ -18,11 +18,21 @@ import com.alibaba.qwen.code.runtimebroker.RuntimePublicationGrant; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; -import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; -import com.alibaba.qwen.code.runtimebroker.provisioning.ProviderRuntimeProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ProviderRuntimeProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.CsiFileFinalizeProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.CsiFileHistoryProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.CsiNativeReadbackProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiFilesProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.ManagedExtensionCommandProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.ManagedExtensionProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.ManagedHookProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.ManagedMcpProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.ManagedMonitorProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.ManagedShellProtocol; import java.io.EOFException; import java.math.BigDecimal; @@ -54,7 +64,7 @@ import java.util.function.Function; * their separate Session control protocol. MCP controls have their own * bounded envelope. */ -public final class HttpRuntimeTransport implements RuntimeTransport { +public final class HttpRuntimeTransport implements com.alibaba.qwen.code.runtimebroker.WorkspacePhysicalTransport { static final int BODY_LIMIT_BYTES = 16 * 1024; static final int TOOL_REQUEST_LIMIT_BYTES = 256 * 1024; static final int TOOL_RESULT_LIMIT_BYTES = 1024 * 1024; @@ -126,12 +136,6 @@ public final class HttpRuntimeTransport implements RuntimeTransport { managedExtensionCacheDirectory, managedExtensionCgroupRoot) : null); } - @FunctionalInterface - public interface ExtensionPreparation { - CompletionStage> prepare(RuntimeBindingRecord runtime, String harnessSessionId, - Map operation); - } - public void configureExtensionPreparation(ExtensionPreparation preparation) { this.extensionPreparation = java.util.Objects.requireNonNull(preparation, "preparation"); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/security/AesGcmSecretProtector.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/adapter/security/AesGcmSecretProtector.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/security/AesGcmSecretProtector.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/adapter/security/AesGcmSecretProtector.java index 3d2337520c0..3109b48780a 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/security/AesGcmSecretProtector.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/adapter/security/AesGcmSecretProtector.java @@ -1,8 +1,10 @@ -package com.alibaba.qwen.code.runtimebroker.security; +package com.alibaba.qwen.code.runtimebroker.adapter.security; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONWriter; import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.security.ProtectedSecret; +import com.alibaba.qwen.code.runtimebroker.security.SecretProtector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalHttpRuntimeClient.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalHttpRuntimeClient.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalHttpRuntimeClient.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalHttpRuntimeClient.java index ee12bdaf42b..2fa9bf3c819 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalHttpRuntimeClient.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalHttpRuntimeClient.java @@ -2,7 +2,8 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.spi.ExternalRuntimeClient; import java.net.URI; import java.net.http.HttpClient; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisioner.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisioner.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisioner.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisioner.java index fb313bffb2d..4580f34c9b6 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisioner.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisioner.java @@ -11,8 +11,8 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; - +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.spi.ExternalRuntimeClient; import org.apache.commons.codec.digest.DigestUtils; import java.net.URI; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisioner.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisioner.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisioner.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisioner.java index d850e559e72..321d219b5d2 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisioner.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisioner.java @@ -7,6 +7,9 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.spi.ExternalRuntimeClient; +import com.alibaba.qwen.code.runtimebroker.spi.ExternalStorageRegistry; import java.time.Duration; import java.util.LinkedHashMap; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReader.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReader.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReader.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReader.java index 275238b833d..db41a5ff32f 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReader.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReader.java @@ -2,6 +2,7 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.spi.KubernetesRuntimeClient; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; @@ -23,8 +24,8 @@ public final class KubernetesCsiLogReader { public record Source(String podName, String podUid, String nodeName, String nodeUid, String daemonSetUid, String containerId, String imageId) { public Source { - KubernetesHttpRuntimeClient.dnsSubdomain(podName); - KubernetesHttpRuntimeClient.dnsSubdomain(nodeName); + com.alibaba.qwen.code.runtimebroker.protocol.KubernetesNames.dnsSubdomain(podName); + com.alibaba.qwen.code.runtimebroker.protocol.KubernetesNames.dnsSubdomain(nodeName); if (!podName.startsWith("csi-plugin-") || !uuid(podUid) || !uuid(nodeUid) || !uuid(daemonSetUid) || containerId == null || !containerId.matches("containerd://[0-9a-f]{64}") || imageId == null || imageId.length() > 512 || !imageId.matches("[^\\s]*sha256:[0-9a-f]{64}")) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClient.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClient.java similarity index 89% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClient.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClient.java index c56bab2cba3..43067fac324 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClient.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClient.java @@ -4,7 +4,7 @@ import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; @@ -24,17 +24,17 @@ import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.cert.CertificateFactory; import java.time.Duration; -import java.util.Map; -import java.util.List; import java.util.ArrayList; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.TimeUnit; /** Bounded core-v1 API calls with rotating service-account tokens and explicit CA trust. */ -public final class KubernetesHttpRuntimeClient implements KubernetesRuntimeClient { +public final class KubernetesHttpRuntimeClient implements com.alibaba.qwen.code.runtimebroker.spi.KubernetesMaintenanceClient { private static final int RESPONSE_LIMIT = 1024 * 1024; private static final int CONFIG_MAP_RESPONSE_LIMIT = RESPONSE_LIMIT + 64 * 1024; private static final int DELETE_REQUEST_LIMIT = 16 * 1024; @@ -188,8 +188,6 @@ public final class KubernetesHttpRuntimeClient implements KubernetesRuntimeClien return result; } - public record DeleteResponse(int status, byte[] body, boolean complete) { } - @Override public CompletionStage readCsiPodLog(String podName) { dnsSubdomain(podName); @@ -335,4 +333,20 @@ public final class KubernetesHttpRuntimeClient implements KubernetesRuntimeClien return new RuntimeBrokerException(status >= 400 && status <= 599 ? status : 502, "runtime_kubernetes_api_failed", "Kubernetes API request failed.", retryable); } + public java.util.concurrent.CompletionStage verifyProtection( + com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration registration, + com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity protection) { + return new com.alibaba.qwen.code.managedagent.adapters.runtime.WorkspaceCsiResourceGuard( + this, registration.clusterDomain(), registration, protection).verify().thenApply(receipt -> null); + } + + public com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentReclamation.Baseline reclamationBaseline( + com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord original, + com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker.ResidentWorkspace selected, + String finalizer, java.time.Instant now) { return com.alibaba.qwen.code.managedagent.adapters.runtime.ResidentWorkspaceReclamation.prepare(original, selected, finalizer, this, now); } + public com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentReclamation.Receipt reclamationReceipt( + com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord original, + com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker.ResidentWorkspace selected, + com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentReclamation.Baseline baseline, + Map pod, java.time.Instant now) { return com.alibaba.qwen.code.managedagent.adapters.runtime.ResidentWorkspaceReclamation.complete(original, selected, baseline, pod, this, now); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisioner.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisioner.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisioner.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisioner.java index f6863c7291e..c04928e7555 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisioner.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisioner.java @@ -11,7 +11,8 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.spi.KubernetesRuntimeClient; import java.net.InetAddress; import java.net.URI; @@ -65,7 +66,7 @@ public final class KubernetesRuntimeProvisioner implements RuntimeProvisioner { || startupTimeout == null || startupTimeout.isNegative() || startupTimeout.isZero()) { throw new IllegalArgumentException("Client, pinned image, worker command and positive timeout are required"); } - KubernetesHttpRuntimeClient.dnsLabel(namespace); + com.alibaba.qwen.code.runtimebroker.protocol.KubernetesNames.dnsLabel(namespace); this.client = client; this.cluster = BrokerValues.requireId(cluster, "cluster"); this.namespace = namespace; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisioner.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisioner.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisioner.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisioner.java index 5f185b7f398..d37c1749e84 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisioner.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisioner.java @@ -17,7 +17,8 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeRecoveryEvidence; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import java.io.BufferedReader; import java.io.IOException; @@ -587,7 +588,7 @@ public final class LocalProcessRuntimeProvisioner // A closed provisioner is not a managed-context startup // failure, and recovery is not blocked by it: keep the // refusal's own message and retryable flag for callers that - // provision directly. Through RuntimeBrokerService a + // provision directly. Through RuntimeBrokerOperations a // managed-context provision failure blocks recovery either // way, so this changes the surfaced error, not that outcome. throw closedRefusal; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStore.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStore.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStore.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStore.java index a6eb55e8427..57cdaad4f44 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStore.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStore.java @@ -229,7 +229,7 @@ final class LocalRuntimeStore { } static boolean supported(RuntimeResourceHandle handle) { - return handle != null && LocalProcessRuntimeProvisioner.KIND.equals(handle.getKind()) + return handle != null && com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(handle.getKind()) && handle.getVersion() == 2; } @@ -350,7 +350,7 @@ final class LocalRuntimeStore { || !Objects.equals(value.get("version"), 1)) { throw blocked(); } - return new Registration(RuntimeResourceHandle.fromJson(LocalProcessRuntimeProvisioner.KIND, 2, + return new Registration(RuntimeResourceHandle.fromJson(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, 2, (String) value.get("handle")), State.valueOf((String) value.get("state")), ((Number) value.get("pid")).longValue(), "".equals(value.get("started")) ? null : (String) value.get("started"), @@ -440,7 +440,7 @@ final class LocalRuntimeStore { scope.getTenantId(), scope.getWorkspaceId(), scope.getWorkspaceGeneration(), scope.getCanonicalCwd(), scope.getCapabilityDigest(), scope.getIsolationClass(), request.getProvisionerKind(), Objects.toString(request.getIsolationKey(), ""), Objects.toString(request.getStorageId(), ""))))); - return new RuntimeResourceHandle(LocalProcessRuntimeProvisioner.KIND, 2, values); + return new RuntimeResourceHandle(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, 2, values); } private static String digest(byte[] bytes) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/MacOsHostIdentity.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/MacOsHostIdentity.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/MacOsHostIdentity.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/MacOsHostIdentity.java diff --git a/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/NativeStorageIdentityReader.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/NativeStorageIdentityReader.java new file mode 100644 index 00000000000..01d05be3630 --- /dev/null +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/NativeStorageIdentityReader.java @@ -0,0 +1,120 @@ +package com.alibaba.qwen.code.runtimebroker.provisioning; + +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader.Identity; +import org.apache.commons.codec.digest.HmacAlgorithms; +import org.apache.commons.codec.digest.HmacUtils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.security.GeneralSecurityException; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.ResolverStyle; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.TimeUnit; +public final class NativeStorageIdentityReader implements StorageIdentityReader { + private static final DateTimeFormatter LINUX_BIRTH_TIME_FORMAT = DateTimeFormatter + .ofPattern("uuuu-MM-dd HH:mm:ss.SSSSSSSSS xx", Locale.ROOT) + .withResolverStyle(ResolverStyle.STRICT); + public Identity read(Path root) throws IOException { + String os = System.getProperty("os.name"); + if (!List.of("Linux", "Mac OS X").contains(os)) { + throw new IOException("Unsupported mount identity provider"); + } + Map attributes = Files.readAttributes(root, + "unix:isDirectory,dev,ino,creationTime,lastModifiedTime", LinkOption.NOFOLLOW_LINKS); + if (!Boolean.TRUE.equals(attributes.get("isDirectory")) || !root.isAbsolute() + || !root.equals(root.toRealPath())) { + throw new IOException("Workspace root is not canonical"); + } + String device = Long.toUnsignedString(((Number) attributes.get("dev")).longValue()); + String inode = Long.toUnsignedString(((Number) attributes.get("ino")).longValue()); + String birthTime = "Mac OS X".equals(os) ? MacOsHostIdentity.birthTime(root, device, inode) + : linuxBirthTime(root, device, inode, (FileTime) attributes.get("creationTime"), + (FileTime) attributes.get("lastModifiedTime")); + String machineId = "Mac OS X".equals(os) ? MacOsHostIdentity.machineId() + : Files.readString(Path.of("/etc/machine-id")).strip(); + if (machineId.isEmpty() || machineId.length() > 256) { + throw new IOException("Host identity is unavailable"); + } + String host; + try { + host = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, + machineId.getBytes(StandardCharsets.UTF_8)).hmacHex( + "Qwen-Code/verified-workspace/v2".getBytes(StandardCharsets.UTF_8)); + } catch (IllegalArgumentException error) { + if (error.getCause() instanceof GeneralSecurityException cause) { + throw new IOException("Host identity is unavailable", cause); + } + throw error; + } + return new Identity(root.toString(), host, device, inode, birthTime); + } + + private static String linuxBirthTime(Path root, String device, String inode, + FileTime creation, FileTime modified) throws IOException { + if (!creation.equals(modified) || creation.compareTo(FileTime.fromMillis(0)) <= 0) { + return verifiedBirthTime(creation, modified); + } + // A marker write can share the birth clock tick. Confirm the exact root, + // rather than treating the JDK's possible mtime fallback as birth evidence. + ProcessBuilder builder = new ProcessBuilder("/usr/bin/stat", "-c", "%d|%i|%w", "--", root.toString()) + .redirectError(ProcessBuilder.Redirect.DISCARD); + builder.environment().put("LC_ALL", "C"); + builder.environment().put("TZ", "UTC"); + Process process = builder.start(); + try { + process.getOutputStream().close(); + if (!process.waitFor(5, TimeUnit.SECONDS) || process.exitValue() != 0) { + throw new IOException("Workspace birth time probe failed"); + } + byte[] output = process.getInputStream().readNBytes(257); + if (output.length > 256) { + throw new IOException("Workspace birth time probe exceeds its size limit"); + } + return verifiedNativeBirthTime(new String(output, StandardCharsets.UTF_8).strip(), + device, inode, creation); + } catch (InterruptedException error) { + Thread.currentThread().interrupt(); + throw new IOException("Workspace birth time probe interrupted", error); + } finally { + process.destroyForcibly(); + process.getInputStream().close(); + process.getErrorStream().close(); + } + } + + public static String verifiedNativeBirthTime(String output, String device, String inode, + FileTime creation) throws IOException { + String[] fields = output.split("\\|", -1); + if (fields.length != 3 || !device.equals(fields[0]) || !inode.equals(fields[1])) { + throw new IOException("Workspace storage identity changed or is unavailable"); + } + try { + Instant birth = OffsetDateTime.parse(fields[2], LINUX_BIRTH_TIME_FORMAT).toInstant(); + if (!birth.isAfter(Instant.EPOCH) || !birth.equals(creation.toInstant())) { + throw new IOException("Workspace birth time is unavailable or ambiguous"); + } + return creation.toInstant().toString(); + } catch (DateTimeException error) { + throw new IOException("Workspace native birth time is invalid", error); + } + } + + public static String verifiedBirthTime(FileTime creation, FileTime modified) throws IOException { + // OpenJDK 21 substitutes mtime or epoch when Linux does not provide birth time. + if (creation.compareTo(FileTime.fromMillis(0)) <= 0 || creation.equals(modified)) { + throw new IOException("Workspace birth time is unavailable or ambiguous"); + } + return creation.toInstant().toString(); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/RoutingRuntimeProvisioner.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/RoutingRuntimeProvisioner.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/RoutingRuntimeProvisioner.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/RoutingRuntimeProvisioner.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManager.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManager.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManager.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManager.java index 84a107101c7..d0a70abce82 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManager.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManager.java @@ -4,6 +4,7 @@ import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeNode; import java.io.IOException; import java.net.ConnectException; @@ -38,7 +39,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; /** Owns SSH control sockets and ephemeral forwarding addresses, never remote workers. */ -public final class SshConnectionManager implements AutoCloseable { +public final class SshConnectionManager implements com.alibaba.qwen.code.runtimebroker.spi.SshConnections { private static final int RECORD_LIMIT = 64 * 1024; private static final Duration COMMAND_TIMEOUT = Duration.ofSeconds(60); private final URI publicationEndpoint; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisioner.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisioner.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisioner.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisioner.java index 30d24c6fad7..40e69773019 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisioner.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisioner.java @@ -12,7 +12,11 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.spi.SshConnections; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeNode; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeStorage; import java.net.URI; import java.nio.charset.StandardCharsets; @@ -43,7 +47,7 @@ public final class SshRuntimeProvisioner implements RuntimeProvisioner { "provisionRequestId", "runtimeInstanceId", "runtimeIncarnation", "leaseId", "epoch", "retirementId", "retiredAt"); private final Function nodes; private final Function storages; - private final SshConnectionManager connections; + private final SshConnections connections; private final HttpRuntimeTransport transport; private final ExecutorService executor = new ThreadPoolExecutor(4, 4, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(64), Thread.ofPlatform().daemon().name("runtime-ssh-provisioner-", 0).factory()); @@ -53,7 +57,7 @@ public final class SshRuntimeProvisioner implements RuntimeProvisioner { private String managedExtensionCgroupRoot; public SshRuntimeProvisioner(Function nodes, - Function storages, SshConnectionManager connections, + Function storages, SshConnections connections, HttpRuntimeTransport transport) { this.nodes = java.util.Objects.requireNonNull(nodes, "nodes"); this.storages = java.util.Objects.requireNonNull(storages, "storages"); @@ -257,7 +261,6 @@ public final class SshRuntimeProvisioner implements RuntimeProvisioner { // Let queued control futures run their closed checks instead of abandoning them. executor.shutdown(); owned.clear(); - connections.close(); } private CompletionStage submit(Supplier operation) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/StaticRuntimeProvisioner.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/StaticRuntimeProvisioner.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/StaticRuntimeProvisioner.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/StaticRuntimeProvisioner.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkspaceFileProcess.java b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkspaceFileProcess.java similarity index 90% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkspaceFileProcess.java rename to packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkspaceFileProcess.java index 53c4e4915b4..f9da8767e90 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkspaceFileProcess.java +++ b/packages/managed-agent-server/adapters/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkspaceFileProcess.java @@ -9,10 +9,10 @@ import java.util.List; import java.util.Map; /** Bounded Workspace file exchanges using the configured local CLI. */ -public final class WorkspaceFileProcess { +public final class WorkspaceFileProcess implements com.alibaba.qwen.code.managedagent.contracts.workspace.LocalWorkspaceFiles { public static final int MAX_UPLOAD_BYTES = 8 * 1024 * 1024; public static final int MAX_UPLOAD_REQUEST_BYTES = 12 * 1024 * 1024; - private WorkspaceFileProcess() { + public WorkspaceFileProcess() { } public static Map read(String node, String entry, Map request) { @@ -86,4 +86,6 @@ public final class WorkspaceFileProcess { throw unsupported(); } } + public Map readFiles(String node, String entry, Map request) { return read(node, entry, request); } + public Map uploadFiles(String node, String entry, Map request) { return upload(node, entry, request); } } diff --git a/packages/managed-agent-server/contracts/pom.xml b/packages/managed-agent-server/contracts/pom.xml new file mode 100644 index 00000000000..f2fcaa98841 --- /dev/null +++ b/packages/managed-agent-server/contracts/pom.xml @@ -0,0 +1,79 @@ + + 4.0.0 + + com.alibaba + qwen-managed-agent-parent + 0.1.0-alpha + ../pom.xml + + qwen-managed-agent-contracts + Qwen Managed Agent Contracts + + ${project.basedir}/.. + + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + jakarta.validation + jakarta.validation-api + + + org.apache.commons + commons-lang3 + + + commons-codec + commons-codec + + + com.alibaba.fastjson2 + fastjson2 + + + com.networknt + json-schema-validator + + + + + + src/main/resources + + + ${managed.module.root}/../core/src/managed-runtime/contracts + + managed-tool-result-v1.schema.json + + contracts + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + com.github.spotbugs + spotbugs-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ArtifactReadLease.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ArtifactReadLease.java new file mode 100644 index 00000000000..e91e350e3d5 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ArtifactReadLease.java @@ -0,0 +1,13 @@ +package com.alibaba.qwen.code.managedagent.contracts.artifact; + +import com.fasterxml.jackson.databind.JsonNode; + +public interface ArtifactReadLease extends AutoCloseable { + public void requireScope(JsonNode key); + public void requireScope(String tenant, String session); + public void check(); + public void checkReadView(); + + @Override + public void close(); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ManagedArtifactPolicy.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ManagedArtifactPolicy.java new file mode 100644 index 00000000000..d2c83ffbf44 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ManagedArtifactPolicy.java @@ -0,0 +1,15 @@ +package com.alibaba.qwen.code.managedagent.contracts.artifact; + +public interface ManagedArtifactPolicy { + /** A stable configuration fingerprint that changes whenever a policy decision changes. */ + public String version(); + + public boolean publishOriginal(String tenantId, String workspaceId, + String sessionId); + + public boolean publishPreview(String tenantId, String workspaceId, + String sessionId); + + public boolean readOriginal(String tenantId, String actorId, String workspaceId, + String sessionId); +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ToolPublicationObjectStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ToolPublicationObjectStore.java similarity index 76% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ToolPublicationObjectStore.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ToolPublicationObjectStore.java index 31bac5d17a6..cca6f4b1f27 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ToolPublicationObjectStore.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ToolPublicationObjectStore.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.artifact; +package com.alibaba.qwen.code.managedagent.contracts.artifact; import java.io.InputStream; @@ -12,22 +12,22 @@ public interface ToolPublicationObjectStore { * PUT attempt. An existing key must never be overwritten. Once the complete object is readable, * this invocation must not create it again, including after a later deletion. */ - void putIfAbsent(String key, byte[] bytes); + public void putIfAbsent(String key, byte[] bytes); /** The caller closes the stream after verifying its complete contents. */ - InputStream open(String key); + public InputStream open(String key); /** Check authorization before each new storage request, including implementation retries. */ - default InputStream open(String key, Runnable guard) { + public default InputStream open(String key, Runnable guard) { guard.run(); return open(key); } /** Physically delete the exact owned key; an absent object is already deleted. */ - default void deleteIfPresent(String key) { + public default void deleteIfPresent(String key) { throw new UnsupportedOperationException("Output deletion is not supported by this adapter"); } /** Validate private, durable, immutable storage and exact-key deletion guarantees. */ - void validateConfiguration(); + public void validateConfiguration(); } diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ToolPublicationRetention.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ToolPublicationRetention.java new file mode 100644 index 00000000000..d6b5be23042 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ToolPublicationRetention.java @@ -0,0 +1,8 @@ +package com.alibaba.qwen.code.managedagent.contracts.artifact; +import java.time.Duration; +import java.util.List; +public interface ToolPublicationRetention { + public record Candidate(String scope, String publication, String tenant, String session, + long bytes, String blocker) { } + public List observe(Duration grace); +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ToolPublicationStorageException.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ToolPublicationStorageException.java similarity index 88% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ToolPublicationStorageException.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ToolPublicationStorageException.java index 977f3a38373..954c519b8ad 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ToolPublicationStorageException.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/artifact/ToolPublicationStorageException.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.artifact; +package com.alibaba.qwen.code.managedagent.contracts.artifact; import java.util.Objects; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/AgentDefinitionExtensions.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/catalog/AgentDefinitionExtensions.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/AgentDefinitionExtensions.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/catalog/AgentDefinitionExtensions.java index d05a7fe8817..99c433a6f4d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/AgentDefinitionExtensions.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/catalog/AgentDefinitionExtensions.java @@ -1,11 +1,10 @@ -package com.alibaba.qwen.code.managedagent.catalog; +package com.alibaba.qwen.code.managedagent.contracts.catalog; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.http.HttpStatus; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -173,7 +172,7 @@ public final class AgentDefinitionExtensions { } private static ManagedAgentException unsupported(String field) { - return new ManagedAgentException(HttpStatus.CONFLICT, "agent_definition_unsupported", + return new ManagedAgentException(409, "agent_definition_unsupported", "The agent definition field cannot take effect yet.", Map.of("field", field)); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/channel/ChannelAdapterModels.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/channel/ChannelAdapterModels.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/channel/ChannelAdapterModels.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/channel/ChannelAdapterModels.java index ebae2dc5c5b..9d34efa19db 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/channel/ChannelAdapterModels.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/channel/ChannelAdapterModels.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.channel; +package com.alibaba.qwen.code.managedagent.contracts.channel; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.JsonNode; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/CommittedEventPublisher.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/CommittedEventPublisher.java similarity index 79% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/CommittedEventPublisher.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/CommittedEventPublisher.java index 45cf8b30bb5..072ee8093ca 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/CommittedEventPublisher.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/CommittedEventPublisher.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.event; +package com.alibaba.qwen.code.managedagent.contracts.event; import com.alibaba.qwen.code.managedagent.model.StoreModels.EventRecord; @@ -11,5 +11,5 @@ import java.util.List; * change to the publication contract must be made in both files. */ public interface CommittedEventPublisher { - void publish(List events); + public void publish(List events); } diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/EventMessageListener.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/EventMessageListener.java new file mode 100644 index 00000000000..12d5929e113 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/EventMessageListener.java @@ -0,0 +1,6 @@ +package com.alibaba.qwen.code.managedagent.contracts.event; + +@FunctionalInterface +public interface EventMessageListener { + void consume(byte[] body, String messageId); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/EventSubscriptions.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/EventSubscriptions.java new file mode 100644 index 00000000000..c3467a73dbb --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/EventSubscriptions.java @@ -0,0 +1,8 @@ +package com.alibaba.qwen.code.managedagent.contracts.event; + +import java.util.Set; +public interface EventSubscriptions extends java.io.Closeable { + Set getLiteTopicSet(); + void subscribeLite(String topic) throws Exception; + void unsubscribeLite(String topic) throws Exception; +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/EventTransport.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/EventTransport.java similarity index 92% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/EventTransport.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/EventTransport.java index 0f6754ff78d..b583142ddc7 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/EventTransport.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/EventTransport.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.event; +package com.alibaba.qwen.code.managedagent.contracts.event; import com.alibaba.qwen.code.managedagent.model.StoreModels.EventRecord; @@ -27,5 +27,5 @@ import java.util.List; * change to the publication contract must be made in both files. */ public interface EventTransport { - void publish(List events); + public void publish(List events); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/JournalEventTransport.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/JournalEventTransport.java similarity index 81% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/JournalEventTransport.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/JournalEventTransport.java index 5b2c0813cd1..967914d3dfa 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/JournalEventTransport.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/JournalEventTransport.java @@ -1,8 +1,8 @@ -package com.alibaba.qwen.code.managedagent.event; +package com.alibaba.qwen.code.managedagent.contracts.event; /** * The authoritative-journal wire (design 2026-10-04-managed-agent-event-transport, - * phase MQ2b). An after-commit publication point on {@link com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore#commit} + * phase MQ2b). An after-commit publication point on {@link com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore#commit} * hands the committed journal record (the raw record bytes) to this port. It is * distinct from the {@link EventTransport} hub seam, which carries the public * event stream that MQ2c wires as the {@code public_event} envelope extension. @@ -14,5 +14,5 @@ package com.alibaba.qwen.code.managedagent.event; * treat the broker as recovery evidence (the #12380 constraint). */ public interface JournalEventTransport { - void publishCommitted(byte[] recordBytes); + public void publishCommitted(byte[] recordBytes); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/ManagedEventEnvelope.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/ManagedEventEnvelope.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/ManagedEventEnvelope.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/ManagedEventEnvelope.java index f29bc662ec6..e634dbd82e2 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/ManagedEventEnvelope.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/ManagedEventEnvelope.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.event; +package com.alibaba.qwen.code.managedagent.contracts.event; /** * The managed-event-envelope/1 contract mirrored to Java: the distribution diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/ManagedEventEnvelopes.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/ManagedEventEnvelopes.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/ManagedEventEnvelopes.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/ManagedEventEnvelopes.java index 588f4bef35f..812584a1e9b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/ManagedEventEnvelopes.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/event/ManagedEventEnvelopes.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.event; +package com.alibaba.qwen.code.managedagent.contracts.event; import com.alibaba.qwen.code.managedagent.model.StoreModels.EventRecord; import com.fasterxml.jackson.databind.JsonNode; @@ -29,32 +29,32 @@ import java.util.regex.Pattern; * consumers parse both. */ public final class ManagedEventEnvelopes { - static final int FORMAT_VERSION = 1; + public static final int FORMAT_VERSION = 1; public static final String STREAM_AUTHORITATIVE_JOURNAL = "authoritative_journal"; public static final String STREAM_PUBLIC_EVENT = "public_event"; // Ordered (the failure message joins it), as the TypeScript constant. - static final List STREAMS = List.of( + public static final List STREAMS = List.of( STREAM_AUTHORITATIVE_JOURNAL, STREAM_PUBLIC_EVENT); - static final int MAX_ID_BYTES = 512; + public static final int MAX_ID_BYTES = 512; // A public kind is the public event type: managed_agent_event.event_type // and the public API both cap it at 128, and the grammar is ASCII. - static final int MAX_PUBLIC_KIND_BYTES = 128; - static final Pattern PUBLIC_KIND = Pattern.compile( + public static final int MAX_PUBLIC_KIND_BYTES = 128; + public static final Pattern PUBLIC_KIND = Pattern.compile( "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*$"); - static final long MAX_TIME_MS = 8640000000000000L; + public static final long MAX_TIME_MS = 8640000000000000L; // MANAGED_SESSION_LIMITS.maxJsonDepth: the digest refuses a value nested // deeper, counting from the top-level list at depth 0. - static final int MAX_JSON_DEPTH = 64; + public static final int MAX_JSON_DEPTH = 64; // Number.MAX_SAFE_INTEGER - 1: the schema's sequence ceiling (the TypeScript // assert rejects exactly MAX_SAFE_INTEGER as "cannot advance"). - static final long MAX_SEQUENCE = 9007199254740990L; + public static final long MAX_SEQUENCE = 9007199254740990L; private static final Pattern DIGEST = Pattern.compile("[0-9a-f]{64}"); public static final String KIND_WAKE_REQUESTED = "wake.requested"; // Ordered (the failure message joins it) — the frozen contract's 20-kind // vocabulary in fixture order. - static final List KINDS = List.of( + public static final List KINDS = List.of( "input.accepted", KIND_WAKE_REQUESTED, "activation.changed", "model.attempt", "message.committed", "tool.intent", "action.changed", "tool.receipt", "checkpoint.committed", @@ -63,7 +63,7 @@ public final class ManagedEventEnvelopes { "message.delta", "message.retracted", "operation.replayed", "hosted.batch.planned", "hosted.cleanup"); - static final Set FORBIDDEN_FIELDS = Set.of( + public static final Set FORBIDDEN_FIELDS = Set.of( "absolutePath", "localPath", "pid", "pod", "runtimeBindingId", "runtimeEndpoint", "secretHandle", "sidecar"); @@ -278,7 +278,7 @@ public final class ManagedEventEnvelopes { // --- canonical JSON (mirrors canonicalManagedSessionJson) --- - static String canonicalJson(JsonNode node) { + public static String canonicalJson(JsonNode node) { StringBuilder sb = new StringBuilder(); appendCanonicalJson(sb, node); return sb.toString(); @@ -334,7 +334,7 @@ public final class ManagedEventEnvelopes { * the TypeScript side byte-for-byte. The thresholds are the spec's: * decimal form while 1e-6 ≤ |x| < 1e21, exponential outside it. */ - static String jsNumberToString(double d) { + public static String jsNumberToString(double d) { if (d == 0) { return "0"; } diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/extensions/ExtensionArchives.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/extensions/ExtensionArchives.java new file mode 100644 index 00000000000..0576337d1ed --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/extensions/ExtensionArchives.java @@ -0,0 +1,11 @@ +package com.alibaba.qwen.code.managedagent.contracts.extensions; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; + +public interface ExtensionArchives { + public boolean configured(); + public long put(String tenant, String workspace, String digest, InputStream archive) throws IOException; + public Path verify(String tenant, String workspace, String digest, long expectedLength) throws IOException; +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/extensions/ExtensionPackages.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/extensions/ExtensionPackages.java new file mode 100644 index 00000000000..30b8acf741b --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/extensions/ExtensionPackages.java @@ -0,0 +1,20 @@ +package com.alibaba.qwen.code.managedagent.contracts.extensions; + +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Package; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Pin; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; + +public interface ExtensionPackages { + public void requireSelection(String tenant, String actor, String workspace, List pins); + public ResolvedPackage resolvePin(String tenant, String workspace, Pin pin) throws IOException; + public record ResolvedPackage(Package descriptor, Path archive, Map settings) { + @Override + public String toString() { + return "ResolvedPackage[descriptor=" + descriptor + ", settings=redacted]"; + } + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackages.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/extensions/ManagedExtensionPackages.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackages.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/extensions/ManagedExtensionPackages.java index 14e2e2669cc..520b49fca2f 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackages.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/extensions/ManagedExtensionPackages.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.extensions; +package com.alibaba.qwen.code.managedagent.contracts.extensions; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessConnector.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/harness/HarnessConnector.java similarity index 60% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessConnector.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/harness/HarnessConnector.java index 6c9795611a2..0e60dae5b9f 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessConnector.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/harness/HarnessConnector.java @@ -1,6 +1,6 @@ -package com.alibaba.qwen.code.managedagent.harness; +package com.alibaba.qwen.code.managedagent.contracts.harness; -import com.alibaba.qwen.code.daemon.HarnessRuntimeRecovery; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRuntimeRecovery; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.fasterxml.jackson.databind.JsonNode; @@ -8,130 +8,130 @@ import java.util.List; import java.util.Map; public interface HarnessConnector extends AutoCloseable { - boolean isAvailable(); + public boolean isAvailable(); /** Reattaches a SQL-proven unowned Session without trusting the local attachment cache. */ - default void recoverForWake(String tenantId, String sessionId) { + public default void recoverForWake(String tenantId, String sessionId) { throw new UnsupportedOperationException("Hosted wake recovery is unavailable"); } - default boolean supportsPublicChildAgents() { return false; } + public default boolean supportsPublicChildAgents() { return false; } - default String closeSession(OperationRecord operation) { + public default String closeSession(OperationRecord operation) { return closeSession(operation.tenantId(), operation.sessionId()); } - default void runLifecycleChildOperation(OperationRecord operation, Map body) { + public default void runLifecycleChildOperation(OperationRecord operation, Map body) { runChildOperation(operation.tenantId(), operation.sessionId(), body); } - default Map drainInstance(String instanceId, String operationId) { + public default Map drainInstance(String instanceId, String operationId) { throw new UnsupportedOperationException("Harness drain is unavailable"); } - default Map releaseOwnership(String tenantId, String sessionId, + public default Map releaseOwnership(String tenantId, String sessionId, String operationId, long expectedPlacementEpoch) { throw new UnsupportedOperationException("Harness ownership release is unavailable"); } - default boolean supportsApprovalSelection() { return false; } + public default boolean supportsApprovalSelection() { return false; } - default boolean supportsLiveApprovalSelection() { return false; } + public default boolean supportsLiveApprovalSelection() { return false; } - default boolean supportsAutoApproval() { return false; } + public default boolean supportsAutoApproval() { return false; } - default boolean supportsPermissionRules() { return false; } + public default boolean supportsPermissionRules() { return false; } - default boolean supportsAgentDefinitionApprovalSelection() { return false; } + public default boolean supportsAgentDefinitionApprovalSelection() { return false; } - default boolean supportsModelSelection() { return false; } + public default boolean supportsModelSelection() { return false; } - default boolean supportsUserMessageQueue() { return false; } + public default boolean supportsUserMessageQueue() { return false; } - default boolean supportsContextCompression() { return false; } + public default boolean supportsContextCompression() { return false; } @FunctionalInterface - interface ContextCompressionSubmission { - Map submit(); + public interface ContextCompressionSubmission { + public Map submit(); } - default ContextCompressionSubmission prepareContextCompression(String tenantId, String sessionId, + public default ContextCompressionSubmission prepareContextCompression(String tenantId, String sessionId, Map body) { return () -> runContextCompression(tenantId, sessionId, body); } - default Map runContextCompression(String tenantId, String sessionId, + public default Map runContextCompression(String tenantId, String sessionId, Map body) { throw new UnsupportedOperationException("Context compression is unavailable"); } - default Map readContextCompression(String tenantId, String sessionId, + public default Map readContextCompression(String tenantId, String sessionId, String operationId) { throw new UnsupportedOperationException("Context compression is unavailable"); } - default boolean supportsPlanMode() { return false; } + public default boolean supportsPlanMode() { return false; } - default boolean supportsInputAttachments() { return false; } + public default boolean supportsInputAttachments() { return false; } - default boolean supportsSessionSkills() { return false; } + public default boolean supportsSessionSkills() { return false; } - default boolean supportsMcpManagement() { return false; } + public default boolean supportsMcpManagement() { return false; } - default Map mcpManagement(String tenantId, String sessionId) { + public default Map mcpManagement(String tenantId, String sessionId) { throw new UnsupportedOperationException("MCP management is unavailable"); } - default Map manageMcp(String tenantId, String sessionId, Map command) { + public default Map manageMcp(String tenantId, String sessionId, Map command) { throw new UnsupportedOperationException("MCP management is unavailable"); } - default Map mcpManagementOperation(String tenantId, String sessionId, String operationId) { + public default Map mcpManagementOperation(String tenantId, String sessionId, String operationId) { throw new UnsupportedOperationException("MCP management is unavailable"); } - default boolean supportsSessionExtensions() { return false; } + public default boolean supportsSessionExtensions() { return false; } - default Map applyExtensions(String tenantId, String sessionId, Map command) { + public default Map applyExtensions(String tenantId, String sessionId, Map command) { throw new UnsupportedOperationException("Extension management is unavailable"); } - default Map continueExtensionOperation(String tenantId, String sessionId, String operationId, boolean recoverPrevious) { + public default Map continueExtensionOperation(String tenantId, String sessionId, String operationId, boolean recoverPrevious) { throw new UnsupportedOperationException("Extension management is unavailable"); } - default boolean isWorkspaceFilesAvailable() { + public default boolean isWorkspaceFilesAvailable() { return false; } - default boolean supportsLifecycle() { return false; } + public default boolean supportsLifecycle() { return false; } - default boolean supportsLifecycle(String profile) { - return com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles.FILES.equals(profile) && supportsLifecycle(); + public default boolean supportsLifecycle(String profile) { + return com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles.FILES.equals(profile) && supportsLifecycle(); } - default void prepareLifecycleResources(com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord operation) { + public default void prepareLifecycleResources(com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord operation) { throw new UnsupportedOperationException("Hosted resource lifecycle is unavailable"); } - default void settleLifecycleResources(com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord operation) { + public default void settleLifecycleResources(com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord operation) { throw new UnsupportedOperationException("Hosted resource lifecycle is unavailable"); } - default boolean supportsFencedTitles() { return false; } + public default boolean supportsFencedTitles() { return false; } - default JsonNode settleLifecycle(com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord operation) { + public default JsonNode settleLifecycle(com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord operation) { throw new UnsupportedOperationException("Hosted lifecycle is unavailable"); } - default void detachLifecycle(com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord operation) { + public default void detachLifecycle(com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord operation) { throw new UnsupportedOperationException("Hosted lifecycle is unavailable"); } - Attachment createOrLoad(String tenantId, String sessionId, + public Attachment createOrLoad(String tenantId, String sessionId, boolean loadExisting); - default Attachment createOrLoad(String tenantId, String sessionId, + public default Attachment createOrLoad(String tenantId, String sessionId, boolean loadExisting, boolean passiveManagedRuntimeRecovery) { return createOrLoad(tenantId, sessionId, loadExisting); } @@ -141,13 +141,13 @@ public interface HarnessConnector extends AutoCloseable { * reporting its parked Runtime executions. A plain cold load must stay * inert, so only this path may touch the Broker for a parked Turn. */ - default Attachment recoverManagedRuntime(String tenantId, String sessionId, + public default Attachment recoverManagedRuntime(String tenantId, String sessionId, boolean cancellation) { throw new UnsupportedOperationException( "Managed Runtime recovery is unavailable"); } - default Attachment recoverManagedRuntime(String tenantId, String sessionId, + public default Attachment recoverManagedRuntime(String tenantId, String sessionId, boolean cancellation, String promptId) { throw new UnsupportedOperationException("Targeted Runtime recovery is unavailable"); } @@ -159,24 +159,24 @@ public interface HarnessConnector extends AutoCloseable { * while the healthy-attachment shortcut would answer from cache and * never tell the daemon (R9-P1-2). */ - default Attachment recoverManagedCancellation(String tenantId, + public default Attachment recoverManagedCancellation(String tenantId, String sessionId) { throw new UnsupportedOperationException( "Managed Runtime recovery is unavailable"); } - default Attachment recoverManagedCancellation(String tenantId, + public default Attachment recoverManagedCancellation(String tenantId, String sessionId, String promptId) { throw new UnsupportedOperationException("Targeted Runtime recovery is unavailable"); } - default Attachment recoverManagedFailureCancellation(String tenantId, + public default Attachment recoverManagedFailureCancellation(String tenantId, String sessionId, String turnId, String dispatchOwner) { throw new UnsupportedOperationException( "Managed Runtime recovery is unavailable"); } - default Attachment recoverManagedFailureCancellation(String tenantId, + public default Attachment recoverManagedFailureCancellation(String tenantId, String sessionId, String turnId, String dispatchOwner, String promptId) { throw new UnsupportedOperationException("Targeted Runtime recovery is unavailable"); } @@ -186,53 +186,53 @@ public interface HarnessConnector extends AutoCloseable { * recovery load re-reads the parked Runtime state instead of answering * from a report already known to be stale. */ - default void discardManagedRecovery(String tenantId, String sessionId) { + public default void discardManagedRecovery(String tenantId, String sessionId) { } - record RecoveryStatus(String state, String reasonCode, String promptId, + public record RecoveryStatus(String state, String reasonCode, String promptId, boolean inputSettled, boolean continuationReady, boolean nextTurnAllowed) {} /** Inspects an existing attachment without loading or acquiring anything. */ - default RecoveryStatus inspectRecovery(String tenantId, String sessionId, String promptId) { + public default RecoveryStatus inspectRecovery(String tenantId, String sessionId, String promptId) { return null; } /** Returns only cached replay identity, without loading or acquiring anything. */ - default Attachment cachedRecoveryAttachment(String tenantId, String sessionId) { + public default Attachment cachedRecoveryAttachment(String tenantId, String sessionId) { return null; } /** Returns false when the original terminal cleanup cannot safely be retried. */ - default boolean recheckRecoveryCleanup(String tenantId, String sessionId, String promptId) { + public default boolean recheckRecoveryCleanup(String tenantId, String sessionId, String promptId) { return false; } - Admission submit(String tenantId, String sessionId, String promptId, + public Admission submit(String tenantId, String sessionId, String promptId, List> input, String payloadDigest); - default Admission continueManagedRuntime(String tenantId, + public default Admission continueManagedRuntime(String tenantId, String sessionId, String promptId, String checkpointId, String activationId) { throw new UnsupportedOperationException( "Managed Runtime continuation is unavailable"); } - default Admission cancelManagedRuntime(String tenantId, String sessionId, + public default Admission cancelManagedRuntime(String tenantId, String sessionId, String promptId, String checkpointId, String activationId) { throw new UnsupportedOperationException( "Managed Runtime cancellation is unavailable"); } - SourceStream stream(String tenantId, String sessionId, long lastEventId, + public SourceStream stream(String tenantId, String sessionId, long lastEventId, String eventEpoch); /** Reads only an existing original attachment; it must never load or take over execution. */ - default SourceStream observeStream(String tenantId, String sessionId, String bootId, + public default SourceStream observeStream(String tenantId, String sessionId, String bootId, long lastEventId, String eventEpoch) { throw new UnsupportedOperationException("Read-only Harness observation is unavailable"); } - default void resolveAction( + public default void resolveAction( String tenantId, String sessionId, String actionId, @@ -240,9 +240,9 @@ public interface HarnessConnector extends AutoCloseable { throw new UnsupportedOperationException("Hosted Actions are unavailable"); } - void cancel(String tenantId, String sessionId); + public void cancel(String tenantId, String sessionId); - default void cancelOriginalTurn(String tenantId, String sessionId, String promptId) { + public default void cancelOriginalTurn(String tenantId, String sessionId, String promptId) { throw new UnsupportedOperationException("Targeted cancellation is unavailable"); } @@ -253,7 +253,7 @@ public interface HarnessConnector extends AutoCloseable { * carries the committed operation result; a cancel_run may still be * awaiting its Turn's physical settlement. */ - default Map runAutomationOperation(String tenantId, + public default Map runAutomationOperation(String tenantId, String sessionId, Map body) { throw new UnsupportedOperationException( "Automation operations are unavailable"); @@ -264,7 +264,7 @@ public interface HarnessConnector extends AutoCloseable { * control plane's relay (dispatch/attach/result/accept/cancel/ * close-scope). The Hosted side settles it before answering. */ - default void runChildOperation(String tenantId, String sessionId, + public default void runChildOperation(String tenantId, String sessionId, Map body) { throw new UnsupportedOperationException( "Child operations are unavailable"); @@ -281,7 +281,7 @@ public interface HarnessConnector extends AutoCloseable { * names a command id, and a settled {@code execute} answers with the * reply the relay copies back. */ - default Map runMessageOperation(String tenantId, + public default Map runMessageOperation(String tenantId, String sessionId, Map body) { throw new UnsupportedOperationException( "Message operations are unavailable"); @@ -294,15 +294,15 @@ public interface HarnessConnector extends AutoCloseable { * The Hosted side settles it before answering, and the answer carries * the operation's result. */ - default Map runChannelOperation(String tenantId, + public default Map runChannelOperation(String tenantId, String sessionId, Map body) { throw new UnsupportedOperationException( "Channel operations are unavailable"); } - void rename(String tenantId, String sessionId, String title); + public void rename(String tenantId, String sessionId, String title); - default void rename(String tenantId, String sessionId, String title, long revision) { + public default void rename(String tenantId, String sessionId, String title, long revision) { throw new UnsupportedOperationException("Fenced Session titles are unavailable"); } @@ -310,7 +310,7 @@ public interface HarnessConnector extends AutoCloseable { * Closes the Session and returns the boot ID of the Harness that * answered. A Harness that does not hold the Session answers too. */ - String closeSession(String tenantId, String sessionId); + public String closeSession(String tenantId, String sessionId); /** * The Sessions this control-plane node holds an attachment for. An @@ -320,7 +320,7 @@ public interface HarnessConnector extends AutoCloseable { * narrows it by SQL to the Sessions whose journal this node's Harness * writes. A connector without attachments holds nothing. */ - default List attachedSessions() { + public default List attachedSessions() { return List.of(); } @@ -328,7 +328,7 @@ public interface HarnessConnector extends AutoCloseable { * The writer id this node's Harness acquires Session journals under, or * null while there is none (no client yet, or the connector is closed). */ - default String writerIdentity() { + public default String writerIdentity() { return null; } @@ -339,7 +339,7 @@ public interface HarnessConnector extends AutoCloseable { * take. Returns false, loading nothing, when this node already holds an * attachment for the Session. */ - default boolean activateForWake(String tenantId, String sessionId) { + public default boolean activateForWake(String tenantId, String sessionId) { throw new UnsupportedOperationException( "Hosted wake activation is unavailable"); } @@ -348,13 +348,13 @@ public interface HarnessConnector extends AutoCloseable { default void close() { } - record AttachedSession(String tenantId, String sessionId, String writerIdentity, long placementEpoch) { + public record AttachedSession(String tenantId, String sessionId, String writerIdentity, long placementEpoch) { public AttachedSession(String tenantId, String sessionId) { this(tenantId, sessionId, null, 0); } } - record Attachment(String bootId, HarnessRuntimeRecovery runtimeRecovery, + public record Attachment(String bootId, HarnessRuntimeRecovery runtimeRecovery, Long lastEventId, String eventEpoch) { public Attachment(String bootId) { this(bootId, null, null, null); @@ -366,23 +366,23 @@ public interface HarnessConnector extends AutoCloseable { } } - record Admission(long lastEventId, String eventEpoch) { + public record Admission(long lastEventId, String eventEpoch) { } - record SourceEvent(Long id, String type, Object data, String promptId, + public record SourceEvent(Long id, String type, Object data, String promptId, Map metadata) { } - interface SourceStream extends AutoCloseable { - String eventEpoch(); + public interface SourceStream extends AutoCloseable { + public String eventEpoch(); - SourceEvent next(); + public SourceEvent next(); - default boolean recoveryBlocked(long afterEventId) { + public default boolean recoveryBlocked(long afterEventId) { return false; } @Override - void close(); + public void close(); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessDisabledException.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/harness/HarnessDisabledException.java similarity index 86% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessDisabledException.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/harness/HarnessDisabledException.java index b0744940ac0..6065295fc2f 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HarnessDisabledException.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/harness/HarnessDisabledException.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.harness; +package com.alibaba.qwen.code.managedagent.contracts.harness; // Signals a Harness disabled by configuration rather than a transient // failure, so the coordinator can hold the Turn instead of burning its diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessRecoveryDeclinedException.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/harness/HostedHarnessRecoveryDeclinedException.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessRecoveryDeclinedException.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/harness/HostedHarnessRecoveryDeclinedException.java index c6414732e34..728f3ad4157 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessRecoveryDeclinedException.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/harness/HostedHarnessRecoveryDeclinedException.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.harness; +package com.alibaba.qwen.code.managedagent.contracts.harness; /** * The Hosted Harness refused a takeover load with a typed, terminal reason: diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/AgentDefinitionCompiler.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/AgentDefinitionCompiler.java similarity index 90% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/AgentDefinitionCompiler.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/AgentDefinitionCompiler.java index 19ca83c7e37..de6a6d79a58 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/AgentDefinitionCompiler.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/AgentDefinitionCompiler.java @@ -1,11 +1,10 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; -import com.alibaba.qwen.code.managedagent.catalog.AgentDefinitionExtensions; +import com.alibaba.qwen.code.managedagent.contracts.catalog.AgentDefinitionExtensions; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.http.HttpStatus; import java.nio.charset.StandardCharsets; import java.util.List; @@ -18,7 +17,7 @@ import java.util.Map; * admission with {@code 409 agent_definition_unsupported} naming the field, * instead of being dropped. */ -final class AgentDefinitionCompiler { +public final class AgentDefinitionCompiler { private static final ObjectMapper JSON = new ObjectMapper(); private static final TypeReference> CONTENT = new TypeReference<>() { @@ -26,19 +25,19 @@ final class AgentDefinitionCompiler { private static final List APPROVAL_MODES = List.of("default", "auto-edit", "yolo"); - record Compiled(String approvalMode, String toolProfile) { + public record Compiled(String approvalMode, String toolProfile) { } private AgentDefinitionCompiler() { } - static Compiled compile(String definitionJson, boolean workspaceBound, + public static Compiled compile(String definitionJson, boolean workspaceBound, String deploymentApprovalMode, List toolProfiles) { return compile(definitionJson, workspaceBound, deploymentApprovalMode, toolProfiles, List.of()); } - static Compiled compile(String definitionJson, boolean workspaceBound, + public static Compiled compile(String definitionJson, boolean workspaceBound, String deploymentApprovalMode, List toolProfiles, List models) { Map definition; @@ -121,7 +120,7 @@ final class AgentDefinitionCompiler { } private static ManagedAgentException unsupported(String field) { - return new ManagedAgentException(HttpStatus.CONFLICT, + return new ManagedAgentException(409, "agent_definition_unsupported", "The agent definition field cannot take effect yet.", Map.of("field", field)); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelDeliveryRepository.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ChannelDeliveryRepository.java similarity index 78% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelDeliveryRepository.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ChannelDeliveryRepository.java index 7021985e947..48f009b7b8f 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelDeliveryRepository.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ChannelDeliveryRepository.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; import java.util.List; import java.util.Optional; @@ -19,7 +19,7 @@ public interface ChannelDeliveryRepository { "delivered", "unknown", "rejected", "cancelled"); /** One outbound delivery. */ - record ChannelDelivery( + public record ChannelDelivery( String tenantId, String channelInstanceId, String deliveryId, @@ -32,18 +32,18 @@ public interface ChannelDeliveryRepository { } /** Exclusive page cursor: created time and delivery id of the last row. */ - record DeliveryCursor(long createdAt, String deliveryId) { + public record DeliveryCursor(long createdAt, String deliveryId) { } /** One newest-first page of deliveries. */ - record DeliveryPage(List deliveries, boolean hasMore) { + public record DeliveryPage(List deliveries, boolean hasMore) { } /** * Whether the shared delivery line allows one step from {@code from} to * {@code to} for a channel target. */ - static boolean isLegalStep(String from, String to) { + public static boolean isLegalStep(String from, String to) { return DELIVERY_STATES.contains(to) && ManagedExtensionRecords.TRANSITIONS.get("delivery") .getOrDefault(from, List.of()).contains(to); @@ -54,9 +54,9 @@ public interface ChannelDeliveryRepository { * the row its deliveryId already committed. A duplicate id bound to * another segment fails instead of silently joining it. */ - ChannelDelivery findOrCreate(ChannelDelivery candidate); + public ChannelDelivery findOrCreate(ChannelDelivery candidate); - Optional find(String tenantId, String channelInstanceId, + public Optional find(String tenantId, String channelInstanceId, String deliveryId); /** @@ -67,11 +67,11 @@ public interface ChannelDeliveryRepository { * current row as the idempotent replay of this step; null when the row * is absent or holds any other state. */ - ChannelDelivery transition(String tenantId, String channelInstanceId, + public ChannelDelivery transition(String tenantId, String channelInstanceId, String deliveryId, String expectedState, String state, String providerReceipt); - ChannelDelivery transition(String tenantId, String channelInstanceId, + public ChannelDelivery transition(String tenantId, String channelInstanceId, String deliveryId, String expectedState, String state, String providerReceipt, long claimGeneration); @@ -79,6 +79,6 @@ public interface ChannelDeliveryRepository { * Newest first by (createdAt, deliveryId) descending; {@code before} * excludes its own row. {@code limit} must be in [1, 100]. */ - DeliveryPage listByChannel(String tenantId, String channelInstanceId, + public DeliveryPage listByChannel(String tenantId, String channelInstanceId, DeliveryCursor before, int limit); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelRouteRepository.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ChannelRouteRepository.java similarity index 81% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelRouteRepository.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ChannelRouteRepository.java index 98d7bfe31ad..46bb15163ee 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelRouteRepository.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ChannelRouteRepository.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; import java.util.List; import java.util.Optional; @@ -17,7 +17,7 @@ public interface ChannelRouteRepository { String SEPARATOR = Character.toString((char) 0); /** One committed route binding. */ - record ChannelRoute( + public record ChannelRoute( String tenantId, String routeKey, String channelInstanceId, @@ -36,15 +36,15 @@ public interface ChannelRouteRepository { } /** Exclusive page cursor: created time and route key of the last row. */ - record RouteCursor(long createdAt, String routeKey) { + public record RouteCursor(long createdAt, String routeKey) { } /** One newest-first page of route bindings. */ - record RoutePage(List routes, boolean hasMore) { + public record RoutePage(List routes, boolean hasMore) { } /** The primary key beside the tenant: the binding identity digest. */ - static String routeKey(String tenantId, String channelInstanceId, + public static String routeKey(String tenantId, String channelInstanceId, long accountGeneration, String platformEventId, long semanticRevision) { if (tenantId.contains(SEPARATOR) || channelInstanceId.contains( @@ -62,21 +62,21 @@ public interface ChannelRouteRepository { * committed. A candidate whose identity matches an existing row adds * nothing, so ingress redelivery of one platform event is one binding. */ - ChannelRoute findOrCreate(ChannelRoute candidate); + public ChannelRoute findOrCreate(ChannelRoute candidate); - Optional find(String tenantId, String routeKey); + public Optional find(String tenantId, String routeKey); /** * Records the input a staged route's Session admitted. Returns null when * the binding is absent or already admitted with another input; a replay * with the same input returns the current row. */ - ChannelRoute admit(String tenantId, String routeKey, String inputId); + public ChannelRoute admit(String tenantId, String routeKey, String inputId); /** * Newest first by (createdAt, routeKey) descending; {@code before} * excludes its own row. {@code limit} must be in [1, 100]. */ - RoutePage listByChannel(String tenantId, String channelInstanceId, + public RoutePage listByChannel(String tenantId, String channelInstanceId, RouteCursor before, int limit); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelStoreSupport.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ChannelStoreSupport.java similarity index 68% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelStoreSupport.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ChannelStoreSupport.java index 77033d012f0..274f088832c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelStoreSupport.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ChannelStoreSupport.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; @@ -6,15 +6,15 @@ import org.apache.commons.lang3.StringUtils; import java.nio.charset.StandardCharsets; /** Shared identity helpers of the H5 channel persistence. */ -final class ChannelStoreSupport { +public final class ChannelStoreSupport { private ChannelStoreSupport() { } - static String sha256(String value) { + public static String sha256(String value) { return DigestUtils.sha256Hex(value.getBytes(StandardCharsets.UTF_8)); } - static String requireId(String value, String name) { + public static String requireId(String value, String name) { if (StringUtils.isBlank(value)) { throw new IllegalArgumentException(name + " is required"); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/EventIdentity.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/EventIdentity.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/EventIdentity.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/EventIdentity.java index f4061c85089..a2739447655 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/EventIdentity.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/EventIdentity.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; import com.alibaba.qwen.code.managedagent.model.StoreModels; @@ -46,20 +46,20 @@ public final class EventIdentity { || "item.reasoning.delta".equals(type); } - static String assistantItemId(String turnId) { + public static String assistantItemId(String turnId) { return "item_" + turnId + "_assistant"; } - static String partType(String type) { + public static String partType(String type) { return "item.reasoning.delta".equals(type) ? "reasoning" : "output_text"; } - static String textPartId(String turnId, String partType, long sequence) { + public static String textPartId(String turnId, String partType, long sequence) { return "part_" + turnId + "_" + partType + "_" + sequence; } - static String toolItemId(String turnId, long sequence, + public static String toolItemId(String turnId, long sequence, Map data) { String itemId = string(data.get("itemId")); if (itemId != null) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistration.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ExternalStorageRegistration.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistration.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ExternalStorageRegistration.java index ab73bada723..6f03c90b293 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistration.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ExternalStorageRegistration.java @@ -1,6 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; /** Operator metadata only; registration does not attest or mount storage. */ public record ExternalStorageRegistration(ExternalStorageIdentity identity, StorageAlias alias) { @@ -27,7 +27,7 @@ public record ExternalStorageRegistration(ExternalStorageIdentity identity, Stor } } - String ownerKey() { + public String ownerKey() { // A revision must never create a second owner for the same provider storage. return ManagedSchemaKeys.key("external-storage/1", providerId, physicalKey); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedChannelRecords.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedChannelRecords.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedChannelRecords.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedChannelRecords.java index f2f1e579c9a..b58e307f1a1 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedChannelRecords.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedChannelRecords.java @@ -1,6 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import java.util.HashSet; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedConfigInstall.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedConfigInstall.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedConfigInstall.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedConfigInstall.java index 7396b8b4a5c..27936af7bbf 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedConfigInstall.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedConfigInstall.java @@ -1,9 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; -import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.fasterxml.jackson.databind.JsonNode; -import org.springframework.http.HttpStatus; -import org.springframework.jdbc.core.JdbcTemplate; import java.util.HashSet; import java.util.List; @@ -160,20 +157,6 @@ public final class ManagedConfigInstall { || "recoverPrevious".equals(body.path("mode").asText()) && !"active".equals(phase); } - public static void requireOpen(JdbcTemplate jdbc, String tenantId, String sessionId) { - if (blocked(jdbc, tenantId, sessionId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, - "session_config_recovery_required", "The Session configuration operation must be reconciled first."); - } - } - - public static boolean blocked(JdbcTemplate jdbc, String tenantId, String sessionId) { - return !jdbc.queryForList("SELECT record_key FROM ma_session_record WHERE session_scope_key=? AND tenant_id=?" - + " AND session_id=? AND record_kind='DOMAIN' AND domain='config_install' AND record_id=?" - + " AND settled_at IS NULL LIMIT 1 FOR UPDATE", ManagedSessionStore.sessionScopeKey(tenantId, sessionId), - tenantId, sessionId, ManagedMcpSettings.RECORD_ID).isEmpty(); - } - public static String derivedOperationId(String prefix, String identity) { byte[] bytes = org.apache.commons.codec.digest.DigestUtils.sha256(prefix + identity); bytes[6] = (byte) ((bytes[6] & 15) | 80); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionProjection.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedExtensionProjection.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionProjection.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedExtensionProjection.java index 18946c3c770..e26e81095d2 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionProjection.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedExtensionProjection.java @@ -1,5 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; +import com.alibaba.qwen.code.managedagent.repository.HarnessPlacementStore; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.codec.digest.DigestUtils; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecords.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedExtensionRecords.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecords.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedExtensionRecords.java index 90746faa890..745104a288d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecords.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedExtensionRecords.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -130,7 +130,7 @@ public final class ManagedExtensionRecords { && left.decimalValue().compareTo(right.decimalValue()) == 0 ? 0 : 1; private static final long MAX_COUNT = 9_007_199_254_740_990L; - static final long MAX_TIME = 8_640_000_000_000_000L; + public static final long MAX_TIME = 8_640_000_000_000_000L; private static final BigInteger MAX_GENERATION = BigInteger.valueOf(Long.MAX_VALUE); private static final Pattern GENERATION = Pattern.compile( @@ -143,7 +143,7 @@ public final class ManagedExtensionRecords { // cannot slip a drive spec past the shared contract. private static final Pattern DRIVE_SPEC = Pattern.compile("[A-Za-z]:"); /** Run states after which no observation, output or run change may land. */ - static final List TERMINAL = List.of("settled", "failed", + public static final List TERMINAL = List.of("settled", "failed", "cancelled"); /** The terminal run states, as every binding and mirror rule names them. */ @@ -274,7 +274,7 @@ public final class ManagedExtensionRecords { extends IllegalArgumentException { private static final long serialVersionUID = 1L; - InvalidRecordException(String message) { + public InvalidRecordException(String message) { super(message); } } @@ -1566,7 +1566,7 @@ public final class ManagedExtensionRecords { * Package-wide: the sibling record classes share it rather than * comparing doubles, which splits -0.0 from 0.0. */ - static boolean same(JsonNode left, JsonNode right) { + public static boolean same(JsonNode left, JsonNode right) { return left.equals(SAME_VALUE, right); } @@ -1596,7 +1596,7 @@ public final class ManagedExtensionRecords { } } - static void closed(JsonNode node, Set keys, + public static void closed(JsonNode node, Set keys, String label) { boolean exact = node != null && node.isObject() && node.size() == keys.size(); @@ -1613,7 +1613,7 @@ public final class ManagedExtensionRecords { + keys); } - static String id(JsonNode node, String label) { + public static String id(JsonNode node, String label) { require(node != null && node.isTextual() && !node.textValue() .isEmpty(), () -> label + " must be a non-empty string"); String value = node.textValue(); @@ -1646,12 +1646,12 @@ public final class ManagedExtensionRecords { * encoders write for it — Java's default encoder folds it to one * byte on its own, so it is counted explicitly. */ - static String boundedText(JsonNode node, String label) { + public static String boundedText(JsonNode node, String label) { return boundedText(node, label, MAX_TEXT_BYTES); } /** {@link #boundedText(JsonNode, String)} under a caller's byte cap. */ - static String boundedText(JsonNode node, String label, int maxBytes) { + public static String boundedText(JsonNode node, String label, int maxBytes) { require(node != null && node.isTextual() && !node.textValue() .isEmpty(), label + " must be a non-empty string"); String value = node.textValue(); @@ -1724,7 +1724,7 @@ public final class ManagedExtensionRecords { * JavaScript, 1.0 counts as 1; a number past the double range, which * Jackson reads as an infinity, counts as none. */ - static long count(JsonNode node, long min, long max, + public static long count(JsonNode node, long min, long max, String label) { BigDecimal value = node != null && node.isNumber() && Double.isFinite(node.doubleValue()) @@ -1737,7 +1737,7 @@ public final class ManagedExtensionRecords { return value.longValueExact(); } - static void digest(JsonNode node, String label) { + public static void digest(JsonNode node, String label) { require(node != null && node.isTextual() && DIGEST.matcher(node.textValue()).matches(), () -> label + " must be a lowercase SHA-256 hex digest"); @@ -1752,7 +1752,7 @@ public final class ManagedExtensionRecords { + " 2^63-1"); } - static void durableRef(JsonNode node, String label) { + public static void durableRef(JsonNode node, String label) { closed(node, Set.of("resourceId", "kind", "schemaVersion", "byteLength", "digest"), label); id(node.get("resourceId"), label + ".resourceId"); @@ -1763,7 +1763,7 @@ public final class ManagedExtensionRecords { digest(node.get("digest"), label + ".digest"); } - static String oneOf(JsonNode node, List allowed, + public static String oneOf(JsonNode node, List allowed, String label) { require(node != null && node.isTextual() && allowed.contains(node.textValue()), diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHarnessReleaseRecords.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHarnessReleaseRecords.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHarnessReleaseRecords.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHarnessReleaseRecords.java index c91b794e55d..96c78f52d37 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHarnessReleaseRecords.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHarnessReleaseRecords.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; import com.fasterxml.jackson.databind.JsonNode; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHistoryMaintenanceRecords.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHistoryMaintenanceRecords.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHistoryMaintenanceRecords.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHistoryMaintenanceRecords.java index 55ef960e729..934022c40da 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHistoryMaintenanceRecords.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHistoryMaintenanceRecords.java @@ -1,15 +1,15 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; import com.fasterxml.jackson.databind.JsonNode; import java.util.List; import java.util.Set; -import static com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.closed; -import static com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.count; -import static com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.durableRef; -import static com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.id; -import static com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.oneOf; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.closed; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.count; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.durableRef; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.id; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.oneOf; /** The context-compression slice of the reserved history_maintenance domain. */ public final class ManagedHistoryMaintenanceRecords { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHookRecords.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHookRecords.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHookRecords.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHookRecords.java index 896304a9d27..dd603d8208b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHookRecords.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHookRecords.java @@ -1,6 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.codec.digest.DigestUtils; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHostedRecoveryRecords.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHostedRecoveryRecords.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHostedRecoveryRecords.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHostedRecoveryRecords.java index 92d1ddc5660..d93e1f462b0 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHostedRecoveryRecords.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHostedRecoveryRecords.java @@ -1,10 +1,9 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredResource; import com.alibaba.qwen.code.runtimebroker.ManagedCheckpointHistory; import com.fasterxml.jackson.databind.JsonNode; -import org.springframework.http.HttpStatus; import java.nio.charset.StandardCharsets; import java.util.HashMap; @@ -20,7 +19,7 @@ public final class ManagedHostedRecoveryRecords { "hosted-model-request", "hosted-turn-cleanup"); public interface HistoricalResources extends Function { - void checkpointHistory(JsonNode ref); + public void checkpointHistory(JsonNode ref); } private ManagedHostedRecoveryRecords() {} @@ -33,7 +32,7 @@ public final class ManagedHostedRecoveryRecords { } } for (String line : new String(records, StandardCharsets.UTF_8).split("\n")) { - JsonNode record = ManagedExtensionRecordStore.parse(line); + JsonNode record = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(line); if (record == null) { continue; } @@ -59,7 +58,7 @@ public final class ManagedHostedRecoveryRecords { if (!KINDS.contains(stored.kind()) && !"managed-action-options".equals(stored.kind())) { return; } - JsonNode body = ManagedExtensionRecordStore.parse(new String(stored.bytes(), StandardCharsets.UTF_8)); + JsonNode body = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(new String(stored.bytes(), StandardCharsets.UTF_8)); require(body != null && stored.schemaVersion() == 1, "Invalid Hosted recovery resource."); if ("managed-action-options".equals(stored.kind()) && body.path("v").asInt() != 3) { return; @@ -161,7 +160,7 @@ public final class ManagedHostedRecoveryRecords { if (KINDS.contains(stored.kind()) || List.of("managed-action-options", "managed-checkpoint", ManagedCheckpointHistory.KIND, "managed-definition", "managed-hook-plan", "managed-hook-message-chunks", "managed-message-chunks").contains(stored.kind()) || ManagedExtensionRecords.DOMAINS.stream().anyMatch(domain -> ("managed-" + domain).equals(stored.kind()))) { - JsonNode body = ManagedExtensionRecordStore.parse(new String(stored.bytes(), StandardCharsets.UTF_8)); + JsonNode body = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(new String(stored.bytes(), StandardCharsets.UTF_8)); require(body != null && stored.schemaVersion() == 1, "Invalid Hosted recovery resource."); if (KINDS.contains(stored.kind())) { validateBody(stored.kind(), body, tenant, workspace, session); @@ -187,7 +186,7 @@ public final class ManagedHostedRecoveryRecords { ? promptId : "wake-" + ToolPublicationContract.sha256(promptId.getBytes(StandardCharsets.UTF_8)); } - static List references(JsonNode body) { + public static List references(JsonNode body) { var refs = new java.util.ArrayList(); for (String field : List.of("definitionRef", "rootSnapshotRef", "assistantRef", "inputRef", "continuationRef")) { if (body.hasNonNull(field)) { @@ -357,6 +356,6 @@ public final class ManagedHostedRecoveryRecords { } private static ManagedAgentException rejected(String message) { - return new ManagedAgentException(HttpStatus.CONFLICT, "managed_hosted_recovery_record_rejected", message); + return new ManagedAgentException(409, "managed_hosted_recovery_record_rejected", message); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMcpRecords.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedMcpRecords.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMcpRecords.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedMcpRecords.java index aec024aaad7..170bfca4496 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMcpRecords.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedMcpRecords.java @@ -1,6 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -168,7 +168,7 @@ public final class ManagedMcpRecords { } } - static boolean same(JsonNode left, JsonNode right) { + public static boolean same(JsonNode left, JsonNode right) { return left.equals((a, b) -> a.isNumber() && b.isNumber() ? Double.compare(a.doubleValue(), b.doubleValue()) : a.equals(b) ? 0 : 1, right); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionCommandRecords.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedSessionCommandRecords.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionCommandRecords.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedSessionCommandRecords.java index 13d0e193047..31514fac88c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionCommandRecords.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedSessionCommandRecords.java @@ -1,6 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import java.util.List; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionMessageRecords.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedSessionMessageRecords.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionMessageRecords.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedSessionMessageRecords.java index 2db19d60748..84e85c1cd53 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionMessageRecords.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedSessionMessageRecords.java @@ -1,6 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import java.util.List; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStoreModels.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedSessionStoreModels.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStoreModels.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedSessionStoreModels.java index bfc39f72d38..df04acf7b26 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStoreModels.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedSessionStoreModels.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; import com.fasterxml.jackson.annotation.JsonInclude; import jakarta.validation.Valid; @@ -160,7 +160,7 @@ public final class ManagedSessionStoreModels { public WriterGrant(long writerGeneration, long leaseUntil, long journalRevision, long committedSequence, String lastCommitDigest, long activationEpoch, boolean replayed) { this(writerGeneration, leaseUntil, journalRevision, committedSequence, lastCommitDigest, - activationEpoch, replayed, ManagedSessionReaderVersion.SUPPORTED); + activationEpoch, replayed, ManagedStorageVersions.SUPPORTED); } } diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedStorageVersions.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedStorageVersions.java new file mode 100644 index 00000000000..cd4992535f1 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedStorageVersions.java @@ -0,0 +1,6 @@ +package com.alibaba.qwen.code.managedagent.contracts.persistence; + +public final class ManagedStorageVersions { + public static final int SUPPORTED = 2; + private ManagedStorageVersions() { } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedTeamRecords.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedTeamRecords.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedTeamRecords.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedTeamRecords.java index bbe3ef706ba..086b75d6bd7 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedTeamRecords.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedTeamRecords.java @@ -1,6 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import java.util.HashSet; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolStageRecords.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedToolStageRecords.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolStageRecords.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedToolStageRecords.java index f83c6420083..5081d76bd53 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolStageRecords.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedToolStageRecords.java @@ -1,6 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import java.util.List; diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/StorageReadPolicy.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/StorageReadPolicy.java new file mode 100644 index 00000000000..a72f79f2f79 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/StorageReadPolicy.java @@ -0,0 +1,15 @@ +package com.alibaba.qwen.code.managedagent.contracts.persistence; + +import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; +@FunctionalInterface +public interface StorageReadPolicy { + void requireReadable(int floor); + static StorageReadPolicy inProcess() { + return floor -> { + if (floor > ManagedStorageVersions.SUPPORTED) { + throw new ManagedAgentException(409, "managed_session_reader_version_unsupported", + "This reader cannot read the Managed Session storage version."); + } + }; + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationContract.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ToolPublicationContract.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationContract.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ToolPublicationContract.java index f22ce7740b3..b8b54d05e17 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationContract.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ToolPublicationContract.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.StreamReadFeature; @@ -422,11 +422,11 @@ public final class ToolPublicationContract { return value.longValueExact(); } - static String text(JsonNode node, String key) { + public static String text(JsonNode node, String key) { return node == null ? "" : node.path(key).asText(""); } - static final class ContractViolationException extends IllegalArgumentException { + public static final class ContractViolationException extends IllegalArgumentException { private ContractViolationException(String message) { super(message); } @@ -436,7 +436,7 @@ public final class ToolPublicationContract { } } - static void require(boolean condition, String reason) { + public static void require(boolean condition, String reason) { if (!condition) { throw new ContractViolationException(reason); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationJson.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/WorkspaceCsiApplicationJson.java similarity index 87% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationJson.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/WorkspaceCsiApplicationJson.java index 80b9eebcda6..665cdedb208 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationJson.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/WorkspaceCsiApplicationJson.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.StreamReadConstraints; @@ -15,7 +15,7 @@ import java.util.Base64; import java.util.HashSet; import java.util.Set; -final class WorkspaceCsiApplicationJson { +public final class WorkspaceCsiApplicationJson { private static final ObjectMapper JSON = new ObjectMapper(JsonFactory.builder() .enable(StreamReadFeature.STRICT_DUPLICATE_DETECTION) .streamReadConstraints(StreamReadConstraints.builder().maxNestingDepth(64) @@ -32,7 +32,7 @@ final class WorkspaceCsiApplicationJson { private WorkspaceCsiApplicationJson() { } - static JsonNode object(byte[] bytes, int limit) { + public static JsonNode object(byte[] bytes, int limit) { require(bytes != null && bytes.length > 0 && bytes.length <= limit); try { String text = StandardCharsets.UTF_8.newDecoder().onMalformedInput(CodingErrorAction.REPORT) @@ -45,14 +45,14 @@ final class WorkspaceCsiApplicationJson { } } - static void closed(JsonNode value, Set fields) { + public static void closed(JsonNode value, Set fields) { require(value != null && value.isObject()); Set actual = new HashSet<>(); value.fieldNames().forEachRemaining(actual::add); require(actual.equals(fields)); } - static T typedObject(byte[] bytes, int limit, Class type) { + public static T typedObject(byte[] bytes, int limit, Class type) { JsonNode value = object(bytes, limit); try { return TYPED.treeToValue(value, type); @@ -61,14 +61,14 @@ final class WorkspaceCsiApplicationJson { } } - static byte[] base64(JsonNode value, int limit) { + public static byte[] base64(JsonNode value, int limit) { require(value != null && value.isTextual() && value.textValue().length() <= ((long) limit + 2) / 3 * 4); byte[] bytes = Base64.getDecoder().decode(value.textValue()); require(bytes.length > 0 && bytes.length <= limit && Base64.getEncoder().encodeToString(bytes).equals(value.textValue())); return bytes; } - static byte[] encode(Object value) { + public static byte[] encode(Object value) { try { return JSON.writeValueAsBytes(value); } catch (IOException error) { @@ -76,7 +76,7 @@ final class WorkspaceCsiApplicationJson { } } - static void require(boolean valid) { + public static void require(boolean valid) { if (!valid) { throw new IllegalArgumentException("Invalid original CSI application evidence"); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRegistration.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/WorkspaceCsiRegistration.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRegistration.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/WorkspaceCsiRegistration.java index dfdd23ea610..1d471d0abcd 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRegistration.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/persistence/WorkspaceCsiRegistration.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; @@ -47,7 +47,7 @@ public record WorkspaceCsiRegistration(String tenantId, String storageId, return aliasKey(tenantId, storageId); } - static String aliasKey(String tenantId, String storageId) { + public static String aliasKey(String tenantId, String storageId) { return key("qwen-csi-alias/1", tenantId, storageId); } diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/runtime/ProtectionIdentity.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/runtime/ProtectionIdentity.java new file mode 100644 index 00000000000..50c20d88809 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/runtime/ProtectionIdentity.java @@ -0,0 +1,19 @@ +package com.alibaba.qwen.code.managedagent.contracts.runtime; + +import java.util.List; + +public record ProtectionIdentity(String namespaceUid, String policyName, String policyUid, + long policyGeneration, String bindingName, String bindingUid, long bindingGeneration) { + public ProtectionIdentity { + for (String value : List.of(namespaceUid, policyName, policyUid, bindingName, bindingUid)) { + if (value.isBlank() + || value.length() > 253 + || value.codePoints().anyMatch(point -> point < 33)) { + throw new IllegalArgumentException("Invalid CSI protection identity"); + } + } + if (policyGeneration < 1 || bindingGeneration < 1) { + throw new IllegalArgumentException("Invalid CSI protection generation"); + } + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/runtime/Receipt.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/runtime/Receipt.java new file mode 100644 index 00000000000..9ea49283e55 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/runtime/Receipt.java @@ -0,0 +1,9 @@ +package com.alibaba.qwen.code.managedagent.contracts.runtime; + +public record Receipt( + String physicalKey, + long registrationRevision, + String namespaceUid, + String pvcResourceVersion, + String pvResourceVersion, + ProtectionIdentity protection) {} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/runtime/WorkerArtifact.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/runtime/WorkerArtifact.java new file mode 100644 index 00000000000..fc34834e713 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/runtime/WorkerArtifact.java @@ -0,0 +1,15 @@ +package com.alibaba.qwen.code.managedagent.contracts.runtime; + +import java.util.Objects; + +import static com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity.dns; +import static com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity.require; +import static com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity.uuid; + +public record WorkerArtifact(String configMapName, String uid, String sha256) { + public WorkerArtifact { + dns(Objects.requireNonNull(configMapName)); + uuid(Objects.requireNonNull(uid)); + require(sha256 != null && sha256.matches("[0-9a-f]{64}")); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/security/WriterCredentialPolicy.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/security/WriterCredentialPolicy.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/security/WriterCredentialPolicy.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/security/WriterCredentialPolicy.java index 02a215ded03..b44121c1f7b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/security/WriterCredentialPolicy.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/security/WriterCredentialPolicy.java @@ -1,15 +1,12 @@ -package com.alibaba.qwen.code.managedagent.security; +package com.alibaba.qwen.code.managedagent.contracts.security; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.HmacAlgorithms; import org.apache.commons.codec.digest.HmacUtils; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; @@ -21,7 +18,6 @@ import java.util.Base64; * scope and self-minted tokens are rejected; without one the legacy * first-writer-wins registration keeps serving loopback deployments. */ -@Component public class WriterCredentialPolicy { private static final String TOKEN_PREFIX = "qwt1_"; private static final String PLACEMENT_PREFIX = "qwp1_"; @@ -30,8 +26,7 @@ public class WriterCredentialPolicy { new WriterCredentialPolicy((String) null); private final byte[] bindingKey; - @Autowired - public WriterCredentialPolicy(ManagedAgentProperties properties) { + public WriterCredentialPolicy(ManagedAgentSettings properties) { this(properties.getSessionStore().getBindingKey()); } @@ -128,7 +123,7 @@ public class WriterCredentialPolicy { } private static ManagedAgentException invalidCredential() { - return new ManagedAgentException(HttpStatus.FORBIDDEN, + return new ManagedAgentException(403, ManagedSessionStoreModels.ERROR_WRITER_CREDENTIAL_INVALID, "The writer credential is not valid for this Session."); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/AcceptanceEvidence.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/AcceptanceEvidence.java similarity index 81% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/AcceptanceEvidence.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/AcceptanceEvidence.java index 45a4a238901..aafb45aefd3 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/AcceptanceEvidence.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/AcceptanceEvidence.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.session; +package com.alibaba.qwen.code.managedagent.contracts.session; import com.alibaba.qwen.code.managedagent.model.StoreModels.AcceptanceVerdict; import com.alibaba.qwen.code.managedagent.model.StoreModels.ParkedOperation; @@ -15,13 +15,13 @@ import java.util.Optional; */ public interface AcceptanceEvidence { /** The {@code ma_operation.operation_kind} this source decides. */ - String kind(); + public String kind(); /** * The verdict for a parked operation, or empty when the row is no longer * parked. */ - Optional read(ParkedOperation parked); + public Optional read(ParkedOperation parked); /** * Records the verdict, only while the row is still parked. A kind whose @@ -30,7 +30,7 @@ public interface AcceptanceEvidence { * * @return false when the row is no longer parked */ - default boolean settle(AgentStateStore store, ParkedOperation parked, + public default boolean settle(AgentStateStore store, ParkedOperation parked, AcceptanceVerdict verdict, long retryAt) { return store.settleParkedOperation(parked, verdict, retryAt); } @@ -39,7 +39,7 @@ public interface AcceptanceEvidence { * The parked rows of a kind that belongs to no Session, which the * Session-scoped scan never reads. */ - default List parked(int limit) { + public default List parked(int limit) { return List.of(); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/AgentStateStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/AgentStateStore.java similarity index 73% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/AgentStateStore.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/AgentStateStore.java index f6f628ff20e..2df7ae10bc3 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/AgentStateStore.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/AgentStateStore.java @@ -1,5 +1,6 @@ -package com.alibaba.qwen.code.managedagent.session; +package com.alibaba.qwen.code.managedagent.contracts.session; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellApprovalChangeResult; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellApprovalSelection; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellLifecycleOperation; @@ -33,7 +34,6 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnPage; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnSummary; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.fasterxml.jackson.databind.JsonNode; import java.time.Duration; @@ -43,6 +43,14 @@ import java.util.Optional; import java.util.Set; public interface AgentStateStore { + default Optional findSession(String tenantId, String sessionId) { + return findSessionById(sessionId).filter(session -> tenantId.equals(session.tenantId())); + } + + public default void failWorktreeInitialTurns(String tenantId, String sessionId, String code) { + throw new UnsupportedOperationException("Session worktrees are unavailable"); + } + /** * #14387: the durable error code a workflow Turn carries once its * recovery was refused — the Harness generation running it was lost @@ -66,10 +74,7 @@ public interface AgentStateStore { String HARNESS_RECOVERY_BLOCKED = "hosted_harness_recovery_blocked"; - // The annotation must sit on the default itself: the delegating body runs - // on the target instance, so without it the self-call bypasses the proxy. - @org.springframework.transaction.annotation.Transactional - default Admission insertSessionCommand(String tenantId, String operation, + public default Admission insertSessionCommand(String tenantId, String operation, String idempotencyKey, String requestDigest, String agentId, String requestedRevision, String title, List> input, String payloadDigest) { @@ -78,7 +83,7 @@ public interface AgentStateStore { title, input, payloadDigest); } - Admission insertSessionCommand(String tenantId, String actorId, + public Admission insertSessionCommand(String tenantId, String actorId, String operation, String idempotencyKey, String requestDigest, String agentId, String requestedRevision, String title, List> input, String payloadDigest); @@ -91,14 +96,14 @@ public interface AgentStateStore { * the control plane (the relay), so this path deliberately skips the * public actor/workspace checks — the parent's row carries them. */ - default StoreModels.Admission insertChildSessionCommand(String tenantId, + public default StoreModels.Admission insertChildSessionCommand(String tenantId, String parentSessionId, String idempotencyKey, String requestDigest, String title, List> input, String payloadDigest, StoreModels.SessionLineage lineage) { throw new UnsupportedOperationException("Child Session creation is unavailable"); } - default StoreModels.Admission insertPublicChildSessionCommand(String tenantId, + public default StoreModels.Admission insertPublicChildSessionCommand(String tenantId, String parentSessionId, String idempotencyKey, String requestDigest, String title, List> input, String payloadDigest, StoreModels.SessionLineage lineage, com.fasterxml.jackson.databind.JsonNode snapshot) { @@ -113,7 +118,7 @@ public interface AgentStateStore { * unfinished, prepared from the parent's current Workspace and storage, * and names {@code childCwdRelative}. */ - default StoreModels.Admission insertChildSessionCommand(String tenantId, + public default StoreModels.Admission insertChildSessionCommand(String tenantId, String parentSessionId, String idempotencyKey, String requestDigest, String title, List> input, String payloadDigest, StoreModels.SessionLineage lineage, @@ -126,36 +131,35 @@ public interface AgentStateStore { * its layout was recorded. Readiness is the creating transaction's to * check, so a replay still answers once the Workspace moved on. */ - default String findChildWorkspaceCwd(String tenantId, + public default String findChildWorkspaceCwd(String tenantId, String parentSessionId, String childRunId) { return null; } /** The replay of {@link #insertChildSessionCommand}: same key and * digest answers the original admission; either mismatch conflicts. */ - default StoreModels.Admission replayChildSessionCommand(String tenantId, + public default StoreModels.Admission replayChildSessionCommand(String tenantId, String parentSessionId, String idempotencyKey, String requestDigest) { throw new UnsupportedOperationException("Child Session creation is unavailable"); } /** A child Session's persisted lineage, or null for a root Session. */ - default StoreModels.SessionLineage findChildLineage(String tenantId, + public default StoreModels.SessionLineage findChildLineage(String tenantId, String sessionId) { return null; } - Admission insertWorkspaceSessionCommand(String tenantId, String actorId, + public Admission insertWorkspaceSessionCommand(String tenantId, String actorId, String idempotencyKey, String requestDigest, String agentId, String requestedRevision, String title, List> input, String payloadDigest, WorkspaceSelection selection); - Optional findWorkspaceCreateReplay(String tenantId, + public Optional findWorkspaceCreateReplay(String tenantId, String actorId, String idempotencyKey, String requestDigest); - @org.springframework.transaction.annotation.Transactional - default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, + public default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, String idempotencyKey, String requestDigest, String agentId, String requestedRevision, String title, List> input, String payloadDigest, @@ -167,8 +171,7 @@ public interface AgentStateStore { requestDigest, agentId, requestedRevision, title, input, payloadDigest, selection); } - @org.springframework.transaction.annotation.Transactional - default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, + public default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, String idempotencyKey, String requestDigest, String agentId, String requestedRevision, String title, List> input, String payloadDigest, WorkspaceSelection selection, String approvalMode, @@ -178,13 +181,12 @@ public interface AgentStateStore { selection, approvalMode); } - default WebShellSessionOptions workspaceSessionOptions( + public default WebShellSessionOptions workspaceSessionOptions( String tenantId, String agentId) { throw new UnsupportedOperationException("Approval mode selection is unavailable"); } - @org.springframework.transaction.annotation.Transactional - default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, + public default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, String idempotencyKey, String requestDigest, String agentId, String requestedRevision, String title, List> input, String payloadDigest, WorkspaceSelection selection, String approvalMode, boolean approvalSelectionSupported, @@ -197,8 +199,7 @@ public interface AgentStateStore { approvalMode, approvalSelectionSupported); } - @org.springframework.transaction.annotation.Transactional - default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, + public default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, String idempotencyKey, String requestDigest, String agentId, String requestedRevision, String title, List> input, String payloadDigest, WorkspaceSelection selection, String approvalMode, boolean approvalSelectionSupported, @@ -211,7 +212,7 @@ public interface AgentStateStore { approvalSelectionSupported, planMode, planModeSupported); } - default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, + public default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, String idempotencyKey, String requestDigest, String agentId, String requestedRevision, String title, List> input, String payloadDigest, WorkspaceSelection selection, String approvalMode, boolean approvalSelectionSupported, Boolean planMode, boolean planModeSupported, @@ -221,8 +222,7 @@ public interface AgentStateStore { approvalSelectionSupported, planMode, planModeSupported, skills); } - @org.springframework.transaction.annotation.Transactional - default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, + public default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, String idempotencyKey, String requestDigest, String agentId, String requestedRevision, String title, List> input, String payloadDigest, WorkspaceSelection selection, String approvalMode, boolean approvalSelectionSupported, Boolean planMode, boolean planModeSupported, @@ -235,13 +235,12 @@ public interface AgentStateStore { approvalSelectionSupported, planMode, planModeSupported, skills, publicChildrenSupported); } - @org.springframework.transaction.annotation.Transactional - default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, + public default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, String idempotencyKey, String requestDigest, String agentId, String requestedRevision, String title, List> input, String payloadDigest, WorkspaceSelection selection, String approvalMode, boolean approvalSelectionSupported, Boolean planMode, boolean planModeSupported, List> skills, boolean publicChildrenSupported, String modelId, - List extensionPins, + List extensionPins, Set extensionLifecycleProfiles) { if (extensionPins != null) { throw new UnsupportedOperationException("Initial extension selection is unavailable"); @@ -251,17 +250,17 @@ public interface AgentStateStore { approvalSelectionSupported, planMode, planModeSupported, skills, publicChildrenSupported, modelId); } - Admission replayWorkspaceSessionCommand(String tenantId, String actorId, + public Admission replayWorkspaceSessionCommand(String tenantId, String actorId, String idempotencyKey, String requestDigest); - default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, + public default Admission insertWorkspaceSessionCommand(String tenantId, String actorId, String idempotencyKey, String requestDigest, String agentId, String requestedRevision, String title, List> input, String payloadDigest, WorkspaceSelection selection, String approvalMode, boolean approvalSelectionSupported, Boolean planMode, boolean planModeSupported, List> skills, boolean publicChildrenSupported, String modelId, - List extensionPins, + List extensionPins, Set extensionLifecycleProfiles, - com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore.Plan worktree) { + com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Plan worktree) { if (worktree != null) { throw new UnsupportedOperationException("Session worktrees are unavailable"); } @@ -271,17 +270,17 @@ public interface AgentStateStore { modelId, extensionPins, extensionLifecycleProfiles); } - default com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore.Descriptor findSessionWorktree( + public default com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Descriptor findSessionWorktree( String tenantId, String sessionId) { return null; } - default Map findSessionWorktrees( + public default Map findSessionWorktrees( String tenantId, List sessionIds) { return Map.of(); } - Admission insertTurnCommand(String tenantId, String operation, + public Admission insertTurnCommand(String tenantId, String operation, String idempotencyKey, String requestDigest, String sessionId, List> input, String payloadDigest); @@ -291,7 +290,7 @@ public interface AgentStateStore { * OPERATOR's own retry of his own key — and not only the creator. The * default drops the actor for implementations that cannot record it. */ - default Admission insertTurnCommand(String tenantId, String operation, + public default Admission insertTurnCommand(String tenantId, String operation, String idempotencyKey, String requestDigest, String sessionId, List> input, String payloadDigest, String actorId) { @@ -299,59 +298,59 @@ public interface AgentStateStore { requestDigest, sessionId, input, payloadDigest); } - Admission insertCancelCommand(String tenantId, String operation, + public Admission insertCancelCommand(String tenantId, String operation, String idempotencyKey, String requestDigest, String sessionId, String turnId); /** The actor-carrying twin of {@link #insertTurnCommand}. */ - default Admission insertCancelCommand(String tenantId, String operation, + public default Admission insertCancelCommand(String tenantId, String operation, String idempotencyKey, String requestDigest, String sessionId, String turnId, String actorId) { return insertCancelCommand(tenantId, operation, idempotencyKey, requestDigest, sessionId, turnId); } - SessionMutationCommand beginSessionMutation(String tenantId, + public SessionMutationCommand beginSessionMutation(String tenantId, String operation, String idempotencyKey, String requestDigest, String sessionId, SessionMutationKind kind); /** The actor-carrying twin of {@link #insertTurnCommand}. */ - default SessionMutationCommand beginSessionMutation(String tenantId, + public default SessionMutationCommand beginSessionMutation(String tenantId, String operation, String idempotencyKey, String requestDigest, String sessionId, SessionMutationKind kind, String actorId) { return beginSessionMutation(tenantId, operation, idempotencyKey, requestDigest, sessionId, kind); } - SessionMutationCommand beginSessionRename(String tenantId, String idempotencyKey, + public SessionMutationCommand beginSessionRename(String tenantId, String idempotencyKey, String requestDigest, String sessionId, String title); /** The actor-carrying twin: the recorded rename answers its recorder. */ - default SessionMutationCommand beginSessionRename(String tenantId, + public default SessionMutationCommand beginSessionRename(String tenantId, String idempotencyKey, String requestDigest, String sessionId, String title, String actorId) { return beginSessionRename(tenantId, idempotencyKey, requestDigest, sessionId, title); } - SessionRecord completeSessionRename(String tenantId, String idempotencyKey, + public SessionRecord completeSessionRename(String tenantId, String idempotencyKey, String sessionId, String title, String harnessBootId, long revision); - SessionRecord completeSessionRename(StoreModels.RenameDelivery delivery, String owner, String bootId); + public SessionRecord completeSessionRename(StoreModels.RenameDelivery delivery, String owner, String bootId); - void abandonSessionRename(String tenantId, String idempotencyKey, + public void abandonSessionRename(String tenantId, String idempotencyKey, String sessionId, long revision, String owner); - List deliverableRenames(long now); + public List deliverableRenames(long now); - Optional claimRename(StoreModels.RenameDelivery delivery, + public Optional claimRename(StoreModels.RenameDelivery delivery, String owner, Duration lease); - void markRenameSubmitted(StoreModels.RenameDelivery delivery, String owner); + public void markRenameSubmitted(StoreModels.RenameDelivery delivery, String owner); - void retryRename(StoreModels.RenameDelivery delivery, String owner, long availableAt); + public void retryRename(StoreModels.RenameDelivery delivery, String owner, long availableAt); - SessionRecord completeSessionMutation(String tenantId, String operation, + public SessionRecord completeSessionMutation(String tenantId, String operation, String idempotencyKey, String sessionId, SessionMutationKind kind, String title, String harnessBootId); @@ -362,7 +361,7 @@ public interface AgentStateStore { * completes. The receipt and digest survive for same-content retries * and concurrent completion; completed outcomes remain replayable. */ - void abandonSessionMutation(String tenantId, String operation, + public void abandonSessionMutation(String tenantId, String operation, String idempotencyKey, String sessionId); /** @@ -370,34 +369,34 @@ public interface AgentStateStore { * same actor already admitted under the key. An archive completes here; * a close or delete waits for {@link #completeOperation}. */ - OperationAdmission beginOperation(String tenantId, String sessionId, + public OperationAdmission beginOperation(String tenantId, String sessionId, OperationKind kind, String actorDigest, String idempotencyKey, String requestDigest); - default boolean workspaceFilesEnabled() { + public default boolean workspaceFilesEnabled() { return false; } - default boolean workspaceShellEnabled() { + public default boolean workspaceShellEnabled() { return false; } - default OperationAdmission beginWorkspaceClose(String tenantId, String sessionId, + public default OperationAdmission beginWorkspaceClose(String tenantId, String sessionId, String actorId, String actorDigest, String key, String digest, boolean supported) { throw new UnsupportedOperationException("Workspace close is unavailable"); } - OperationAdmission beginWorkspaceLifecycle(String tenantId, String sessionId, + public OperationAdmission beginWorkspaceLifecycle(String tenantId, String sessionId, OperationKind kind, String actorId, String actorDigest, String key, String digest, boolean closeSupported); - default OperationAdmission beginChildClose(String tenantId, String parentSessionId, + public default OperationAdmission beginChildClose(String tenantId, String parentSessionId, String childSessionId, String childRunId, String actorDigest, String key, String digest, boolean supported) { throw new UnsupportedOperationException("Internal child close is unavailable"); } - default OperationAdmission beginWorkspaceLifecycle(String tenantId, String sessionId, OperationKind kind, + public default OperationAdmission beginWorkspaceLifecycle(String tenantId, String sessionId, OperationKind kind, String actorId, String actorDigest, String key, String digest, boolean supported, int protocolVersion) { if (protocolVersion != 0) { throw new UnsupportedOperationException("Workspace lifecycle protocol is unavailable"); @@ -405,10 +404,10 @@ public interface AgentStateStore { return beginWorkspaceLifecycle(tenantId, sessionId, kind, actorId, actorDigest, key, digest, supported); } - boolean hasCompletedWorkspaceClose(String tenantId, String sessionId); + public boolean hasCompletedWorkspaceClose(String tenantId, String sessionId); /** The given Sessions with a completed workspace close, in one read. */ - Set completedWorkspaceCloses(String tenantId, + public Set completedWorkspaceCloses(String tenantId, List sessionIds); /** @@ -419,21 +418,21 @@ public interface AgentStateStore { * any admission certifying a run under the creator's grants checks it * first. */ - boolean hasExecutionRegistryFacts(String tenantId, String sessionId); + public boolean hasExecutionRegistryFacts(String tenantId, String sessionId); /** The given Sessions whose execution facts hold, in one read. */ - Set sessionsWithExecutionRegistryFacts(String tenantId, + public Set sessionsWithExecutionRegistryFacts(String tenantId, java.util.Collection sessionIds); - SessionMutation unarchiveWorkspaceSession(String tenantId, String sessionId, + public SessionMutation unarchiveWorkspaceSession(String tenantId, String sessionId, String actorId, String scopedKey, String requestDigest); - default boolean renewLifecycleOperation(String tenantId, String sessionId, String operationId, + public default boolean renewLifecycleOperation(String tenantId, String sessionId, String operationId, String owner, long generation, Duration duration) { return false; } - default void blockLifecycleOperation(String tenantId, String sessionId, String operationId, + public default void blockLifecycleOperation(String tenantId, String sessionId, String operationId, String owner, long generation, String failureCode, long availableAt) { throw new UnsupportedOperationException("Lifecycle reconciliation is unavailable"); } @@ -443,7 +442,7 @@ public interface AgentStateStore { * attempt as a wait on an external condition (a live journal writer or a * stale Harness view) that never consumed the terminal retry budget. */ - default void blockLifecycleOperation(String tenantId, String sessionId, String operationId, + public default void blockLifecycleOperation(String tenantId, String sessionId, String operationId, String owner, long generation, String failureCode, long availableAt, boolean budgetExempt) { if (budgetExempt) { @@ -453,46 +452,46 @@ public interface AgentStateStore { generation, failureCode, availableAt); } - default OperationAdmission beginContextCompression(String tenantId, String sessionId, + public default OperationAdmission beginContextCompression(String tenantId, String sessionId, String actorId, String actorDigest, String key, String digest, long expectedCompactionRevision, boolean supported) { throw new UnsupportedOperationException("Context compression is unavailable"); } - record ContextCompressionState(long compactionRevision, String operationId, boolean available) { + public record ContextCompressionState(long compactionRevision, String operationId, boolean available) { public ContextCompressionState(long compactionRevision, String operationId) { this(compactionRevision, operationId, true); } } - default ContextCompressionState contextCompressionState(String tenantId, String sessionId) { + public default ContextCompressionState contextCompressionState(String tenantId, String sessionId) { return new ContextCompressionState(0, null); } - default Map contextCompressionStates(String tenantId, List sessionIds) { + public default Map contextCompressionStates(String tenantId, List sessionIds) { return Map.of(); } - default Map contextCompressionRequest(String tenantId, String sessionId, + public default Map contextCompressionRequest(String tenantId, String sessionId, String operationId) { return Map.of(); } - default List findContextCompressions(int limit) { return List.of(); } + public default List findContextCompressions(int limit) { return List.of(); } - default Optional claimContextCompression(String tenantId, String sessionId, + public default Optional claimContextCompression(String tenantId, String sessionId, String operationId, String owner, Duration duration) { return Optional.empty(); } - default void requireContextCompressionCommit(String tenantId, String sessionId, String operationId) {} + public default void requireContextCompressionCommit(String tenantId, String sessionId, String operationId) {} - default boolean markContextCompressionSubmitted(OperationRecord operation, String owner) { + public default boolean markContextCompressionSubmitted(OperationRecord operation, String owner) { return false; } - default boolean settleContextCompression(OperationRecord operation, String owner, + public default boolean settleContextCompression(OperationRecord operation, String owner, String outcome, Long revision, String failureCode) { return false; } - default void retryContextCompressionPreparation(OperationRecord operation, String owner, long retryAt) {} + public default void retryContextCompressionPreparation(OperationRecord operation, String owner, long retryAt) {} - default void awaitContextCompression(OperationRecord operation, String owner, + public default void awaitContextCompression(OperationRecord operation, String owner, boolean acceptanceKnown, String failureCode, long retryAt) {} /** @@ -503,7 +502,7 @@ public interface AgentStateStore { * * @return the outcome; a contested claim returns {@code null} */ - CwdChangeOutcome completeCwdChangeOperation(String tenantId, + public CwdChangeOutcome completeCwdChangeOperation(String tenantId, String sessionId, String operationId, String owner, long claimGeneration); @@ -514,12 +513,12 @@ public interface AgentStateStore { * @return false when the claim is no longer current — the write was * skipped and the caller must not report a terminal refusal */ - boolean failCwdChangeOperation(String tenantId, String sessionId, + public boolean failCwdChangeOperation(String tenantId, String sessionId, String operationId, String owner, long claimGeneration, String failureCode); /** The result of a settled cwd change. */ - record CwdChangeOutcome(boolean completed, String failureCode, + public record CwdChangeOutcome(boolean completed, String failureCode, Long resultContextRevision) { } @@ -532,14 +531,14 @@ public interface AgentStateStore { * {@code cancel} action ({@code 409 task_action_unavailable}) and no * other open operation ({@code 409 session_operation_active}). */ - default OperationAdmission beginTaskCancelOperation(String tenantId, + public default OperationAdmission beginTaskCancelOperation(String tenantId, String sessionId, String taskId, String actorDigest, String idempotencyKey, String requestDigest) { throw new UnsupportedOperationException("Task cancel is unavailable"); } /** Task cancels due for delivery: pending, or leased past their lease. */ - default List findDeliverableTaskCancels(int limit) { + public default List findDeliverableTaskCancels(int limit) { return List.of(); } @@ -552,7 +551,7 @@ public interface AgentStateStore { * * @return false when the claim is no longer current */ - default boolean settleTaskCancel(String tenantId, String sessionId, + public default boolean settleTaskCancel(String tenantId, String sessionId, String operationId, String owner, long claimGeneration, TaskCancelOutcome outcome, long retryAt) { throw new UnsupportedOperationException("Task cancel is unavailable"); @@ -563,7 +562,7 @@ public interface AgentStateStore { * acceptance is unknown, due for reconciliation. No claim ever takes * one; only the reconciler reads them. */ - default List findParkedOperations(int limit) { + public default List findParkedOperations(int limit) { return List.of(); } @@ -575,7 +574,7 @@ public interface AgentStateStore { * * @return false when the operation is no longer parked */ - default boolean settleParkedOperation(ParkedOperation parked, + public default boolean settleParkedOperation(ParkedOperation parked, AcceptanceVerdict verdict, long retryAt) { throw new UnsupportedOperationException( "Operation reconciliation is unavailable"); @@ -586,7 +585,7 @@ public interface AgentStateStore { * submission no Harness answered: committed once the journal holds its * prompt's admission, not committed once no writer can still commit it. */ - default AcceptanceVerdict turnInputAcceptance(String tenantId, + public default AcceptanceVerdict turnInputAcceptance(String tenantId, String sessionId, String operationId) { return AcceptanceVerdict.unknown(); } @@ -597,7 +596,7 @@ public interface AgentStateStore { * holds its revision's title, not committed once no writer can still * commit it. */ - default Optional renameAcceptance(ParkedOperation parked) { + public default Optional renameAcceptance(ParkedOperation parked) { return Optional.of(AcceptanceVerdict.unknown()); } @@ -608,14 +607,14 @@ public interface AgentStateStore { * * @return false when the title is no longer parked */ - default boolean settleParkedRename(ParkedOperation parked, + public default boolean settleParkedRename(ParkedOperation parked, AcceptanceVerdict verdict, long retryAt) { throw new UnsupportedOperationException( "Operation reconciliation is unavailable"); } /** A task cancel's recorded outcome: its public status and code. */ - record TaskCancelOutcome(String status, String failureCode) { + public record TaskCancelOutcome(String status, String failureCode) { public static TaskCancelOutcome completed() { return new TaskCancelOutcome("completed", null); } @@ -629,12 +628,12 @@ public interface AgentStateStore { } } - Optional findOperation(String tenantId, + public Optional findOperation(String tenantId, String sessionId, String operationId); - List findDeliverableOperations(long now, int limit); + public List findDeliverableOperations(long now, int limit); - Optional claimOperation(String tenantId, + public Optional claimOperation(String tenantId, String sessionId, String operationId, String owner, Duration leaseDuration); @@ -643,7 +642,7 @@ public interface AgentStateStore { * * @return false when the claim is no longer current */ - boolean completeOperation(String tenantId, String sessionId, + public boolean completeOperation(String tenantId, String sessionId, String operationId, String owner, long claimGeneration, boolean harnessConfirmed); @@ -658,11 +657,11 @@ public interface AgentStateStore { * * @return false when the claim is no longer current */ - boolean failOperation(String tenantId, String sessionId, + public boolean failOperation(String tenantId, String sessionId, String operationId, String owner, long claimGeneration, String failureCode); - void retryOperation(String tenantId, String sessionId, + public void retryOperation(String tenantId, String sessionId, String operationId, String owner, long claimGeneration, long availableAt); @@ -674,7 +673,7 @@ public interface AgentStateStore { * The attempt count still grows, so the dispatch backoff keeps * stretching. */ - default void retryOperation(String tenantId, String sessionId, + public default void retryOperation(String tenantId, String sessionId, String operationId, String owner, long claimGeneration, long availableAt, boolean budgetExempt) { if (budgetExempt) { @@ -692,92 +691,92 @@ public interface AgentStateStore { * write (review round 14, R14-3). The row stays leased; the watermark * only makes a re-claimed attempt read budgetExemptAttempt > 0. */ - void markOperationAnswered(String tenantId, String sessionId, + public void markOperationAnswered(String tenantId, String sessionId, String operationId, String owner, long claimGeneration); - Admission replayCommand(String tenantId, String operation, + public Admission replayCommand(String tenantId, String operation, String idempotencyKey, String requestDigest); - Optional findCommand(String tenantId, String operation, + public Optional findCommand(String tenantId, String operation, String idempotencyKey); - Optional findSessionById(String sessionId); + public Optional findSessionById(String sessionId); - SessionPage listSessions(String tenantId, String actorId, + public SessionPage listSessions(String tenantId, String actorId, Long beforeCreatedAt, String beforeSessionId, int limit); - SessionPage listSessions(String tenantId, String actorId, Long beforeCreatedAt, + public SessionPage listSessions(String tenantId, String actorId, Long beforeCreatedAt, String beforeSessionId, int limit, String archiveState); - SessionPage listSessions(String tenantId, String actorId, Long beforeCreatedAt, + public SessionPage listSessions(String tenantId, String actorId, Long beforeCreatedAt, String beforeSessionId, int limit, String archiveState, String titleQuery); - Map findLatestLifecycleOperations(String tenantId, List sessionIds); + public Map findLatestLifecycleOperations(String tenantId, List sessionIds); - Optional findTurn(String tenantId, String sessionId, + public Optional findTurn(String tenantId, String sessionId, String turnId); /** The active Turn of each given Session, in one round trip. */ - Map findActiveTurns(String tenantId, + public Map findActiveTurns(String tenantId, List sessionIds); /** The latest Turn of each given Session, in one round trip. */ - Map findLatestTurns(String tenantId, + public Map findLatestTurns(String tenantId, List sessionIds); - default Map findModelContexts( + public default Map findModelContexts( String tenantId, List sessionIds) { return Map.of(); } - default List modelOptions() { return List.of(); } + public default List modelOptions() { return List.of(); } - default Map findModelSelections( + public default Map findModelSelections( String tenantId, List sessionIds) { return Map.of(); } - default WebShellModelChangeResult changeSessionModel(String tenantId, String actorId, + public default WebShellModelChangeResult changeSessionModel(String tenantId, String actorId, String sessionId, String key, String digest, String modelId, long expectedRevision, boolean supported) { throw new UnsupportedOperationException("Model selection is unavailable"); } - default Map findApprovalSelections( + public default Map findApprovalSelections( String tenantId, List sessionIds) { return Map.of(); } - default boolean supportsPlanMode(SessionRecord session) { return false; } + public default boolean supportsPlanMode(SessionRecord session) { return false; } - default boolean hasMcpServers(SessionRecord session) { return false; } + public default boolean hasMcpServers(SessionRecord session) { return false; } - default SessionRecord requireMcpManagement(String tenantId, String actorId, String sessionId, String operationId) { + public default SessionRecord requireMcpManagement(String tenantId, String actorId, String sessionId, String operationId) { throw new UnsupportedOperationException("MCP management is unavailable"); } - default void requireMcpSettingsCommit(String tenantId, String workspaceId, String sessionId) { + public default void requireMcpSettingsCommit(String tenantId, String workspaceId, String sessionId) { throw new UnsupportedOperationException("MCP management is unavailable"); } - default SessionRecord requireExtensionManagement(String tenantId, String actorId, String sessionId, String operationId, boolean mutation) { + public default SessionRecord requireExtensionManagement(String tenantId, String actorId, String sessionId, String operationId, boolean mutation) { throw new UnsupportedOperationException("Extension management is unavailable"); } - default void requireConfigurationCommit(String tenantId, String workspaceId, String sessionId, JsonNode body) { + public default void requireConfigurationCommit(String tenantId, String workspaceId, String sessionId, JsonNode body) { throw new UnsupportedOperationException("Extension management is unavailable"); } - default Set findApprovalOwners(String tenantId, String actorId, + public default Set findApprovalOwners(String tenantId, String actorId, List sessionIds) { return Set.of(); } - default WebShellApprovalChangeResult changeSessionApproval(String tenantId, String actorId, + public default WebShellApprovalChangeResult changeSessionApproval(String tenantId, String actorId, String sessionId, String key, String digest, String mode, long expectedRevision, boolean supported, boolean agentDefinitionSupported) { throw new UnsupportedOperationException("Approval selection is unavailable"); } - default WebShellApprovalSelection findTurnApprovalSelection(String tenantId, String sessionId, + public default WebShellApprovalSelection findTurnApprovalSelection(String tenantId, String sessionId, String promptId) { return null; } - default WebShellApprovalChangeResult changeSessionApproval(String tenantId, String actorId, + public default WebShellApprovalChangeResult changeSessionApproval(String tenantId, String actorId, String sessionId, String key, String digest, String mode, long expectedRevision, boolean supported, boolean agentDefinitionSupported, Boolean planMode, boolean planModeSupported) { if (planMode != null) { @@ -787,7 +786,7 @@ public interface AgentStateStore { supported, agentDefinitionSupported); } - default WebShellApprovalChangeResult changeSessionApproval(String tenantId, String actorId, + public default WebShellApprovalChangeResult changeSessionApproval(String tenantId, String actorId, String sessionId, String key, String digest, String mode, long expectedRevision, boolean supported, boolean agentDefinitionSupported, Boolean planMode, boolean planModeSupported, boolean liveSupported, Map> permissionRules, boolean rulesSupported, @@ -799,10 +798,10 @@ public interface AgentStateStore { supported, agentDefinitionSupported, planMode, planModeSupported, liveSupported); } - default WebShellModelSelection findTurnModelSelection(String tenantId, String sessionId, + public default WebShellModelSelection findTurnModelSelection(String tenantId, String sessionId, String promptId) { return null; } - default WebShellApprovalChangeResult changeSessionApproval(String tenantId, String actorId, + public default WebShellApprovalChangeResult changeSessionApproval(String tenantId, String actorId, String sessionId, String key, String digest, String mode, long expectedRevision, boolean supported, boolean agentDefinitionSupported, Boolean planMode, boolean planModeSupported, boolean liveSupported) { @@ -814,7 +813,7 @@ public interface AgentStateStore { * The latest environment event of each Session's latest Turn, in one * round trip. */ - Map findLatestEnvironmentEvents(String tenantId, + public Map findLatestEnvironmentEvents(String tenantId, Map latestTurns); /** @@ -822,19 +821,19 @@ public interface AgentStateStore { * Turn ID, both descending. A position excludes the Turn it names and * every newer one. */ - TurnPage listTurns(String tenantId, String sessionId, + public TurnPage listTurns(String tenantId, String sessionId, Long beforeCreatedAt, String beforeTurnId, int limit); - Optional findTurnSummary(String tenantId, String sessionId, + public Optional findTurnSummary(String tenantId, String sessionId, String turnId); - List findEvents(String tenantId, String sessionId, + public List findEvents(String tenantId, String sessionId, long afterSequence, int limit); - List findControlEvents(String tenantId, String sessionId, + public List findControlEvents(String tenantId, String sessionId, long throughSequence); - EventPage findTranscriptEvents(String tenantId, String sessionId, + public EventPage findTranscriptEvents(String tenantId, String sessionId, Long beforeSequence, int limit); StoreModels.MessagePage findMessagePage(String tenantId, String sessionId, @@ -847,7 +846,7 @@ public interface AgentStateStore { Map findMessageCoveredSequences(String tenantId, List sessionIds); - ReplayWindow findReplayWindow(String tenantId, String sessionId); + public ReplayWindow findReplayWindow(String tenantId, String sessionId); /** The Sessions whose message projection covers more than their replay floor. */ List findReplayFloorTargets(int limit); @@ -855,12 +854,12 @@ public interface AgentStateStore { /** * Raises the Session's replay floor only through readable message coverage. */ - ReplayWindow advanceReplayFloor(String tenantId, String sessionId, + public ReplayWindow advanceReplayFloor(String tenantId, String sessionId, long floorSequence); - List findMaterializationTargets(int limit); + public List findMaterializationTargets(int limit); - MaterializationResult materializeNextBatch(String tenantId, + public MaterializationResult materializeNextBatch(String tenantId, String sessionId, int limit); /** @@ -868,24 +867,24 @@ public interface AgentStateStore { * touching only {@code gmt_modified} on its progress row — never * {@code covered_sequence}, which the gap guard must freeze on failure. */ - void deferMaterializationTarget(String tenantId, String sessionId); + public void deferMaterializationTarget(String tenantId, String sessionId); - List findDispatchable(long now, int limit); + public List findDispatchable(long now, int limit); /** * D7-2: active Turns parked on an unresolved Runtime execution whose * recheck is due. They are never dispatched; the reconciler drives a * fresh cancellation load instead. */ - List findRuntimeRecoveryParked(long now, int limit); + public List findRuntimeRecoveryParked(long now, int limit); - Optional claimTurn(String tenantId, String sessionId, + public Optional claimTurn(String tenantId, String sessionId, String turnId, String owner, Duration leaseDuration); - boolean renewTurn(String tenantId, String sessionId, String turnId, + public boolean renewTurn(String tenantId, String sessionId, String turnId, String owner, Duration leaseDuration); - void releaseTurnLease(String tenantId, String sessionId, String turnId, + public void releaseTurnLease(String tenantId, String sessionId, String turnId, String owner); /** @@ -897,7 +896,7 @@ public interface AgentStateStore { * backs off before read-only journal observation; it may still settle * from later authoritative evidence without replaying execution. */ - boolean markTurnRecoveryBlocked(String tenantId, String sessionId, + public boolean markTurnRecoveryBlocked(String tenantId, String sessionId, String turnId, String owner, String errorCode, String errorMessage); @@ -906,41 +905,41 @@ public interface AgentStateStore { * {@code retryAfter}, releasing the dispatch lease. The Turn keeps its * status: only a proven cancel may settle it. */ - boolean parkTurnForRuntimeRecovery(String tenantId, String sessionId, + public boolean parkTurnForRuntimeRecovery(String tenantId, String sessionId, String turnId, String owner, String message, long retryAfter); - boolean markTurnFailureRecovery(String tenantId, String sessionId, + public boolean markTurnFailureRecovery(String tenantId, String sessionId, String turnId, String owner, String message); - boolean requestRecoveryRecheck(String tenantId, String sessionId, + public boolean requestRecoveryRecheck(String tenantId, String sessionId, String turnId); - void deferTurnRetry(String tenantId, String sessionId, String turnId, + public void deferTurnRetry(String tenantId, String sessionId, String turnId, String owner, long retryAfter); - void scheduleTurnRetry(String tenantId, String sessionId, String turnId, + public void scheduleTurnRetry(String tenantId, String sessionId, String turnId, String owner, long retryAfter, boolean budgetExempt); - boolean bindHarness(String tenantId, String sessionId, String turnId, + public boolean bindHarness(String tenantId, String sessionId, String turnId, String owner, String harnessBootId); - boolean bindRecoveredHarness(String tenantId, String sessionId, + public boolean bindRecoveredHarness(String tenantId, String sessionId, String turnId, String owner, String expectedHarnessBootId, String harnessBootId); - void markSubmissionAttempted(String tenantId, String sessionId, + public void markSubmissionAttempted(String tenantId, String sessionId, String turnId, String owner); - boolean withdrawSubmissionAttempted(String tenantId, String sessionId, + public boolean withdrawSubmissionAttempted(String tenantId, String sessionId, String turnId, String owner); - boolean cancelUnadmittedTurn(String tenantId, String sessionId, + public boolean cancelUnadmittedTurn(String tenantId, String sessionId, String turnId, String owner); - void recordAdmission(String tenantId, String sessionId, String turnId, + public void recordAdmission(String tenantId, String sessionId, String turnId, String owner, String eventEpoch, long lastEventId); - void recordRecoveryAdmission(String tenantId, String sessionId, + public void recordRecoveryAdmission(String tenantId, String sessionId, String turnId, String owner, String expectedTurnEventEpoch, String expectedSessionEventEpoch, String eventEpoch, long lastEventId); @@ -949,7 +948,7 @@ public interface AgentStateStore { * Clears non-terminal text from a continuation epoch that did not reach * a public terminal event, so the replacement stream is the only copy. */ - void retractContinuationOutput(String tenantId, String sessionId, + public void retractContinuationOutput(String tenantId, String sessionId, String turnId, String owner, String harnessBootId, String eventEpoch); @@ -960,29 +959,29 @@ public interface AgentStateStore { * rebuilt, and a {@code stream.reconciled} event is appended. The Harness * cursor advances past {@code retractionSourceId} either way. */ - void retractHarnessTurnOutput(String tenantId, String sessionId, + public void retractHarnessTurnOutput(String tenantId, String sessionId, String turnId, String owner, String eventEpoch, long fromSourceId, long retractionSourceId); - void retractHarnessTurnOutput(String tenantId, String sessionId, + public void retractHarnessTurnOutput(String tenantId, String sessionId, String turnId, String owner, String eventEpoch, long fromSourceId, long retractionSourceId, String sourceBootId, String sourceEventEpoch, Long throughSourceId); - void recordHarnessEvents(String tenantId, String sessionId, + public void recordHarnessEvents(String tenantId, String sessionId, String turnId, String owner, String eventEpoch, List events); - void cancelBeforeAdmission(String tenantId, String sessionId, + public void cancelBeforeAdmission(String tenantId, String sessionId, String turnId, String owner); - void failTurn(String tenantId, String sessionId, String turnId, + public void failTurn(String tenantId, String sessionId, String turnId, String owner, String code, String message); - boolean failRecoveryBlockedTurn(String tenantId, String sessionId, + public boolean failRecoveryBlockedTurn(String tenantId, String sessionId, String turnId, String owner, String message); - void appendPublicEventIfAbsent(String tenantId, String sessionId, + public void appendPublicEventIfAbsent(String tenantId, String sessionId, String turnId, String type, Map data, boolean terminal, String sourceKey); @@ -992,11 +991,11 @@ public interface AgentStateStore { * The Session is locked before its status is read, so a deletion that * commits first is always seen. */ - void appendLiveSessionEventIfAbsent(String tenantId, String sessionId, + public void appendLiveSessionEventIfAbsent(String tenantId, String sessionId, String type, Map data, String sourceKey); - SessionRecord requireSession(String tenantId, String sessionId); + public SessionRecord requireSession(String tenantId, String sessionId); - Optional findLastVisibleStatusBeforeDelete(String tenantId, + public Optional findLastVisibleStatusBeforeDelete(String tenantId, String sessionId); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessConflictCodes.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/HarnessConflictCodes.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessConflictCodes.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/HarnessConflictCodes.java index 058e26100ee..85c66f2826d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessConflictCodes.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/HarnessConflictCodes.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.session; +package com.alibaba.qwen.code.managedagent.contracts.session; import java.util.Set; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ReplayCursorExpired.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/ReplayCursorExpired.java similarity index 82% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ReplayCursorExpired.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/ReplayCursorExpired.java index 50e8f965746..eabd4c9522e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ReplayCursorExpired.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/ReplayCursorExpired.java @@ -1,8 +1,7 @@ -package com.alibaba.qwen.code.managedagent.session; +package com.alibaba.qwen.code.managedagent.contracts.session; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.ReplayWindow; -import org.springframework.http.HttpStatus; import java.util.Map; @@ -14,8 +13,8 @@ import java.util.Map; public final class ReplayCursorExpired extends ManagedAgentException { private final transient ReplayWindow window; - ReplayCursorExpired(ReplayWindow window) { - super(HttpStatus.CONFLICT, "cursor_expired", + public ReplayCursorExpired(ReplayWindow window) { + super(409, "cursor_expired", "The replay cursor is older than the replay floor.", Map.of("replay_floor_sequence", window.floorSequence(), "snapshot_through_sequence", diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/UserMessagePolicy.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/UserMessagePolicy.java new file mode 100644 index 00000000000..562ad375b62 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/session/UserMessagePolicy.java @@ -0,0 +1,14 @@ +package com.alibaba.qwen.code.managedagent.contracts.session; + +import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; +import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; + +import java.util.List; +import java.util.Map; + +public interface UserMessagePolicy { + public boolean supportsUserMessageQueue(); + public SessionRecord authorizeUserMessageActor(String tenantId, String actorId, String sessionId); + public SessionRecord authorizeUserMessages(String tenantId, String actorId, String sessionId); + public List> userMessageInput(List blocks); +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceException.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ChildWorkspaceException.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceException.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ChildWorkspaceException.java index ee4fb753c64..a01341d1fb5 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceException.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ChildWorkspaceException.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.workspace; +package com.alibaba.qwen.code.managedagent.contracts.workspace; /** * A child Workspace step that could not complete (#13753 I1). A terminal @@ -48,11 +48,11 @@ public final class ChildWorkspaceException extends RuntimeException { return retryable; } - static ChildWorkspaceException layout(String message) { + public static ChildWorkspaceException layout(String message) { return new ChildWorkspaceException(LAYOUT, false, message); } - static ChildWorkspaceException diverged(String message) { + public static ChildWorkspaceException diverged(String message) { return new ChildWorkspaceException(DIVERGED, false, message); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceProvider.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ChildWorkspaceProvider.java similarity index 68% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceProvider.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ChildWorkspaceProvider.java index 3a04ced7e3e..b0bb8984aeb 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceProvider.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ChildWorkspaceProvider.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.workspace; +package com.alibaba.qwen.code.managedagent.contracts.workspace; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; @@ -8,7 +8,7 @@ import java.nio.file.Path; * The Workspace provider's child Workspace capability (#13753 I1): the * verified root of a binding's storage and the Git steps that run inside * it. Only a provider whose control plane mounts the storage answers one; - * {@link com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer#childWorkspaces()} is null everywhere else. + * {@link com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer#childWorkspaces()} is null everywhere else. */ public interface ChildWorkspaceProvider { /** @@ -16,7 +16,7 @@ public interface ChildWorkspaceProvider { * continuity check an acquisition makes. Refuses with the terminal * workspace_unavailable when the storage has no mount here. */ - Path storageRoot(ContextBinding binding); + public Path storageRoot(ContextBinding binding); - ChildWorktreeGit git(); + public ChildWorktreeGit git(); } diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ChildWorktreeGit.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ChildWorktreeGit.java new file mode 100644 index 00000000000..190653d0b13 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ChildWorktreeGit.java @@ -0,0 +1,71 @@ +package com.alibaba.qwen.code.managedagent.contracts.workspace; + +import java.nio.file.Path; +import java.util.List; +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; + +public interface ChildWorktreeGit { + public static final String CONTAINER = ".qwen-child-workspaces"; + + public static final String PIN_PREFIX = "refs/qwen/child-workspaces/"; + + public static final int MAX_CONFLICT_PATHS = 100; + + public record Repository(Path top, Path gitDir, Path commonDir) { + } + + public record Layout(String repositoryRelative, String offset) { + } + + public record Merge(String parentTree, String mergedTree, List conflicts) { + public boolean clean() { + return conflicts.isEmpty(); + } + } + + public String requireSupportedVersion(); + + public byte[] sessionCommand(Path directory, Path root, List arguments); + + public void close(); + + public static String childDirectory(String childWorkspaceId) { + return CONTAINER + "/" + childWorkspaceId; + } + + public static String childCwd(String childWorkspaceId, String offset) { + return ".".equals(offset) ? childDirectory(childWorkspaceId) + : childDirectory(childWorkspaceId) + "/" + offset; + } + + public Layout layout(Path root, String parentCwdRelative); + + public Layout sessionLayout(Path root, String cwdRelative); + + public Repository open(Path root, String repositoryRelative); + + public Repository sessionOpen(Path root, String repositoryRelative); + + public String snapshot(Path root, Repository repository, Path directory, String parent); + + public String base(Path root, Repository repository, String childWorkspaceId); + + public boolean hasDirectory(Path root, Repository repository, String commit, String offset); + + public void create(Path root, Repository repository, String childWorkspaceId, String base); + + public String result(Path root, Repository repository, String childWorkspaceId, String base); + + public Merge merge(Path root, Repository repository, String base, String result); + + public void apply(Path root, Repository repository, String parentTree, String mergedTree); + + public void discard(Path root, String repositoryRelative, String childWorkspaceId, boolean keepResult); + + public void pin(Path root, Repository repository, String childWorkspaceId, String name, String commit); + + public String pinned(Path root, Repository repository, String childWorkspaceId, String name); + + public T runWhileLive(BooleanSupplier live, Supplier work); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/LocalWorkspaceFiles.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/LocalWorkspaceFiles.java new file mode 100644 index 00000000000..ef45274a5d0 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/LocalWorkspaceFiles.java @@ -0,0 +1,13 @@ +package com.alibaba.qwen.code.managedagent.contracts.workspace; + +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; + +import java.util.Map; +public interface LocalWorkspaceFiles { + int MAX_UPLOAD_BYTES = 8 * 1024 * 1024; + Map readFiles(String node, String entry, Map request); + Map uploadFiles(String node, String entry, Map request); + static RuntimeBrokerException unsupported() { + return new RuntimeBrokerException(409, "workspace_files_unsupported", "Workspace file access requires an updated CLI.", false); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/PrivateCsiSession.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/PrivateCsiSession.java new file mode 100644 index 00000000000..d5617b37f34 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/PrivateCsiSession.java @@ -0,0 +1,21 @@ +package com.alibaba.qwen.code.managedagent.contracts.workspace; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +public final class PrivateCsiSession { + private PrivateCsiSession() { } + public record Request(WorkspaceCsiRegistration registration, String actorId, + String idempotencyKey, String requestedRevision, String title, WorkspaceSelection workspace) { + public Request { + if (registration == null || workspace == null || !".".equals(workspace.cwdRelative()) + || actorId == null || actorId.isEmpty() + || idempotencyKey == null || !idempotencyKey.matches("[\\x21-\\x7e]{1,128}") + || requestedRevision != null && (requestedRevision.isBlank() || requestedRevision.length() > 128) + || title != null && title.length() > 512) { + throw new IllegalArgumentException("Invalid private CSI Session creation request"); + } + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(registration.tenantId(), actorId); + } + } + + public record Created(String sessionId, String runtimeRequestKey, boolean replayed) { + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/RecoveryWorker.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/RecoveryWorker.java new file mode 100644 index 00000000000..4ea29729031 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/RecoveryWorker.java @@ -0,0 +1,15 @@ +package com.alibaba.qwen.code.managedagent.contracts.workspace; + +import com.fasterxml.jackson.databind.JsonNode; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.function.BiFunction; +public interface RecoveryWorker { + interface Session { + void workerFailed(); + JsonNode inspect(); + } + void execute(JsonNode request, Session session, BiFunction calls) throws Exception; + void validateStateDirectory(Path directory) throws IOException; +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ResidentReclamation.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ResidentReclamation.java new file mode 100644 index 00000000000..48ef1e5eab0 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ResidentReclamation.java @@ -0,0 +1,36 @@ +package com.alibaba.qwen.code.managedagent.contracts.workspace; + +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; + +import java.time.Instant; + +import static com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity.digest; + +public final class ResidentReclamation { + private ResidentReclamation() { } + public static final String DATA_POLICY = "retain-same-tenant-admin-handoff/1"; + + public record Baseline(String finalizer, String bootId, String nodeResourceVersion, String nodeHeartbeat, + String leaseUid, String leaseResourceVersion, String leaseRenewTime, String attachmentUid) { } + + public record Receipt(String resourceIdentity, String baselineDigest, String podResourceVersion, + String deletionTimestamp, String finishedAt, String nodeResourceVersion, String nodeHeartbeat, + String leaseResourceVersion, String leaseRenewTime, String observedAt, String dataPolicy) { } + + public static void verifyReceipt(RuntimeBindingRecord original, Baseline baseline, Receipt receipt) { + require(receipt != null && receipt.resourceIdentity().equals(original.getResourceHandle().getValue().get("identity")) + && receipt.baselineDigest().equals(digest(baseline)) && DATA_POLICY.equals(receipt.dataPolicy()) + && receipt.observedAt() != null && receipt.finishedAt() != null && receipt.deletionTimestamp() != null + && !Instant.parse(receipt.observedAt()).isBefore(Instant.parse(receipt.finishedAt()))); + Instant.parse(receipt.deletionTimestamp()); + } + private static void require(boolean valid) { + if (!valid) { + throw unavailable(); + } + } + private static RuntimeBrokerException unavailable() { + return new RuntimeBrokerException(409, "resident_reclamation_incomplete", "Original platform cleanup is incomplete.", false); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ResidentWorkspaceCatalog.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ResidentWorkspaceCatalog.java new file mode 100644 index 00000000000..99ce9acebac --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ResidentWorkspaceCatalog.java @@ -0,0 +1,65 @@ +package com.alibaba.qwen.code.managedagent.contracts.workspace; + +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker.ResidentWorkspace; +import com.alibaba.qwen.code.runtimebroker.ResidentWorkspaceProfile; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; + +import java.util.HashSet; +import java.util.List; +public class ResidentWorkspaceCatalog { + protected final List configured; + + public ResidentWorkspaceCatalog(List configured) { + this.configured = List.copyOf(configured); + var workspaces = new HashSet>(); + var aliases = new HashSet(); + var physical = new HashSet(); + for (var entry : configured) { + if (!workspaces.add(List.of(entry.registration().tenantId(), entry.workspaceId())) + || !aliases.add(entry.registration().aliasKey()) || !physical.add(entry.registration().physicalKey())) { + throw new IllegalArgumentException("Resident Workspace ownership is duplicated"); + } + } + } + + public ResidentWorkspace find(ContextBinding binding) { + if (binding == null) { + return null; + } + return configured.stream().filter(value -> value.registration().tenantId().equals(binding.getTenantId()) + && value.workspaceId().equals(binding.getWorkspaceId()) + && value.workspaceGeneration() == binding.getWorkspaceGeneration() + && value.registration().storageId().equals(binding.getStorageId()) + && WorkspaceExecutionProfile.CONTEXT_CONFIG_REF.equals(binding.getContextConfigRef())) + .findFirst().orElse(null); + } + + public ResidentWorkspace require(RuntimeScope scope) { + if (!ResidentWorkspaceProfile.selects(scope)) { + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); + } + return configured.stream().filter(value -> value.registration().tenantId().equals(scope.getTenantId()) + && value.workspaceId().equals(scope.getWorkspaceId()) + && Long.toString(value.workspaceGeneration()).equals(scope.getWorkspaceGeneration()) + && value.registration().mountRoot().equals(scope.getCanonicalCwd())) + .findFirst().orElseThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore::unavailable); + } + + public RuntimeProvisionRequest request(ContextBinding binding) { + var selected = find(binding); + if (selected == null) { + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); + } + return new RuntimeProvisionRequest(scope(selected), null, com.alibaba.qwen.code.managedagent.repository.WorkspaceCsiReservations.PROVISIONER_KIND, + selected.registration().storageId()); + } + + public static RuntimeScope scope(ResidentWorkspace selected) { + return new RuntimeScope(selected.registration().tenantId(), selected.workspaceId(), + Long.toString(selected.workspaceGeneration()), selected.registration().mountRoot(), + ResidentWorkspaceProfile.CAPABILITY_DIGEST, "workspace"); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ResidentWorkspaceSnapshot.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ResidentWorkspaceSnapshot.java new file mode 100644 index 00000000000..8ebc472e443 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/ResidentWorkspaceSnapshot.java @@ -0,0 +1,58 @@ +package com.alibaba.qwen.code.managedagent.contracts.workspace; + +import java.util.List; + +public record ResidentWorkspaceSnapshot( + String workspaceId, + long workspaceGeneration, + com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration + registration, + com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity protection, + String image, + List command, + List artifacts) + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker + .ResidentWorkspace { + public ResidentWorkspaceSnapshot { + if (registration == null + || protection == null + || workspaceId == null + || workspaceId.isBlank() + || workspaceGeneration < 1 + || image == null + || !image.matches("[^\\s]+@sha256:[0-9a-f]{64}") + || command == null + || command.isEmpty() + || command.size() > 32 + || command.stream() + .anyMatch( + value -> + value == null + || value.isBlank() + || value.length() > 8192 + || value.indexOf('\0') >= 0) + || artifacts == null + || artifacts.size() > 48 + || artifacts.stream().anyMatch(java.util.Objects::isNull) + || artifacts.stream() + .map( + com.alibaba.qwen.code.managedagent.contracts.runtime + .WorkerArtifact + ::configMapName) + .distinct() + .count() + != artifacts.size()) { + throw new IllegalArgumentException("Invalid resident Workspace configuration"); + } + new com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding( + registration.tenantId(), + workspaceId, + workspaceGeneration, + registration.storageId(), + ".", + com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, + 1); + command = List.copyOf(command); + artifacts = List.copyOf(artifacts); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeWarmer.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/RuntimeWarmer.java similarity index 64% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeWarmer.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/RuntimeWarmer.java index 9f5dad09fb9..ffe1928204d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeWarmer.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/RuntimeWarmer.java @@ -1,52 +1,50 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.contracts.workspace; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.workspace.ChildWorkspaceProvider; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import java.util.concurrent.CompletionStage; public interface RuntimeWarmer { - boolean isEnabled(); + public boolean isEnabled(); - CompletionStage warm(String sessionId); + public CompletionStage warm(String sessionId); - CompletionStage drain(String sessionId); + public CompletionStage drain(String sessionId); - default boolean supportsWorkspaceClose() { + public default boolean supportsWorkspaceClose() { return false; } - default boolean supportsWorkspaceClose(ContextBinding binding) { + public default boolean supportsWorkspaceClose(ContextBinding binding) { return supportsWorkspaceClose(); } - static boolean supportsClose(RuntimeWarmer warmer, ContextBinding binding) { + public static boolean supportsClose(RuntimeWarmer warmer, ContextBinding binding) { return warmer != null && warmer.supportsWorkspaceClose(binding); } - default void requestWorkspaceClose(String tenantId, String sessionId) { + public default void requestWorkspaceClose(String tenantId, String sessionId) { throw new UnsupportedOperationException("Workspace close is unavailable"); } - default CompletionStage closeWorkspace(String tenantId, String sessionId) { + public default CompletionStage closeWorkspace(String tenantId, String sessionId) { return java.util.concurrent.CompletableFuture.failedFuture( new UnsupportedOperationException("Workspace close is unavailable")); } - default CompletionStage drainWorkspaceResources(String tenantId, String sessionId, + public default CompletionStage drainWorkspaceResources(String tenantId, String sessionId, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority) { return java.util.concurrent.CompletableFuture.failedFuture( new UnsupportedOperationException("Workspace resource drain is unavailable")); } - default CompletionStage prepareWorkspaceResources(String tenantId, String sessionId, + public default CompletionStage prepareWorkspaceResources(String tenantId, String sessionId, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority) { return java.util.concurrent.CompletableFuture.failedFuture( new UnsupportedOperationException("Workspace resource preparation is unavailable")); } - default CompletionStage recoverStoppedWorkspaceResources(String tenantId, String sessionId, + public default CompletionStage recoverStoppedWorkspaceResources(String tenantId, String sessionId, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority) { return java.util.concurrent.CompletableFuture.failedFuture( new UnsupportedOperationException("Stopped Workspace recovery is unavailable")); @@ -60,12 +58,12 @@ public interface RuntimeWarmer { * gated on the workspace-files deployment shape, which requires one. * The refusal is a terminal workspace_unavailable, never a retry. */ - default void verifyWorkspaceCwdTarget(ContextBinding binding, + public default void verifyWorkspaceCwdTarget(ContextBinding binding, String targetCwdRelative) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } - default boolean supportsWorkspaceContextChange(ContextBinding binding) { + public default boolean supportsWorkspaceContextChange(ContextBinding binding) { return true; } @@ -74,7 +72,7 @@ public interface RuntimeWarmer { * or null when this host cannot create a child Workspace: it is off * by default and needs a control plane that mounts the storage. */ - default ChildWorkspaceProvider childWorkspaces() { + public default ChildWorkspaceProvider childWorkspaces() { return null; } } diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/SessionWorktreeOperations.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/SessionWorktreeOperations.java new file mode 100644 index 00000000000..9edd64e87fd --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/SessionWorktreeOperations.java @@ -0,0 +1,30 @@ +package com.alibaba.qwen.code.managedagent.contracts.workspace; + +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Plan; + +import java.nio.file.Path; + +public interface SessionWorktreeOperations extends AutoCloseable { + String CONTAINER = ".qwen-managed-worktrees"; + public record Source(String repositoryRelative, String offset, String commonDirectory, + String commonIdentity, String commit, java.util.List branches) { } + public interface Factory { + public SessionWorktreeOperations create(String git, java.time.Duration timeout); + } + public T runWhileLive(java.util.function.BooleanSupplier live, java.util.function.Supplier work); + public static com.alibaba.qwen.code.managedagent.exception.ManagedAgentException error(String code, String message) { + return new com.alibaba.qwen.code.managedagent.exception.ManagedAgentException(409, code, message); + } + + public Source source(Path root, String cwd, String baseRef); + + public String currentBranch(Path root, String cwd); + + public String prepare(Plan plan); + + public String cleanupBlocker(Plan plan, String savedIdentity); + + public void cleanup(Plan plan, String savedIdentity); + + public void close(); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/SshWorkspaceFiles.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/SshWorkspaceFiles.java new file mode 100644 index 00000000000..b34fe0ac765 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/SshWorkspaceFiles.java @@ -0,0 +1,12 @@ +package com.alibaba.qwen.code.managedagent.contracts.workspace; + +import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeNode; + +import java.util.Map; +public interface SshWorkspaceFiles { + record FileTarget(SshRuntimeNode node, Map request, String qwenVersion) { } + Map files(ContextBinding binding, Map files); + FileTarget freezeFiles(ContextBinding binding); + Map upload(FileTarget target, Map files); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/StorageIdentityReader.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/StorageIdentityReader.java new file mode 100644 index 00000000000..2eb9b25d21c --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/StorageIdentityReader.java @@ -0,0 +1,8 @@ +package com.alibaba.qwen.code.managedagent.contracts.workspace; + +import java.io.IOException; +import java.nio.file.Path; +public interface StorageIdentityReader { + record Identity(String root, String hostId, String device, String inode, String birthTime) { } + Identity read(Path root) throws IOException; +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorkspaceRecoveryProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorkspaceRecoveryProtocol.java new file mode 100644 index 00000000000..bd4f8250754 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorkspaceRecoveryProtocol.java @@ -0,0 +1,97 @@ +package com.alibaba.qwen.code.managedagent.contracts.workspace; + +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.codec.digest.DigestUtils; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.HexFormat; +import java.util.UUID; +public final class WorkspaceRecoveryProtocol { + private WorkspaceRecoveryProtocol() { } + private static final ObjectMapper JSON = new ObjectMapper(); + public static JsonNode parse(String text) { + return parse(text.getBytes(StandardCharsets.UTF_8)); + } + + public static JsonNode parse(byte[] bytes) { + check(bytes.length <= 16 * 1024 * 1024, "message_too_large"); + return ToolPublicationContract.readJson(bytes); + } + + public static String text(JsonNode node, String field) { + JsonNode value = node.path(field); + check(value.isTextual() && !value.asText().isEmpty() && value.asText().length() <= 8192, "invalid_request"); + return value.asText(); + } + + public static long positive(JsonNode node, String field) { + JsonNode value = node.path(field); + check(value.isIntegralNumber() && value.canConvertToLong() && value.asLong() > 0 + && value.asLong() <= ManagedSessionStoreModels.MAX_SAFE_COUNTER, "invalid_request"); + return value.asLong(); + } + + public static String uuid(JsonNode node, String field) { + String value = text(node, field); + try { + check(UUID.fromString(value).toString().equals(value), "invalid_request"); + } catch (IllegalArgumentException error) { + throw failure("invalid_request"); + } + return value; + } + + public static String hash(String value) { + return hash(value.getBytes(StandardCharsets.UTF_8)); + } + + public static String hash(byte[] bytes) { + return HexFormat.of().formatHex(digest().digest(bytes)); + } + + public static MessageDigest digest() { + try { + return DigestUtils.getSha256Digest(); + } catch (IllegalArgumentException error) { + throw new IllegalStateException(error); + } + } + + public static void check(boolean condition, String code) { + if (!condition) { + throw failure(code); + } + } + + // The deepest cause is not the verdict: a structural refusal keeps its + // service code even when the discarded I/O cause sits deeper in the chain. + public static String errorCode(Throwable error) { + for (Throwable current = error; current != null && current != current.getCause(); + current = current.getCause()) { + if (current instanceof RecoveryFailure failure) { + return failure.code; + } + if (current instanceof RuntimeBrokerException failure) { + return failure.getCode(); + } + } + return "migration_failed"; + } + + public static RecoveryFailure failure(String code) { + return new RecoveryFailure(code); + } + + public static final class RecoveryFailure extends IllegalStateException { + public final String code; + public RecoveryFailure(String code) { + super("Workspace recovery: " + code); + this.code = code; + } + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceSelection.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorkspaceSelection.java similarity index 89% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceSelection.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorkspaceSelection.java index 3eb35e5d059..b1ec9a31de9 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceSelection.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorkspaceSelection.java @@ -1,10 +1,9 @@ -package com.alibaba.qwen.code.managedagent.workspace; +package com.alibaba.qwen.code.managedagent.contracts.workspace; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceException; import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceRelativePath; import com.fasterxml.jackson.databind.JsonNode; -import org.springframework.http.HttpStatus; import java.util.regex.Pattern; @@ -59,12 +58,12 @@ public record WorkspaceSelection(String workspaceId, String cwdRelative) { } private static ManagedAgentException invalidRequest() { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", + return new ManagedAgentException(400, "invalid_request", "Workspace selection is invalid."); } private static ManagedAgentException invalidCwd() { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_cwd", + return new ManagedAgentException(400, "invalid_cwd", "Workspace directory must be a relative path."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceToolProfiles.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorkspaceToolProfiles.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceToolProfiles.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorkspaceToolProfiles.java index bb0184ed25a..cc9a2a1bb2a 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceToolProfiles.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorkspaceToolProfiles.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.workspace; +package com.alibaba.qwen.code.managedagent.contracts.workspace; import com.alibaba.qwen.code.managedagent.model.StoreModels; diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorktreeFiles.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorktreeFiles.java new file mode 100644 index 00000000000..cc1c691c74e --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorktreeFiles.java @@ -0,0 +1,10 @@ +package com.alibaba.qwen.code.managedagent.contracts.workspace; +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Plan; + +import java.nio.file.Path; +public interface WorktreeFiles { + public String verify(Plan plan, String savedIdentity); + public void requireRoot(Plan plan); + public String currentBranch(String verifiedIdentity); + public String identity(Path path); +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/exception/ManagedAgentException.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/exception/ManagedAgentException.java similarity index 68% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/exception/ManagedAgentException.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/exception/ManagedAgentException.java index 1acfe90ba71..0dea6c44afa 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/exception/ManagedAgentException.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/exception/ManagedAgentException.java @@ -1,19 +1,17 @@ package com.alibaba.qwen.code.managedagent.exception; -import org.springframework.http.HttpStatus; - import java.util.Map; public class ManagedAgentException extends RuntimeException { - private final HttpStatus status; + private final int status; private final String code; private final Map details; - public ManagedAgentException(HttpStatus status, String code, String message) { + public ManagedAgentException(int status, String code, String message) { this(status, code, message, Map.of()); } - public ManagedAgentException(HttpStatus status, String code, String message, + public ManagedAgentException(int status, String code, String message, Throwable cause) { this(status, code, message, Map.of(), cause); } @@ -22,12 +20,12 @@ public class ManagedAgentException extends RuntimeException { * Creates an error whose envelope carries {@code details} next to its code, * message and request id. */ - public ManagedAgentException(HttpStatus status, String code, String message, + public ManagedAgentException(int status, String code, String message, Map details) { this(status, code, message, details, null); } - private ManagedAgentException(HttpStatus status, String code, String message, + private ManagedAgentException(int status, String code, String message, Map details, Throwable cause) { super(message, cause); this.status = status; @@ -35,7 +33,7 @@ public class ManagedAgentException extends RuntimeException { this.details = Map.copyOf(details); } - public HttpStatus getStatus() { + public int getStatus() { return status; } diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessCapabilities.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessCapabilities.java new file mode 100644 index 00000000000..b9f9dd5be10 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessCapabilities.java @@ -0,0 +1,13 @@ +package com.alibaba.qwen.code.managedagent.harness.contracts; + +public record HarnessCapabilities(int currentProtocolVersion, java.util.List supportedProtocolVersions, String bootId, String instanceId, String capabilityDigest, int lifecycleProtocolVersion, java.util.List features, int titleProtocolVersion, java.util.List lifecycleV2Profiles) { + public int getCurrentProtocolVersion() { return currentProtocolVersion; } + public java.util.List getSupportedProtocolVersions() { return supportedProtocolVersions; } + public String getBootId() { return bootId; } + public String getInstanceId() { return instanceId; } + public String getCapabilityDigest() { return capabilityDigest; } + public int getLifecycleProtocolVersion() { return lifecycleProtocolVersion; } + public java.util.List getFeatures() { return features; } + public int getTitleProtocolVersion() { return titleProtocolVersion; } + public java.util.List getLifecycleV2Profiles() { return lifecycleV2Profiles; } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessCapabilityFailure.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessCapabilityFailure.java new file mode 100644 index 00000000000..28abecb7fd8 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessCapabilityFailure.java @@ -0,0 +1,20 @@ +package com.alibaba.qwen.code.managedagent.harness.contracts; + +public final class HarnessCapabilityFailure extends HarnessFailure { + private final String expectedDigest; + private final String actualDigest; + private final String code; + + public HarnessCapabilityFailure(String message, Throwable cause, String expectedDigest, String actualDigest, String code) { + super(message, cause); + this.expectedDigest = expectedDigest; + this.actualDigest = actualDigest; + this.code = code; + } + + public String getExpectedDigest() { return expectedDigest; } + + public String getActualDigest() { return actualDigest; } + + public String getCode() { return code; } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessCapacityFailure.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessCapacityFailure.java new file mode 100644 index 00000000000..e93c07af677 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessCapacityFailure.java @@ -0,0 +1,7 @@ +package com.alibaba.qwen.code.managedagent.harness.contracts; + +public final class HarnessCapacityFailure extends HarnessFailure { + public HarnessCapacityFailure(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessFailure.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessFailure.java new file mode 100644 index 00000000000..461b0fbcbdc --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessFailure.java @@ -0,0 +1,6 @@ +package com.alibaba.qwen.code.managedagent.harness.contracts; + +public class HarnessFailure extends RuntimeException { + public HarnessFailure(String message) { super(message); } + public HarnessFailure(String message, Throwable cause) { super(message, cause); } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessGenerationFailure.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessGenerationFailure.java new file mode 100644 index 00000000000..21dac98d9fb --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessGenerationFailure.java @@ -0,0 +1,20 @@ +package com.alibaba.qwen.code.managedagent.harness.contracts; + +public final class HarnessGenerationFailure extends HarnessFailure { + private final String expectedBootId; + private final String actualBootId; + private final String code; + + public HarnessGenerationFailure(String message, Throwable cause, String expectedBootId, String actualBootId, String code) { + super(message, cause); + this.expectedBootId = expectedBootId; + this.actualBootId = actualBootId; + this.code = code; + } + + public String getExpectedBootId() { return expectedBootId; } + + public String getActualBootId() { return actualBootId; } + + public String getCode() { return code; } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessHttpFailure.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessHttpFailure.java new file mode 100644 index 00000000000..04a01b25277 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessHttpFailure.java @@ -0,0 +1,24 @@ +package com.alibaba.qwen.code.managedagent.harness.contracts; + +import java.util.Map; + +public final class HarnessHttpFailure extends HarnessFailure { + private final int statusCode; + private final String responseBody; + private final Map bodyFields; + + public HarnessHttpFailure(String message, Throwable cause, int statusCode, String responseBody, Map bodyFields) { + super(message, cause); + this.statusCode = statusCode; + this.responseBody = responseBody; + this.bodyFields = Map.copyOf(bodyFields); + } + + public int getStatusCode() { return statusCode; } + + public String getResponseBody() { return responseBody; } + + public String getErrorCode() { return getBodyField("code"); } + + public String getBodyField(String name) { return bodyFields.get(name); } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessMutationUnknownFailure.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessMutationUnknownFailure.java new file mode 100644 index 00000000000..01b98836789 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessMutationUnknownFailure.java @@ -0,0 +1,10 @@ +package com.alibaba.qwen.code.managedagent.harness.contracts; + +public class HarnessMutationUnknownFailure extends HarnessFailure { + private final String operation; + public HarnessMutationUnknownFailure(String message, Throwable cause, String operation) { + super(message, cause); + this.operation = operation; + } + public String getOperation() { return operation; } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessProtocolFailure.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessProtocolFailure.java new file mode 100644 index 00000000000..f549895d4ea --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessProtocolFailure.java @@ -0,0 +1,7 @@ +package com.alibaba.qwen.code.managedagent.harness.contracts; + +public final class HarnessProtocolFailure extends HarnessFailure { + public HarnessProtocolFailure(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessRules.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessRules.java new file mode 100644 index 00000000000..19ffe3ee324 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessRules.java @@ -0,0 +1,10 @@ +package com.alibaba.qwen.code.managedagent.harness.contracts; + +import java.util.List; +import java.util.Map; + +public interface HarnessRules { + public String payloadDigest(List> input); + public Map> permissionRules(Map rules); + public Map> nonEmptyPermissionRules(Map> rules); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessRuntimeRecovery.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessRuntimeRecovery.java new file mode 100644 index 00000000000..a31a4eb1e8d --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessRuntimeRecovery.java @@ -0,0 +1,27 @@ +package com.alibaba.qwen.code.managedagent.harness.contracts; + +import java.util.List; +import java.util.Map; + +public record HarnessRuntimeRecovery(String phase, String checkpointId, String activationId, + List executions, boolean unknownOutcome, boolean continuationReady, boolean cancellationReady) { + public HarnessRuntimeRecovery { executions = List.copyOf(executions); } + public String getPhase() { return phase; } + public String getCheckpointId() { return checkpointId; } + public String getActivationId() { return activationId; } + public List getExecutions() { return executions; } + public boolean hasUnknownOutcome() { return unknownOutcome; } + public boolean isContinuationReady() { return continuationReady; } + public boolean isCancellationReady() { return cancellationReady; } + + public record Execution(String functionCallId, String toolName, String executionCallId, + String runtimeSessionId, String progressCursor, String outcome, Map status) { + public String getFunctionCallId() { return functionCallId; } + public String getToolName() { return toolName; } + public String getExecutionCallId() { return executionCallId; } + public String getRuntimeSessionId() { return runtimeSessionId; } + public String getProgressCursor() { return progressCursor; } + public String getOutcome() { return outcome; } + public Map getStatus() { return status; } + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessSessionRefusedFailure.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessSessionRefusedFailure.java new file mode 100644 index 00000000000..5d695424d70 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessSessionRefusedFailure.java @@ -0,0 +1,16 @@ +package com.alibaba.qwen.code.managedagent.harness.contracts; + +public final class HarnessSessionRefusedFailure extends HarnessFailure { + private final int statusCode; + private final String code; + + public HarnessSessionRefusedFailure(String message, Throwable cause, int statusCode, String code) { + super(message, cause); + this.statusCode = statusCode; + this.code = code; + } + + public int getStatusCode() { return statusCode; } + + public String getCode() { return code; } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessSettleAnsweredFailure.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessSettleAnsweredFailure.java new file mode 100644 index 00000000000..9aeef32c601 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessSettleAnsweredFailure.java @@ -0,0 +1,5 @@ +package com.alibaba.qwen.code.managedagent.harness.contracts; + +public class HarnessSettleAnsweredFailure extends HarnessFailure { + public HarnessSettleAnsweredFailure(String message, Throwable cause) { super(message, cause); } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessTransportFailure.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessTransportFailure.java new file mode 100644 index 00000000000..4ce6c9567fa --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/harness/contracts/HarnessTransportFailure.java @@ -0,0 +1,7 @@ +package com.alibaba.qwen.code.managedagent.harness.contracts; + +public final class HarnessTransportFailure extends HarnessFailure { + public HarnessTransportFailure(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/model/ApiModels.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/model/ApiModels.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/model/ApiModels.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/model/ApiModels.java index 7842c175bd6..f1fadb4ed75 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/model/ApiModels.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/model/ApiModels.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.model; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.Pin; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Pin; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -266,7 +266,7 @@ public final class ApiModels { compactionRevision, contextCompressionOperationId, value, worktree); } - public PublicSession withWorktree(com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore.Descriptor value) { + public PublicSession withWorktree(com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Descriptor value) { return new PublicSession(id, object, agentId, agentRevision, agentDefinitionDigest, status, createdAt, updatedAt, metadata, activeTurn, lastEventId, replayFloorSequence, @@ -685,7 +685,7 @@ public final class ApiModels { Long approvalRevision, String initialApprovalMode, boolean planMode, boolean planModeSupported, Long compactionRevision, String contextCompressionOperationId, WebShellLifecycleOperation lifecycleOperation, SessionRecovery recovery, - com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore.Descriptor worktree) { + com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Descriptor worktree) { public WebShellSession(String sessionId, String title, String agentId, String status, long createdAt, long updatedAt, WebShellTurn activeTurn, Object environment, long lastSequence, WebShellWorkspace workspace, WebShellSessionCapabilities capabilities, @@ -748,7 +748,7 @@ public final class ApiModels { lifecycleOperation, value, worktree); } - public WebShellSession withWorktree(com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore.Descriptor value) { + public WebShellSession withWorktree(com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Descriptor value) { return new WebShellSession(sessionId, title, agentId, status, createdAt, updatedAt, activeTurn, environment, lastSequence, workspace, capabilities, approvalMode, modelContext, diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/model/RequestDigests.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/model/RequestDigests.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/model/RequestDigests.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/model/RequestDigests.java index b049b1b295d..744659f5f56 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/model/RequestDigests.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/model/RequestDigests.java @@ -6,9 +6,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.json.JsonMapper; import org.apache.commons.codec.digest.DigestUtils; -import org.springframework.stereotype.Component; -@Component public class RequestDigests { private final ObjectMapper canonicalMapper; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/model/StoreModels.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/model/StoreModels.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/model/StoreModels.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/model/StoreModels.java index b2633799c7e..fffae7a963a 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/model/StoreModels.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/model/StoreModels.java @@ -1,8 +1,7 @@ package com.alibaba.qwen.code.managedagent.model; -import com.alibaba.qwen.code.managedagent.persistence.EventIdentity; -import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.EventIdentity; +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import java.util.Arrays; @@ -117,7 +116,7 @@ public final class StoreModels { } try { return Arrays.equals(actorKey, - ManagedWorkspaceRegistry.actorKey(tenantId, actorId)); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId)); } catch (IllegalArgumentException error) { return false; } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/model/TenantContext.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/model/TenantContext.java similarity index 76% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/model/TenantContext.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/model/TenantContext.java index 1526945bb6b..7e1b6f77432 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/model/TenantContext.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/model/TenantContext.java @@ -1,12 +1,11 @@ package com.alibaba.qwen.code.managedagent.model; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import org.springframework.http.HttpStatus; public record TenantContext(String tenantId, String actorId) { public String requireActorId() { if (actorId == null) { - throw new ManagedAgentException(HttpStatus.UNAUTHORIZED, + throw new ManagedAgentException(401, "actor_required", "A trusted actor is required."); } return actorId; diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/AutomationLedgerStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/AutomationLedgerStore.java new file mode 100644 index 00000000000..3e81f8d3db5 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/AutomationLedgerStore.java @@ -0,0 +1,184 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.commons.codec.digest.DigestUtils; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Optional; + +public interface AutomationLedgerStore { + public static final String STATE_LIVE = "live"; + + public static final String STATE_RETIRED = "retired"; + + public static final String OUTCOME_FIRING = "firing"; + + public static final String OUTCOME_FIRED = "fired"; + + public static final String OUTCOME_SKIPPED = "skipped"; + + public static final String OUTCOME_MISSED = "missed"; + + public static final String OUTCOME_UNKNOWN = "unknown"; + + public static final String GATE_DELIVERED = "delivered"; + + public static final String GATE_QUIET = "quiet"; + + public static final String GATE_FAILED = "failed"; + + public static final String GATE_BLOCKED = "blocked"; + + public static final String GATE_UNKNOWN = "unknown"; + + public record ScheduleRow(String tenantId, String scheduleId, + String sessionId, String workspaceId, String actorId, + long recordRevision, long definitionRevision, + String definitionDigest, String goal, String cron, String timezone, + String sessionMode, String overlap, String catchUp, + Long catchUpLimit, boolean enabled, String state, + String blockedReason, long armedAt, Long watermarkSlot, + String leaseOwner, Long leaseUntil, long fence, long createdAt, + long updatedAt, String gate, String gateLastOutcome, + Long gateLastAt, int gateQuietSinceDelivery, int gateFailures, + String gateLastError) { + } + + public record OccurrenceRow(String tenantId, String scheduleId, + String occurrenceKey, String runId, String sessionId, Long slot, + String trigger, String outcome, String reason, + long definitionRevision, long fence, int attempts, + long nextRetryAt, String lastError, long createdAt, + long updatedAt) { + /** A fresh decision of one occurrence of the definition as held. */ + public static OccurrenceRow decision(ScheduleRow definition, + String occurrenceKey, Long slot, String trigger, + String outcome, String reason, long fence, long now) { + return new OccurrenceRow(definition.tenantId(), + definition.scheduleId(), occurrenceKey, + automationRunId(definition.scheduleId(), occurrenceKey), + definition.sessionId(), slot, trigger, outcome, reason, + definition.definitionRevision(), fence, 0, 0L, null, now, + now); + } + } + + public record OccurrenceView(OccurrenceRow occurrence, String taskState) { + } + + public record CommandRow(String tenantId, String idempotencyKey, + String actorId, String requestDigest, String scheduleId, + String resultJson) { + } + + public record ScheduleCursor(long createdAt, String scheduleId) { + } + + public record SchedulePage(List rows, boolean hasMore) { + } + + public record OccurrenceCursor(long createdAt, String occurrenceKey) { + } + + public record OccurrencePage(List rows, boolean hasMore) { + } + + public Optional findSchedule(String tenantId, + String scheduleId); + + public void projectSchedule(String tenantId, String workspaceId, + String sessionId, long recordRevision, JsonNode record, long now); + + public boolean recordGateOutcome(ScheduleRow row, long definitionRevision, + String owner, long fence, String outcome, String detail, long now); + + public SchedulePage listReadableSchedules(String tenantId, String actorId, + ScheduleCursor cursor, int limit); + + public int countLive(String tenantId, String sessionId); + + public List findArmed(long now, int limit); + + public List findFiringSchedules(long now, int limit); + + public long claim(String tenantId, String scheduleId, String owner, + long leaseUntil, long now); + + public void release(String tenantId, String scheduleId, String owner, + long fence, long now); + + public boolean advanceWatermark(String tenantId, String scheduleId, + String owner, long fence, long slot, long now); + + public void block(String tenantId, String scheduleId, String owner, + long fence, String reason, long now); + + public void retire(String tenantId, String scheduleId, long now); + + public Optional recordOccurrence(OccurrenceRow row, + String owner); + + public Optional findOccurrence(String tenantId, + String scheduleId, String occurrenceKey); + + public boolean settleOccurrence(String tenantId, String scheduleId, + String occurrenceKey, String outcome, String reason, String owner, + long fence, long now); + + public boolean settleOccurrence(String tenantId, String scheduleId, + String occurrenceKey, String outcome, String reason, + String lastError, String owner, long fence, long now); + + public boolean repinOccurrence(String tenantId, String scheduleId, + String occurrenceKey, long definitionRevision, String owner, + long fence, long now); + + public boolean deferOccurrence(String tenantId, String scheduleId, + String occurrenceKey, int attempts, long nextRetryAt, + String lastError, String owner, long fence, long now); + + public List findFiring(String tenantId, String scheduleId, + long now); + + public int countActive(String tenantId, String scheduleId); + + public List findBlockingRuns(String tenantId, + String scheduleId, int limit); + + public boolean hasOpenTurn(String tenantId, String sessionId); + + public Optional findOccurrenceView(String tenantId, + String scheduleId, String occurrenceKey); + + public OccurrencePage listOccurrences(String tenantId, String scheduleId, + OccurrenceCursor cursor, int limit); + + public Optional findCommand(String tenantId, + String idempotencyKey); + + public void recordCommand(CommandRow command, long now); + + public boolean claimCommand(String sessionId, CommandRow command, long now); + + public boolean settleCommand(String tenantId, String idempotencyKey, + String requestDigest, String actorId, String resultJson); + + public boolean releaseCommand(String tenantId, String idempotencyKey, + String requestDigest, String actorId); + + public String sessionStatus(String tenantId, String sessionId); + + public static String automationRunId(String scheduleId, + String occurrenceKey) { + return "arun_" + sha256(scheduleId + '\0' + occurrenceKey); + } + + public static String automationInputId(String runId) { + return runId + ":input"; + } + + public static String sha256(String value) { + return DigestUtils.sha256Hex(value.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ChannelInstanceStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ChannelInstanceStore.java new file mode 100644 index 00000000000..bf3e10d1f51 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ChannelInstanceStore.java @@ -0,0 +1,92 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import java.util.List; +import java.util.Optional; + +public interface ChannelInstanceStore { + public static final List INSTANCE_STATES = List.of("connected", + "disconnected"); + + public record ChannelInstance(String tenantId, String channelId, + String platform, String accountId, long accountGeneration, + String state, String actorId, String workspaceId, + String cwdRelative, String policyJson, long createdAt, + long updatedAt) { + } + + public record InstanceCursor(long createdAt, String channelId) { + } + + public record InstancePage(List instances, + boolean hasMore) { + } + + public record ChannelBinding(String tenantId, String channelId, + String routeId, String sessionId, String scopeKind, + String senderId, String chatId, String threadId, + long createdAt) { + } + + public record ChannelClaim(String tenantId, String channelId, + String deliveryId, String sessionId, long claimedAt, long claimGeneration) { + } + + public record PendingDelivery(String sessionId, String deliveryId, + long revision, String deliveryState, String recordResourceId) { + } + + public record DeliveryRecordState(String deliveryState, + String recordResourceId) { + } + + public ChannelInstance register(ChannelInstance candidate); + + public Optional setState(String tenantId, + String channelId, String state); + + public Optional findInstance(String tenantId, + String channelId); + + public InstancePage listInstances(String tenantId, InstanceCursor before, + int limit); + + public Optional findBinding(String tenantId, + String channelId, String routeId); + + public ChannelBinding bind(ChannelBinding candidate); + + public List listSessionsWithPendingDeliveries(String tenantId, + String channelId, int limit); + + public Optional findDeliveryRecord(String tenantId, + String sessionId, String deliveryId); + + public List findPendingDeliveries(String tenantId, + String sessionId, int limit); + + public String readResource(String tenantId, String sessionId, String resourceId); + + public ChannelClaim claim(String tenantId, String channelId, + String deliveryId, String sessionId); + + public ChannelClaim touchClaim(String tenantId, String channelId, + String deliveryId); + + public Optional findClaim(String tenantId, String channelId, + String deliveryId); + + public static final String RECONCILE_OPERATION_PREFIX = "channel-reconcile:"; + + public boolean reserveReconciliation(ChannelClaim claim); + + public List findExpiredClaims(long before, int limit); + + public String sessionStatus(String tenantId, String sessionId); + + public long databaseNow(); + + public static String reconciliationOperationId(ChannelClaim claim) { + return RECONCILE_OPERATION_PREFIX + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( + claim.tenantId(), claim.channelId(), claim.deliveryId(), Long.toString(claim.claimGeneration())); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ChildResultRelayStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ChildResultRelayStore.java new file mode 100644 index 00000000000..a1298a20ef9 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ChildResultRelayStore.java @@ -0,0 +1,140 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.fasterxml.jackson.databind.JsonNode; + +import java.util.List; +import java.util.Set; + +public interface ChildResultRelayStore { + public record PendingChild(String tenantId, String parentSessionId, + String childRunId, long revision, String deliveryState, + String recordResourceId) { + } + + public record RelayRow(String tenantId, String parentSessionId, + String childRunId, String creationKey, String childSessionId, + String state, String claimedBy, long claimedUntil, int attempts, + long nextRetryAt, String lastError, long createdAt, + long updatedAt, long claimGeneration, String operationKind) { + public RelayRow(String tenantId, String parentSessionId, String childRunId, String creationKey, + String childSessionId, String state, String claimedBy, long claimedUntil, int attempts, + long nextRetryAt, String lastError, long createdAt, long updatedAt) { + this(tenantId, parentSessionId, childRunId, creationKey, childSessionId, state, claimedBy, + claimedUntil, attempts, nextRetryAt, lastError, createdAt, updatedAt, 0, "CHILD_SESSION_START"); + } + } + + public record TurnLine(String turnId, String status, Long completedAt, + String errorCode, boolean submissionAttempted, + String harnessEventEpoch) { + /** Whether this Turn proves a dispatch went out — the same pair + * the G3 submission machinery guards with. */ + public boolean dispatched() { + return submissionAttempted || harnessEventEpoch != null; + } + + /** Whether this Turn is the terminal pre-admission failure: an + * undispatched, terminally failed (or cancelled) admission. A + * live enqueued ACCEPTED Turn shares `!dispatched()` with it — + * only the terminal half may ever settle a never-started + * verdict (R24's runnable-Turn guard). */ + public boolean preAdmissionTerminal() { + return ("FAILED".equals(status) || "CANCELLED".equals(status)) + && !dispatched(); + } + } + + public List findPendingChildren(String workerId, int limit); + + public List findPendingChildren(String workerId, long now, + int limit); + + public String deliveryState(String tenantId, String parentSessionId, + String childRunId); + + public String executionState(String tenantId, String parentSessionId, + String childRunId); + + public record StopState(boolean stopRequested, boolean ended) { + } + + public StopState stopState(String tenantId, String parentSessionId, + String childRunId); + + public boolean turnCancelRequested(String tenantId, String sessionId, + String turnId); + + public String readResource(String tenantId, String sessionId, String resourceId); + + public byte[] readResourceBytes(String tenantId, String sessionId, String resourceId); + + public String readResource(String tenantId, String resourceId); + + public byte[] readResourceBytes(String tenantId, String resourceId); + + public record SettledTurn(String turnId, String outcome, String source, + long settledAt) { + } + + public record JournalTurns(int pendingMessageInputs, + Set messageInputs, long lastActivityAt, + SettledTurn lastSettled) { + } + + public record EdgeMessages(int undelivered, int toChild, + List receivedToChild) { + } + + public EdgeMessages edgeMessages(String tenantId, String parentSessionId, + String childRunId, String childSessionId); + + public boolean childOwesHandover(String tenantId, String childSessionId); + + public boolean hasSessionMessages(String tenantId, String sessionId); + + public JournalTurns journalTurns(String tenantId, String sessionId); + + public String journalTurnText(String tenantId, String sessionId, + String turnId); + + public JsonNode childRunBody(String tenantId, String parentSessionId, + String childRunId); + + public record LiveScope(String childRunId, String recordResourceId) { + } + + public List findLiveScopes(String tenantId, + String parentSessionId); + + public boolean hasAcceptance(String tenantId, String sessionId, + String childRunId); + + public String sessionStatus(String tenantId, String sessionId); + + public TurnLine latestTurn(String tenantId, String sessionId); + + public TurnLine firstTurn(String tenantId, String sessionId); + + public String terminalResultText(String tenantId, String sessionId, + String turnId); + + public RelayRow claim(String tenantId, String parentSessionId, String childRunId, String creationKey, + String owner, long leaseUntil, long now); + + public RelayRow find(String tenantId, String parentSessionId, String childRunId); + + public String findLineageChild(String tenantId, String parentSessionId, + String childRunId); + + public void advance(RelayRow row, String owner, String state, String childSessionId, long nextRetryAt, + String lastError, long leaseUntil, long now); + + public void retainCloseDebt(RelayRow row, String owner, String classification, String childSessionId, + long nextRetryAt, String lastError, long leaseUntil, long now); + + public void scheduleRetry(RelayRow row, String owner, long nextRetryAt, long leaseUntil, long now); + + public void defer(RelayRow row, String owner, long nextRetryAt, String lastError, long leaseUntil, long now); + + public void classify(RelayRow row, String owner, String state, String lastError, long now); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ChildWorkspaceStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ChildWorkspaceStore.java new file mode 100644 index 00000000000..fd399429687 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ChildWorkspaceStore.java @@ -0,0 +1,101 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import org.apache.commons.codec.digest.DigestUtils; + +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public interface ChildWorkspaceStore { + public static final String PREPARING = "preparing"; + + public static final String READY = "ready"; + + public static final String MERGING = "merging"; + + public static final String APPLYING = "applying"; + + public static final String APPLIED = "applied"; + + public static final String MERGED = "merged"; + + public static final String CONFLICTED = "conflicted"; + + public static final String DISCARDING = "discarding"; + + public static final String DISCARDED = "discarded"; + + public static final String FAILED = "failed"; + + public static final String BLOCKED = "blocked"; + + public static final String MERGE = "merge"; + + public static final String DISCARD = "discard"; + + public static final String DISCARD_PINNED = "discard_pinned"; + + public static final String WORKTREE = "worktree"; + + public static final String SNAPSHOT = "snapshot"; + + public record Row(String tenantId, String parentSessionId, String childRunId, + String childWorkspaceId, String workspaceId, long workspaceGeneration, String storageId, + String parentCwdRelative, String repositoryRelative, String childCwdRelative, + String baseCommit, String resultCommit, String parentTree, String mergedTree, + String state, String finishRequest, String outcomeCode, List conflictPaths, + String lastError, String claimedBy, Long claimedUntil, long claimGeneration, + int attempts, long nextRetryAt, long createdAt, long updatedAt, String workspaceMode) { + /** Whether the row is a read-only snapshot, whose every finish is a discard. */ + public boolean snapshot() { + return SNAPSHOT.equals(workspaceMode); + } + } + + public Row admit(String tenantId, String parentSessionId, String childRunId, + ContextBinding parent, long now); + + public Row admit(String tenantId, String parentSessionId, String childRunId, + ContextBinding parent, String workspaceMode, long now); + + public Row find(String tenantId, String parentSessionId, String childRunId); + + public List findDue(long now, int limit); + + public Row claim(Row row, String owner, long now, long leaseMillis); + + public boolean transition(Row claimed, String owner, String toState, Map columns, long now); + + public boolean startFinish(Row claimed, String owner, String toState, String finish, long now); + + public boolean childSessionsClosed(Row row); + + public boolean retry(Row claimed, String owner, boolean countAttempt, long nextRetryAt, String error, long now); + + public boolean renew(Row claimed, String owner, long until, long now); + + public void release(Row claimed, String owner, long now); + + public Row requestFinish(String tenantId, String parentSessionId, String childRunId, + String finish, long now); + + public static String childWorkspaceId(String tenantId, String parentSessionId, String childRunId) { + try { + return DigestUtils.sha256Hex( + (tenantId + "\u0000" + parentSessionId + "\u0000" + childRunId) + .getBytes(StandardCharsets.UTF_8)).substring(0, 32); + } catch (IllegalArgumentException error) { + throw new IllegalStateException("SHA-256 is unavailable", error); + } + } + + public static Map columns(Object... pairs) { + Map columns = new LinkedHashMap<>(); + for (int index = 0; index + 1 < pairs.length; index += 2) { + columns.put((String) pairs[index], pairs[index + 1]); + } + return columns; + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ExtensionPackageRepository.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ExtensionPackageRepository.java new file mode 100644 index 00000000000..690638eb92a --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ExtensionPackageRepository.java @@ -0,0 +1,28 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Operation; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Package; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Pin; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Settings; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.SettingsCommand; + +import java.util.List; +import java.util.Map; + +public interface ExtensionPackageRepository { + @FunctionalInterface + public interface Access { + public void require(boolean write); } + public List list(String tenant, String workspace); + public Package get(String tenant, String workspace, Pin pin); + public boolean hasSettings(String tenant, String workspace, Pin pin); + public Operation preparePublish(String tenant, String actor, String workspace, String extensionId, + String id, String digest, String operationId, String requestDigest, String encodedManifest, Access access); + public Operation finishPublish(String tenant, String actor, String workspace, String id, + String digest, String operationId, long length, Operation operation, Access access); + public Operation operation(String tenant, String actor, String workspace, String operationId, Access access); + public Settings settings(String tenant, String actor, String workspace, String extensionId, Access access); + public Operation saveSettings(String tenant, String actor, String workspace, String extensionId, + String operationId, String requestDigest, SettingsCommand command, byte[] plaintext, Access access); + public Map settingsValues(String tenant, String workspace, String extensionId, long revision); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/HarnessPlacementStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/HarnessPlacementStore.java new file mode 100644 index 00000000000..6900e114640 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/HarnessPlacementStore.java @@ -0,0 +1,32 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; + +import java.util.Map; + +public interface HarnessPlacementStore { + public static final String RELEASE_DOMAIN = "hosted_harness_release"; + + public record Placement(String tenantId, String sessionId, String workspaceId, + String instanceId, String bootId, long epoch, String state) { + } + + public Placement get(String tenant, String session); + + public boolean hasUnreleasedOwnership(String instance, String boot); + + public Map releaseReceipt(String tenant, String session, long epoch, String operationId); + + public Placement assign(String tenant, String session, String instance, String boot); + + public Placement assignForLifecycle(OperationRecord operation, String instance, String boot); + + public boolean quiesce(String tenant, String session, Placement expected); + + public Placement takeover(String tenant, String session, Placement expected, String instance, String boot); + + public Placement takeoverForLifecycle(OperationRecord operation, Placement expected, String instance, String boot); + public static String releaseId(long epoch) { + return "placement:" + epoch; + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/HostedCleanupDebtStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/HostedCleanupDebtStore.java new file mode 100644 index 00000000000..ef6f2971a36 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/HostedCleanupDebtStore.java @@ -0,0 +1,21 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import java.util.List; + +public interface HostedCleanupDebtStore { + public record CleanupDebt(String tenantId, String workspaceId, String sessionId, + String ownerKey, String promptId) { + } + + /** + * One advancing page. {@code nextCreate}/{@code nextOwnerKey} name the last + * raw row read (valid or not) so the next call resumes after it; + * {@code exhausted} is true when the query returned fewer rows than the + * limit, meaning the cursor has reached the end and should wrap. + */ + public record ScanPage(List debts, long nextCreate, String nextOwnerKey, + boolean exhausted) { + } + + ScanPage findOwed(int limit, long afterCreate, String afterOwnerKey); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedActionStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedActionStore.java new file mode 100644 index 00000000000..893febd671b --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedActionStore.java @@ -0,0 +1,90 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; +import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; +import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationTarget; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.codec.digest.DigestUtils; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public interface ManagedActionStore { + public record Action( + String id, + String state, + JsonNode options, + String decisionReceiptId, + String decisionDigest) {} + + public record Response( + String actionId, JsonNode body, String errorCode, String decisionReceiptId) {} + + public String approvalMode(String tenantId, String sessionId); + + public Optional find(String tenantId, String sessionId, String id); + + public record ScopedAction(String sessionId, Action action) { } + + public List listWithChildren(String tenant, String parent, Long before, + String beforeSession, String beforeId, int limit); + + public List list( + String tenantId, String sessionId, Long before, String beforeId, int limit); + + public OperationAdmission admit( + String tenantId, + String sessionId, + String actorId, + String actorDigest, + String key, + String requestDigest, + String actionId, + JsonNode body, + long now); + + public Optional publicTurnId(String tenantId, String sessionId, String promptId); + + public Response response(String tenantId, String sessionId, String op); + + public List deliverable(long now); + + public void settleEndedResponses(long now); + + public void complete( + OperationRecord op, + String owner, + String errorCode, + String decisionReceiptId, + boolean harnessConfirmed, + long now); + + public static String endedCode(String state) { + return switch (state) { + case "expired" -> "action_expired"; + case "cancelled" -> "action_cancelled"; + default -> "action_already_resolved"; + }; + } + + public static String decisionDigest(JsonNode response) { + Map decision = new LinkedHashMap<>(); + decision.put("v", 1); + decision.put("optionId", response.path("optionId").asText()); + decision.put("inputRevision", response.path("inputRevision").asLong()); + decision.put("policyRevision", response.path("policyRevision").asText()); + try { + return DigestUtils.sha256Hex(Codec.JSON.writeValueAsBytes(decision)); + } catch (Exception error) { + throw new IllegalStateException(error); + } + } + final class Codec { + private static final ObjectMapper JSON = new ObjectMapper(); + + private Codec() { } + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedAgentDefinitionStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedAgentDefinitionStore.java new file mode 100644 index 00000000000..06a21eb0e9b --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedAgentDefinitionStore.java @@ -0,0 +1,57 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; +import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; + +import java.util.Map; +import java.util.Optional; + +public interface ManagedAgentDefinitionStore { + public static final class ConcurrentWriteException + extends RuntimeException { + private final String code; + + public ConcurrentWriteException(String code, String message) { + super(message); + this.code = code; + } + + public String code() { + return code; + } + } + + public record DefinitionRevision(String agentId, long revision, + String digest, String definitionJson, long createdAt) { + } + + public record Admission(DefinitionRevision revision, boolean replayed) { + } + + public Admission create(String tenantId, String idempotencyKey, + String requestDigest, String agentId, String digest, + String definitionJson, long now); + + public Admission update(String tenantId, String idempotencyKey, + String requestDigest, String agentId, String digest, + String definitionJson, long now); + + public Map pinnedContent(SessionRecord session); + + public Optional latest(String tenantId, + String agentId); + + public Optional latestForUpdate(String tenantId, + String agentId); + + public Optional find(String tenantId, String agentId, + long revision); + + public Optional replayCommitted(String tenantId, + String idempotencyKey, String requestDigest); + + public static ManagedAgentException notFound() { + return new ManagedAgentException(404, "agent_not_found", + "The agent definition was not found."); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedArtifactReader.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedArtifactReader.java new file mode 100644 index 00000000000..fff54bc319d --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedArtifactReader.java @@ -0,0 +1,30 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.Artifact; + +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +public interface ManagedArtifactReader { + public boolean supported(); + + public boolean available(Artifact artifact); + + public Map availability(List artifacts); + + public InputStream open(Artifact artifact); + + public InputStream open(Artifact artifact, Runnable guard); + + public InputStream open(Artifact artifact, com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease, Runnable guard); + + public byte[] readRange(Artifact artifact, long offset, int length); + + public byte[] readRange(Artifact artifact, long offset, int length, Runnable guard); + + public byte[] readRange(Artifact artifact, long offset, int length, + com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease, Runnable guard); + + public com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease(Artifact artifact); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedExtensionRecordStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedExtensionRecordStore.java new file mode 100644 index 00000000000..e8ef51a15b7 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedExtensionRecordStore.java @@ -0,0 +1,114 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.TaskProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellModelContext; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.StreamReadConstraints; +import com.fasterxml.jackson.core.StreamReadFeature; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public interface ManagedExtensionRecordStore { + public com.fasterxml.jackson.databind.JsonNode currentMcpSettings(String tenant, String session); + + public static final String ERROR_MESSAGE_LINEAGE = + "session_message_lineage_refused"; + + public static final String ERROR_COMMAND_LINEAGE = + "session_command_lineage_refused"; + + public static final String ERROR_REJECTED = + "managed_session_extension_record_rejected"; + + public record TaskRow(String taskId, String kind, + TaskProjection projection, String resourceId) { + } + + public record TaskPage(List tasks, boolean hasMore) { + } + + public record TaskTarget(String domain, String recordId, String kind, + String state, long revision, JsonNode body) { + } + + public Optional findRecord(String tenantId, String sessionId, String domain, String recordId); + + public List listRecords(String tenantId, String sessionId, + String domain); + + public Map findModelContexts( + String tenantId, List sessionIds); + + public Optional latestHookRegistration(String tenantId, String sessionId); + + public Optional findTaskTarget(String tenantId, + String sessionId, String taskId); + + public JsonNode readRecordResource(String tenantId, String sessionId, + JsonNode ref); + + public StoredResource readCommittedResource(String tenantId, String sessionId, + JsonNode ref); + + public TaskPage listTasks(String tenantId, String sessionId, + Long beforeCreatedAt, String beforeTaskId, int limit); + + public Optional findTask(String tenantId, String sessionId, + String taskId); + + public record ChildTaskDetails(String sessionId, Boolean stopRequested, String approvalMode) { } + + public Long activeChildCount(String tenantId, String sessionId); + + public String childSessionId(String tenantId, String sessionId, TaskRow task); + + public ChildTaskDetails childTaskDetails(String tenantId, String sessionId, TaskRow task); + + public static JsonNode parse(String text) { + try { + JsonNode node = Codec.JSON.readTree(text); + return node != null && node.isObject() && finite(node) ? node + : null; + } catch (JsonProcessingException error) { + return null; + } + } + + public static boolean finite(JsonNode node) { + if (node.isNumber()) { + return Double.isFinite(node.doubleValue()); + } + for (JsonNode child : node) { + if (!finite(child)) { + return false; + } + } + return true; + } + final class Codec { + private static final ObjectMapper JSON = + JsonMapper.builder( + JsonFactory.builder() + .streamReadConstraints( + StreamReadConstraints.builder() + .maxNestingDepth( + ManagedSessionStoreModels + .MAX_JSON_DEPTH) + .build()) + .build()) + .enable(StreamReadFeature.STRICT_DUPLICATE_DETECTION) + .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) + .build(); + + private Codec() { } + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedSessionSkills.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedSessionSkills.java new file mode 100644 index 00000000000..5c8f819a233 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedSessionSkills.java @@ -0,0 +1,22 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; + +import java.util.List; +import java.util.Map; + +public interface ManagedSessionSkills { + public List> fromLaunchReference(String tenant, String session, + com.fasterxml.jackson.databind.JsonNode ref); + + public List> effective(SessionRecord session); + + public List> snapshot(String tenant, String session); + + public Map initialSelection(String tenant, String session); + + public void saveInitialSelection(String tenant, String session, + List pins); + + public void save(String tenant, String session, List> skills); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedSessionStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedSessionStore.java new file mode 100644 index 00000000000..74c878f6713 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedSessionStore.java @@ -0,0 +1,116 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.BlockRecoveryRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitReceipt; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.PublishToolResultRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RecoveryStateReceipt; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RenewWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RestoreHead; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealReceipt; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.ToolResultResourceRef; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.TransactionPage; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.WriterGrant; +import com.fasterxml.jackson.databind.JsonNode; + +import java.util.List; + +public interface ManagedSessionStore { + public void authorizeLifecycle(String tenantId, String sessionId, String writerToken, + ManagedSessionStoreModels.AuthorizeLifecycleRequest request, + com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority); + + public void authorizeOrdinary(String tenantId, String sessionId, String writerToken, + ManagedSessionStoreModels.AuthorizeLifecycleRequest request); + + public java.util.Map approvalSelection(String tenantId, String sessionId, String writerToken, + ManagedSessionStoreModels.AuthorizeLifecycleRequest request); + + public WriterGrant acquireWriter(String tenantId, String sessionId, + String writerToken, AcquireWriterRequest request); + + public WriterGrant acquireWriter(String tenantId, String sessionId, String writerToken, + AcquireWriterRequest request, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority); + + public WriterGrant renewWriter(String tenantId, String sessionId, + String writerToken, RenewWriterRequest request); + + public WriterGrant renewWriter(String tenantId, String sessionId, String writerToken, + RenewWriterRequest request, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority); + + public SealReceipt sealWriter(String tenantId, String sessionId, + String writerToken, SealWriterRequest request); + + public SealReceipt sealWriter(String tenantId, String sessionId, String writerToken, + SealWriterRequest request, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority); + + public void quiesceOwnership(String tenant, String session, String token, + ManagedSessionStoreModels.QuiesceOwnershipRequest request); + + public JsonNode releaseOwnership(String tenantId, String sessionId, String writerToken, + ManagedSessionStoreModels.ReleaseOwnershipRequest request); + + public RecoveryStateReceipt blockRecovery(String tenantId, + String sessionId, String writerToken, + BlockRecoveryRequest request); + + public CommitReceipt commit(String tenantId, String sessionId, + String writerToken, CommitTransactionRequest request); + + public CommitReceipt commit(String tenantId, String sessionId, String writerToken, + CommitTransactionRequest request, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority); + + public RestoreHead restore(String tenantId, String workspaceId, + String sessionId, String writerToken); + + public ManagedSessionStoreModels.RecentTransactionPage recentTransactions(String tenantId, String workspaceId, + String sessionId, String writerToken, Long throughRevision, Long beforeRevision, int limit); + + public ManagedSessionStoreModels.CommandTransaction commandTransaction(String tenantId, String workspaceId, + String sessionId, String writerToken, Long throughRevision, String operation, String commandId); + + public TransactionPage transactions(String tenantId, String workspaceId, + String sessionId, String writerToken, long afterRevision, + int limit); + + public boolean hasPristineInitialJournal(String tenantId, String sessionId, String workspaceId); + + public StoredResource readResource(String tenantId, String workspaceId, + String sessionId, String resourceId, String writerToken); + + public StoredResource readOwnerResource( + String tenantId, + String workspaceId, + String sessionId, + String resourceId, + String writerToken); + + public ToolResultResourceRef publishAgentInstructions(String tenantId, + String workspaceId, String sessionId, String instructions); + + public ToolResultResourceRef publishAgentSkills(String tenantId, + String workspaceId, String sessionId, List> skills); + + public ToolResultResourceRef publishInheritedExtensionResource(String tenantId, String workspaceId, String sessionId, StoredResource source); + + public ToolResultResourceRef publishToolResult(String tenantId, String sessionId, + String writerToken, PublishToolResultRequest request); + + public ToolResultResourceRef publishToolResult(String tenantId, String sessionId, + String writerToken, PublishToolResultRequest request, + com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority); + + public boolean hasLiveWriter(String tenantId, String sessionId); + + public static String sessionScopeKey(String tenantId, String sessionId) { + return com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(tenantId, sessionId); + } + + public static String resourceKey(String scopeKey, String resourceId) { + return com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scopeKey, resourceId); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedTaskEventStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedTaskEventStore.java new file mode 100644 index 00000000000..e165ba61e42 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedTaskEventStore.java @@ -0,0 +1,105 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.List; + +public interface ManagedTaskEventStore { + public static final int SCHEMA_VERSION = 1; + + public static final int PROJECTION_VERSION = 1; + + public static final int MAX_OUTPUT_CHARS = 16384; + + public static final int ARTIFACT_REF_BOUND = 100; + + public static final int RETAIN_TARGET = 256; + + public static final int BACKLOG_BOUND = 512; + + public static final String CODE_ADMISSION_REFUSED = + "managed_task_output_admission_refused"; + + public static final String CODE_FLOOR_PINNED = + "managed_task_event_floor_pinned"; + + public static final String CODE_BACKLOG_FULL = + "managed_task_event_backlog_full"; + + public static final String CODE_SEGMENT_CONFLICT = + "managed_task_event_segment_conflict"; + + public record TaskEvent(long sequence, String type, long occurredAt, + int schemaVersion, int projectionVersion, String state, + String runtimeState, String text, Boolean truncated, + String artifactId, String captureId, String streamId, + Long firstOrdinal, Long endOrdinal, boolean archived) { + } + + public record EventPage(List events, boolean hasMore) { + } + + public record CursorPositions(long lastSequence, long expiredThrough, + List artifactRefs) { + public static final CursorPositions EMPTY = new CursorPositions(0, 0, + List.of()); + } + + public CursorPositions positions(String tenantId, String sessionId, String taskId); + + public EventPage read(String tenantId, String sessionId, String taskId, long after, int limit); + + public void appendStateChange(String tenantId, String sessionId, String taskId, String state, + String runtimeState, long occurredAt); + + public long appendOutput(String tenantId, String sessionId, String taskId, String text, boolean truncated, + String captureId, String streamId, Long firstOrdinal, Long endOrdinal, long occurredAt); + + public long appendArtifact(String tenantId, String sessionId, String taskId, String artifactId, long occurredAt); + + public int markOutputArchived(String tenantId, String sessionId, String taskId, long throughSequence); + + public long expireThrough(String tenantId, String sessionId, String taskId, long throughSequence); + + public static String encodeCursor(String tenantId, String sessionId, String taskId, long sequence) { + if (sequence < 0) { + throw new IllegalArgumentException("A task cursor requires a nonnegative sequence"); + } + return Base64.getUrlEncoder().withoutPadding().encodeToString( + ("tev2:" + ManagedSchemaKeys.key(tenantId, sessionId, taskId) + ":" + sequence) + .getBytes(StandardCharsets.US_ASCII)); + } + + public static long decodeCursor(String tenantId, String sessionId, String taskId, String cursor) { + if (cursor == null || cursor.length() > 128) { + throw invalidCursor(); + } + String decoded; + try { + decoded = new String(Base64.getUrlDecoder().decode(cursor), StandardCharsets.US_ASCII); + } catch (IllegalArgumentException error) { + throw invalidCursor(); + } + String[] parts = decoded.split(":", -1); + if (parts.length != 3 || !"tev2".equals(parts[0]) + || !ManagedSchemaKeys.key(tenantId, sessionId, taskId).equals(parts[1])) { + throw invalidCursor(); + } + try { + long position = Long.parseLong(parts[2]); + if (position < 0) { + throw new NumberFormatException(); + } + return position; + } catch (NumberFormatException error) { + throw invalidCursor(); + } + } + + public static ManagedAgentException invalidCursor() { + return new ManagedAgentException(400, "invalid_event_cursor", "The task event cursor is invalid."); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedToolDetailsStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedToolDetailsStore.java new file mode 100644 index 00000000000..346431aeb41 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedToolDetailsStore.java @@ -0,0 +1,8 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellToolDetails; + +public interface ManagedToolDetailsStore { + public WebShellToolDetails read(String tenant, String workspace, + String session, String itemId); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedToolResultStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedToolResultStore.java new file mode 100644 index 00000000000..83344a9207e --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedToolResultStore.java @@ -0,0 +1,61 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Optional; + +public interface ManagedToolResultStore { + public record Source(String id, String tenantId, String workspaceId, String sessionId, + String executionCallId, long journalRevision, long receiptSequence, + JsonNode outcomeRef, JsonNode resultRef, JsonNode resources, String sourceDigest) { + public JsonNode sessionKey() { + return Codec.JSON.createObjectNode().put("tenantId", tenantId).put("workspaceId", workspaceId) + .put("sessionId", sessionId); + } + } + + public record Claim(Source source, long generation) { } + + public record Artifact(JsonNode descriptor, Source source, String publicationId, + JsonNode binding, JsonNode manifestRef, String streamId, long creationSequence) { } + + public record ArtifactPage(List artifacts, long watermark, boolean hasMore) { } + + public record Projection(JsonNode descriptor, String publicationId, JsonNode binding, + JsonNode manifestRef, List artifacts, String policyVersion) { } + + public Optional claim(); + + public void fail(Claim claim, String state, String code); + + public void verifySource(Source source); + + public boolean complete(Claim claim, Projection projection, String currentPolicyVersion); + + public Optional findResult(String tenant, String session, String itemId); + + public Optional findArtifact(String tenant, String session, String artifactId); + + public ArtifactPage listArtifacts(String tenant, String session, Long watermark, Long beforeSequence, + String beforeId, int limit); + + public static String identity(String prefix, String... values) { + var digest = new java.io.ByteArrayOutputStream(); + for (String value : values) { + byte[] bytes = value.getBytes(StandardCharsets.UTF_8); + digest.writeBytes(ByteBuffer.allocate(4).putInt(bytes.length).array()); + digest.writeBytes(bytes); + } + return prefix + "_" + ToolPublicationContract.sha256(digest.toByteArray()); + } + final class Codec { + private static final ObjectMapper JSON = new ObjectMapper(); + + private Codec() { } + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedUserMessageStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedUserMessageStore.java new file mode 100644 index 00000000000..7f6efcfe5cb --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedUserMessageStore.java @@ -0,0 +1,36 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; + +import java.util.List; +import java.util.Map; + +public interface ManagedUserMessageStore { + public record Message(String id, long sequence, long revision, String state, + List> input, String turnId) { } + + public record Snapshot(boolean paused, List messages) { } + + public record Command(String action, String messageId, Long expectedRevision, List input) { } + + public Snapshot queryMessages(String tenant, String actor, String session); + + public Snapshot command(String tenant, String actor, String session, String key, Command command); + + public List claim(String tenant, String session, String promptId, boolean initialOnly); + + public List> readySessions(); + + public void attempted(String tenant, String session); + + @FunctionalInterface + public interface QueuedTurnSubmitter { + public com.alibaba.qwen.code.managedagent.model.ApiModels.CommandAdmission submit(String tenantId, + String actorId, String idempotencyKey, String sessionId, + List input); + } + + public void startNext(String tenant, String session, QueuedTurnSubmitter submitter); + + public void retireClosed(); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedWorkspaceRegistry.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedWorkspaceRegistry.java new file mode 100644 index 00000000000..bf45cca2a4f --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ManagedWorkspaceRegistry.java @@ -0,0 +1,60 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceAccess; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceActor; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +public interface ManagedWorkspaceRegistry { + public boolean createdSession(String tenantId, String actorId, String sessionId); + + public boolean isSessionOwner(String tenantId, String actorId, String sessionId); + + public WorkspaceAccess accessOf(String tenantId, String actorId, + String workspaceId); + + public boolean canRead(String tenantId, String actorId, + String workspaceId); + + public List listReadable(String tenantId, + String actorId, String afterId, int limit); + + public WorkspaceSummary findReadable(String tenantId, String actorId, + String workspaceId); + + public java.util.Map findReadable( + String tenantId, String actorId, + java.util.Collection workspaceIds); + + public WorkspaceSummary readableDefault(String tenantId, + String actorId); + + public boolean bindingCurrent(String tenantId, String workspaceId, + long generation, String storageId); + + public record WorkspaceSummary(String workspaceId, String displayName, + String state, boolean canCreateSession, String defaultCwdRelative, boolean canSelectDirectory) { + public WorkspaceSummary(String workspaceId, String displayName, String state, boolean canCreateSession) { + this(workspaceId, displayName, state, canCreateSession, ".", false); + } + } + + public record ReadableGrant(String workspaceId, long workspaceGeneration, + String storageId, boolean canCreateSession) { + } + + public ResolvedBinding resolveForCreation(String tenantId, + String actorId, WorkspaceSelection selection); + + public record ResolvedBinding(ContextBinding binding, String configRef, + String policyRef) { + } + + public static byte[] actorKey(String tenantId, String actorId) { + return new WorkspaceActor(tenantId, actorId).getActorId() + .getBytes(StandardCharsets.UTF_8); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ResidentRetirementRepository.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ResidentRetirementRepository.java new file mode 100644 index 00000000000..956705320a5 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/ResidentRetirementRepository.java @@ -0,0 +1,83 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentReclamation; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker.ResidentWorkspace; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.spi.KubernetesMaintenanceClient; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +public interface ResidentRetirementRepository { + record Request( + String tenantId, + String workspaceId, + long workspaceGeneration, + String actorId, + String retirementId) { + public Request { + for (String value : new String[] {tenantId, workspaceId, actorId, retirementId}) { + require(value != null && !value.isBlank() && value.getBytes(StandardCharsets.UTF_8).length <= 512 + && value.codePoints().noneMatch(point -> point < 32 || point >= 127 && point <= 159 + || point >= 0xd800 && point <= 0xdfff)); + } + require(workspaceGeneration > 0); + } + } + + record AttemptStatus( + int ordinal, + String resourceVersion, + String state, + Integer httpStatus, + String responseSha256) {} + + record FinalizerAttemptStatus( + int ordinal, + String action, + String resourceVersion, + String state, + Integer httpStatus, + String responseSha256) {} + + record Status( + String retirementId, + String state, + String bindingId, + long runtimeGeneration, + List attempts, + List finalizerAttempts, + Completion completion) {} + + record Completion(String receiptSha256, String dataPolicy, Long releasedRevision) {} + + record Attempt(String workKey, String owner, long generation, byte[] body) {} + + void completeDeletion(Request request, ResidentWorkspace selected, Attempt attempt, KubernetesMaintenanceClient.DeleteResponse response); + Attempt beginDeletion(Request request, ResidentWorkspace selected, String workKey, String resourceVersion, byte[] body); + void recordBaseline(Request request, ResidentWorkspace selected, ResidentReclamation.Baseline observed, String finalizer); + Status release(Request request, ResidentWorkspace selected, ResidentReclamation.Receipt proof); + void recordCleanup(Request request, ResidentWorkspace selected, ResidentReclamation.Baseline baseline, ResidentReclamation.Receipt observed, RuntimeBindingRecord original); + void completeFinalizer(Request request, ResidentWorkspace selected, Attempt attempt, KubernetesMaintenanceClient.DeleteResponse reply); + Attempt beginFinalizer(Request request, ResidentWorkspace selected, RuntimeBindingRecord original, String finalizer, String action, Map metadata, String resourceVersion, byte[] body); + Status inspect(Request request); + Status begin(Request request); + ResidentWorkspace selected(Request request); + RuntimeBindingRecord original(Request request, ResidentWorkspace selected); + Map loadState(Request request); + boolean hasDeletionAttempt(Request request, String workKey); + boolean hasDeletionAttempts(Request request); + int planVersion(Request request, Map state); + ResidentReclamation.Baseline baseline(Map saved); + ResidentReclamation.Receipt receipt(Map saved, RuntimeBindingRecord original); + private static void require(boolean valid) { + if (!valid) { + throw new RuntimeBrokerException( + 409, + "resident_retirement_unavailable", + "Original idle Workspace maintenance is unavailable.", + false); + } + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SessionMessageRelayStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SessionMessageRelayStore.java new file mode 100644 index 00000000000..52c7a803aba --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SessionMessageRelayStore.java @@ -0,0 +1,65 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import java.util.List; + +public interface SessionMessageRelayStore { + public static final int MAX_ATTEMPTS = 64; + + public static final String LEDGER_OWES_WORK = "(l.delivery_id IS NULL" + + " OR (l.transport_state NOT IN ('done', 'orphaned', 'unknown')" + + " AND l.attempt_count < " + MAX_ATTEMPTS + "))"; + + public record PendingMessage(String tenantId, String senderSessionId, + String messageId, String domain, String deliveryState, + String recordResourceId) { + } + + public record MessageRow(String tenantId, String senderSessionId, + String messageId, String targetSessionId, String state, + String claimedBy, long claimedUntil, int attempts, + long nextRetryAt, String lastError, long claimGeneration) { + public MessageRow(String tenantId, String senderSessionId, String messageId, String targetSessionId, + String state, String claimedBy, long claimedUntil, int attempts, long nextRetryAt, String lastError) { + this(tenantId, senderSessionId, messageId, targetSessionId, state, claimedBy, claimedUntil, + attempts, nextRetryAt, lastError, 0); + } + } + + public record Lineage(String parentSessionId, String parentChildRunId) { + } + + public record CurrentRecord(String deliveryState, + String recordResourceId) { + } + + public List findPendingMessages(String workerId, long now, + int limit); + + public CurrentRecord currentRecord(String tenantId, String sessionId, + String domain, String messageId); + + public String deliveryState(String tenantId, String sessionId, + String domain, String messageId); + + public Lineage lineage(String tenantId, String sessionId); + + public boolean olderOpenCommand(String tenantId, String senderSessionId, + String commandId); + + public List olderOwedMessages(String tenantId, + String senderSessionId, String messageId); + + public MessageRow claim(String tenantId, String senderSessionId, + String messageId, String owner, long leaseUntil, long now); + + public MessageRow find(String tenantId, String senderSessionId, String messageId); + + public void advance(MessageRow row, String owner, String state, String targetSessionId, + long nextRetryAt, long leaseUntil, long now); + + public void scheduleRetry(MessageRow row, String owner, long nextRetryAt, long leaseUntil, long now); + + public void defer(MessageRow row, String owner, long nextRetryAt, String lastError, long leaseUntil, long now); + + public void classify(MessageRow row, String owner, String state, String lastError, long now); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SessionRecoveryRepository.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SessionRecoveryRepository.java new file mode 100644 index 00000000000..cd8f9c4eb9d --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SessionRecoveryRepository.java @@ -0,0 +1,9 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import java.util.List; + +public interface SessionRecoveryRepository { + public record HeadRecovery(String state, String reasonCode) { } + public List heads(String tenantId, String sessionId); + public long nonInitialTurns(String tenantId, String sessionId); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SessionWorktreeStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SessionWorktreeStore.java new file mode 100644 index 00000000000..b05dc5acbe9 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SessionWorktreeStore.java @@ -0,0 +1,107 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; +import com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.ResolvedBinding; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; + +import java.nio.file.Path; +import java.util.List; +import java.util.Locale; + +public interface SessionWorktreeStore { + public static final String KIND = "SESSION_WORKTREE"; + + public record Plan(String tenantId, String workspaceId, long workspaceGeneration, String storageId, + String sourceCwdRelative, String contextConfigRef, long contextRevision, String root, String rootIdentity, + String repositoryRelative, String sourceOffset, String commonDirectory, String commonIdentity, + String baseRef, String baseCommit, String worktreeId, String worktreeRoot, String cwdRelative, String branch, + String storageIdentity) { + public Plan(String tenantId, String workspaceId, long workspaceGeneration, String storageId, + String sourceCwdRelative, String contextConfigRef, long contextRevision, String root, String rootIdentity, + String repositoryRelative, String sourceOffset, String commonDirectory, String commonIdentity, + String baseRef, String baseCommit, String worktreeId, String worktreeRoot, String cwdRelative, String branch) { + this(tenantId, workspaceId, workspaceGeneration, storageId, sourceCwdRelative, contextConfigRef, contextRevision, + root, rootIdentity, repositoryRelative, sourceOffset, commonDirectory, commonIdentity, baseRef, + baseCommit, worktreeId, worktreeRoot, cwdRelative, branch, null); + } + public ContextBinding source() { + return new ContextBinding(tenantId, workspaceId, workspaceGeneration, storageId, sourceCwdRelative, + contextConfigRef, contextRevision); + } + + public ResolvedBinding target(ResolvedBinding source) { + if (!source().equals(source.binding())) { + throw error("worktree_source_changed"); + } + return new ResolvedBinding(new ContextBinding(tenantId, workspaceId, workspaceGeneration, storageId, + cwdRelative, source.binding().getContextConfigRef(), 1), source.configRef(), source.policyRef()); + } + } + + public record Descriptor(String id, String sessionId, String workspaceId, String state, String branch, + String baseCommit, String cwdRelative, String operationId, long revision, String errorCode) { + public String worktreeId() { + return id; + } + } + + public record Row(String tenantId, String sessionId, String workspaceId, String worktreeId, String state, + Plan plan, String identityJson, String operationId, String claimedBy, long generation, + Long claimUntil, String phase, int attempts, long revision, String errorCode, String actor) { + public Descriptor descriptor() { + return descriptor(plan.branch()); + } + + public Descriptor descriptor(String branch) { + return new Descriptor(worktreeId, sessionId, workspaceId, state.toLowerCase(Locale.ROOT), branch, + plan.baseCommit(), plan.cwdRelative(), operationId, revision, errorCode); + } + } + + public record SourceLocation(ResolvedBinding resolved, Path root, String storageIdentity) { } + + public SourceLocation source(String tenant, String actor, com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection selection); + + public void authorizeOptions(String tenant, String actor, String workspace); + + public boolean active(Row row); + + public Descriptor describe(String tenant, String session); + + public void admit(String tenant, String actor, String session, Plan plan, ResolvedBinding source); + + public Row get(String tenant, String actor, String workspace, String id, boolean write); + + public List list(String tenant, String actor, String workspace); + + public List due(); + + public List failedInitialSessions(); + + public Row claim(Row row, String owner, long leaseMillis); + + public boolean renew(Row row, String owner, long leaseMillis); + + public boolean complete(Row row, String owner, String state, String identity, String error); + + public void defer(Row row, String owner, String error); + + public void retry(Row row, String owner, String error); + + public void resumeStopped(); + + public boolean maintenanceHeld(Row row); + + public Row cleanup(String tenant, String actor, String workspace, String id, String idempotencyKey); + + public String cleanupBlocker(Row row); + + public static ContextBinding storageBinding(Plan plan) { + return new ContextBinding(plan.tenantId(), plan.workspaceId(), plan.workspaceGeneration(), plan.storageId(), + ".", plan.contextConfigRef(), plan.contextRevision()); + } + + public static ManagedAgentException error(String code) { + return new ManagedAgentException(409, code, "The Session worktree cannot perform this operation (" + code + ")."); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SshWorkspaceStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SshWorkspaceStore.java new file mode 100644 index 00000000000..449073b6cec --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/SshWorkspaceStore.java @@ -0,0 +1,84 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.model.StoreModels; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeStorage; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public interface SshWorkspaceStore { + public static final TypeReference> MAP = new TypeReference<>() { }; + + public static final String KIND = "SSH_LOCAL"; + + public static final String OPERATION = "WORKSPACE_CREATE"; + + public record Operation(String operationId, String type, String status, + String workspaceId, String failureCode, boolean replayed) { + } + + public record Intent(String operationId, String tenantId, String actorId, + String nodeId, String path, String displayName, String workspaceId, + String storageId, String registrationId, Map registration) { + } + + public record Parked(Intent intent, String failureCode) { + public boolean reserved() { + return !intent.registration().isEmpty(); + } + } + + public Operation begin(String tenant, String actor, String key, + String node, String path, String name); + + public Operation get(String tenant, String actor, String id); + + public Operation replay(String tenant, String actor, String key, + String node, String path, String name); + + public Intent claim(String tenant, String actor, String id, String owner); + + public void reserve(Intent intent, String owner, Map registration); + + public void complete(Intent intent, String owner); + + public void fail(Intent intent, String owner, String code, boolean retryable); + + public List findParked(int limit); + + public java.util.Optional parked(String tenant, String operationId); + + public boolean completeParked(String tenant, String operationId); + + public boolean releaseParked(String tenant, String operationId, String code); + + public boolean reparkParked(String tenant, String operationId, long delayMillis); + + public SshRuntimeStorage runtimeStorage(RuntimeScope scope); + + public SshRuntimeStorage runtimeStorage(String tenant, String storage); + + public boolean verify(ContextBinding binding); + + public static Map object(Map value, String key) { + return Codec.JSON.convertValue(Objects.requireNonNull(value.get(key), key), MAP); + } + + public static String string(Map value, String key) { + if (!(value.get(key) instanceof String text) || text.isBlank()) { + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); + } + return text; + } + final class Codec { + private static final ObjectMapper JSON = new ObjectMapper(); + + private Codec() { } + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/TransportConsumerStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/TransportConsumerStore.java new file mode 100644 index 00000000000..3f6018ed96a --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/TransportConsumerStore.java @@ -0,0 +1,30 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import java.util.List; +import java.util.Optional; +import java.util.OptionalLong; +import java.util.Set; + +public interface TransportConsumerStore { + public record WakeFence(String sessionStatus, String workspaceId, + String headState, long committedSequence, boolean liveWriter) { + /** Whether a wake may act: an ACTIVE Session with an ACTIVE or SEALED journal head. */ + public boolean open() { + return "ACTIVE".equals(sessionStatus) + && ("ACTIVE".equals(headState) || "SEALED".equals(headState)); + } + } + + public Optional wakeFence(String tenantId, String sessionId); + + public Set heldBy(String writerId, String tenantId, + List sessionIds); + + public boolean claimWake(String tenantId, String sessionId, + long sequence); + + public OptionalLong materializedThrough(String tenantId, + String sessionId); + + public Optional workspaceOf(String tenantId, String sessionId); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WakeRecoveryStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WakeRecoveryStore.java new file mode 100644 index 00000000000..013384f8ce9 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WakeRecoveryStore.java @@ -0,0 +1,19 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import java.util.List; +import java.util.Optional; + +public interface WakeRecoveryStore { + public record Candidate(String tenantId, String workspaceId, String sessionId, + long journalRevision, long committedSequence, long compactedThroughRevision, + String parentSessionId, String parentChildRunId) { + } + + public List findPending(String defaultWorkspace, int limit); + + public boolean claim(Candidate candidate); + + public Optional hasPendingInput(Candidate candidate); + + public boolean recordPending(Candidate candidate, boolean pending); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WorkspaceCsiReservations.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WorkspaceCsiReservations.java new file mode 100644 index 00000000000..078b4b9ac57 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WorkspaceCsiReservations.java @@ -0,0 +1,19 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker.ResidentWorkspace; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +public interface WorkspaceCsiReservations { + String PROVISIONER_KIND = "kubernetes-workspace"; + public record Reservation(String phase, long revision, String reservationId, + String registrationKey, Long registrationRevision, String bindingId, + Long runtimeGeneration, String provisionRequestId) { + } + + Reservation inspect(WorkspaceCsiRegistration registration); + Reservation reserve(WorkspaceCsiRegistration registration, RuntimeBindingRepository bindings, + RuntimeBindingRecord original, String reservationId); + boolean usesSameDataSource(RuntimeBindingRepository bindings); + void requireRegistration(ResidentWorkspace selected); +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WorkspaceExecutionStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WorkspaceExecutionStore.java new file mode 100644 index 00000000000..af5c1be7120 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WorkspaceExecutionStore.java @@ -0,0 +1,82 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; + +public interface WorkspaceExecutionStore { + public java.nio.file.Path createdStorageRoot(String tenantId, String storageId); + + public boolean verifiedRecoveryEnabled(); + + public void authorize(SessionRecord session); + + public void authorizeLifecycle(SessionRecord session, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority); + + public void authorizeLegacyClose(SessionRecord session); + + public void verifyMountForProbe(ContextBinding binding); + + public void authorizePassiveAttachment(SessionRecord session); + + public void authorizeCancellation(SessionRecord session); + + public void authorizeRecoveryCancellation(SessionRecord session, + String turnId, String dispatchOwner); + + public void authorizeCommittedStop(SessionRecord session); + + public void authorizeActionResponse(SessionRecord session); + + public void claim(ContextBinding binding, RuntimeSessionRecord session); + + public boolean beginRewind(ContextBinding binding, RuntimeSessionRecord session); + + public void endRewind(ContextBinding binding, RuntimeSessionRecord session); + + public boolean hasExclusiveClaim(RuntimeSessionRecord session); + + public void assertHeld(ContextBinding binding, RuntimeSessionRecord session); + + public boolean isHeld(ContextBinding binding, RuntimeSessionRecord session); + + public void release(ContextBinding binding, RuntimeSessionRecord session); + + public String holdForMaintenance(ContextBinding binding, String maintenanceId, + long claimGeneration); + + public void releaseMaintenance(ContextBinding binding, String maintenanceId, + long claimGeneration, String executionId); + + public int releaseStaleMaintenance(long now, long resumableGraceMillis); + + public boolean hasHolder(RuntimeBindingRecord saved); + + public boolean canStopDrained(RuntimeBindingRecord saved); + + public void releaseLost(RuntimeBindingRecord saved); + + public static RuntimeBrokerException unavailable() { + return new RuntimeBrokerException(409, "workspace_unavailable", + "Workspace execution authority is unavailable.", false); + } + + public static RuntimeBrokerException unavailable(Throwable cause) { + return new RuntimeBrokerException(409, "workspace_unavailable", + "Workspace execution authority is unavailable.", false, cause); + } + + public static RuntimeBrokerException unavailableTransient( + Throwable cause) { + return new RuntimeBrokerException(409, "workspace_unavailable", + "Workspace mount cannot be verified right now.", true, + cause); + } + + public static RuntimeBrokerException unavailablePendingGrant() { + return new RuntimeBrokerException(409, "workspace_unavailable", + "Workspace access grant or registry state is changing.", true); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WorkspaceLifecycleStore.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WorkspaceLifecycleStore.java new file mode 100644 index 00000000000..300f5c6f24f --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/repository/WorkspaceLifecycleStore.java @@ -0,0 +1,28 @@ +package com.alibaba.qwen.code.managedagent.repository; + +import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; +import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority; +import com.fasterxml.jackson.databind.JsonNode; + +public interface WorkspaceLifecycleStore { + public JsonNode recoverEffects(OperationRecord operation); + + public void saveEffects(OperationRecord operation, JsonNode receipt); + + public void verifyCompletion(OperationRecord operation); + + public void requireSettledExecutions(OperationRecord operation); + + public void verifyOutputContents(OperationRecord operation); + + public void verifyResources(OperationRecord operation); + + public static RuntimeLifecycleAuthority authority(OperationRecord operation) { + return new RuntimeLifecycleAuthority(operation.operationId(), operation.claimGeneration()); + } + + public static ManagedAgentException blocked(String code) { + return new ManagedAgentException(409, code, "Workspace lifecycle recovery is blocked."); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/settings/ManagedAgentSettings.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/settings/ManagedAgentSettings.java new file mode 100644 index 00000000000..61cb38b0b34 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/settings/ManagedAgentSettings.java @@ -0,0 +1,228 @@ +package com.alibaba.qwen.code.managedagent.settings; + +import java.time.Duration; +import java.util.List; +import java.util.Map; + +public interface ManagedAgentSettings { + public Harness getHarness(); + public boolean admitsPublicChildAgents(); + public Automation getAutomation(); + public Channels getChannels(); + public EventTransport getEventTransport(); + public SessionStore getSessionStore(); + public ToolPublication getToolPublication(); + public Artifacts getArtifacts(); + public ManagedExtensions getManagedExtensions(); + public Extensions getExtensions(); + public Dispatch getDispatch(); + public Events getEvents(); + public RuntimeBroker getRuntimeBroker(); + public String getAgentRevision(); + public List getDefinitionToolProfiles(); + public List getDefinitionModels(); + public List getModelCatalog(); + public interface ModelOption { + public String getModelId(); + public String getLabel(); + } + public interface Harness { + public HarnessPool getPool(); + public Duration getApprovalTimeout(); + public boolean isEnabled(); + public boolean isWorkspaceFilesEnabled(); + public boolean isWorkspaceChildAgentsEnabled(); + public boolean isWorkspaceShellEnabled(); + public String getBaseUrl(); + public String getToken(); + public String getCapabilityDigest(); + public String getCredentialsFile(); + public String getApprovalMode(); + public Duration getLoadTimeout(); + public Duration getConnectTimeout(); + public Duration getRequestTimeout(); + public Duration getHeartbeatInterval(); + public Duration getTurnDeadline(); + } + public interface HarnessPool { + public boolean isEnabled(); + public String getDiscovery(); + public List getInstances(); + public String getKeyStoreFile(); + public String getKeyStorePasswordFile(); + public String getTrustStoreFile(); + public String getTrustStorePasswordFile(); + public String getKubernetesApiUrl(); + public String getKubernetesTokenFile(); + public String getKubernetesCaFile(); + public String getNamespace(); + public String getServiceName(); + public String getServiceUid(); + public String getPortName(); + } + public interface HarnessInstance { + public String getInstanceId(); + public String getBaseUrl(); + } + public interface SessionStore { + public boolean isEnabled(); + public String getBaseUrl(); + public String getWorkspaceId(); + public Duration getWriterLeaseDuration(); + public String getBindingKey(); + public boolean isAllowInsecureHttp(); + } + public interface ToolPublication { + public boolean isAsyncVerificationEnabled(); + public int getVerificationConcurrency(); + public boolean isGcEnabled(); + public Duration getDeletionGrace(); + public boolean isEnabled(); + public String getOssEndpoint(); + public String getOssRegion(); + public String getOssBucket(); + public String getServiceBaseUrl(); + public Long getExecutionBytes(); + public Long getSessionBytes(); + public Long getTenantBytes(); + public Long getActiveCaptures(); + public Integer getEntryConcurrency(); + public Duration getOperationTimeout(); + public Duration getClaimTimeout(); + public Long getVerificationBytesPerSecond(); + public Duration getMaxVerificationTimeout(); + } + public interface ManagedExtensions { + public boolean isEnabled(); + public String getMode(); + public String getCacheDirectory(); + public String getCgroupRoot(); + } + public interface Extensions { + public boolean isEnabled(); + public String getArtifactDirectory(); + public List getAllowedActors(); + } + public interface Artifacts { + public boolean isEnabled(); + public boolean isPublishOriginal(); + public boolean isPublishPreview(); + public int getMaxConcurrentReads(); + public Duration getReadTimeout(); + public Duration getReadRevalidationInterval(); + } + public interface Dispatch { + public Duration getLeaseDuration(); + public Duration getLeaseRenewInterval(); + public Duration getRetryInitialDelay(); + public Duration getRetryMaxDelay(); + public int getMaxPreAdmissionRetries(); + public int getMaxPostAdmissionRetries(); + public int getMaxOperationRetries(); + } + public interface Events { + public Duration getPollInterval(); + public Duration getReadGrantRecheckInterval(); + public Duration getHeartbeatInterval(); + public Duration getStreamTimeout(); + public Duration getBatchInterval(); + public Duration getMaterializeInterval(); + public int getBatchMaxEvents(); + public int getBatchMaxBytes(); + public boolean isReplayFloorEnabled(); + } + public interface RuntimeBroker { + public SshSettings getSsh(); + public Retention getRetention(); + public interface Retention { + public boolean isEnabled(); + public Duration getMaxAge(); + public int getBatchSize(); + public Duration getScanDelay(); + } + public boolean isEnabled(); + public String getHost(); + public int getPort(); + public boolean isAllowNonLoopback(); + public Duration getV3ResultWindow(); + public String getToken(); + public String getProvisioner(); + public String getWorkspaceId(); + public String getWorkspaceGeneration(); + public String getWorkspaceCwd(); + public String getIsolationClass(); + public String getBoundSessionIsolationClass(); + public boolean isSessionCreateWarm(); + public List getWorkspaceMounts(); + public String getWorkspaceCreationRoot(); + public boolean isLocalDirectoryImportEnabled(); + public interface WorkspaceMount { + public String tenantId(); + public String storageId(); + public String root(); + } + public interface ResidentWorkspace { + public String workspaceId(); + public long workspaceGeneration(); + public com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration registration(); + public com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity protection(); + public String image(); + public List command(); + public List artifacts(); + } + public List getResidentWorkspaces(); + public String getResidentKubernetesApiUrl(); + public String getResidentKubernetesTokenFile(); + public String getResidentKubernetesCaFile(); + public boolean isDurableLocalProcess(); + public boolean isTrustedLocalRebootRecovery(); + public boolean isOperatorRecoveryEnabled(); + public boolean isVerifiedWorkspaceRecoveryEnabled(); + public boolean isChildWorkspacesEnabled(); + public boolean isSessionWorktreesEnabled(); + public boolean isChildSnapshotsEnabled(); + public String getChildWorkspaceGit(); + public Duration getChildWorkspaceGitTimeout(); + public String getStateDirectory(); + public String getCredentialKeyId(); + public String getCredentialKey(); + public Map getPreviousCredentialKeys(); + public String getNodeExecutable(); + public String getWorkerEntry(); + public String getStaticEndpoint(); + public String getStaticToken(); + public String getExternalProviderUrl(); + public String getExternalProviderToken(); + public String getExternalProviderId(); + public Duration getExternalRequestTimeout(); + public Duration getExternalStartupTimeout(); + public String getStaticRuntimeInstanceId(); + public String getStaticLeaseId(); + public long getStaticEpoch(); + public Map getEnvironment(); + } + public interface Automation { + public boolean isEnabled(); + public Duration getScanDelay(); + public Duration getLease(); + public Duration getLateTolerance(); + public Duration getLookback(); + public int getMaxSlotsPerTick(); + public int getConcurrency(); + public int getScannerThreads(); + } + public interface Channels { + public boolean isEnabled(); + public Duration getClaimLease(); + } + public interface EventTransport { + public String getCapabilityProbeGroup(); + public String getWakeBootstrapLiteTopic(); + public String getWakeBootstrapGroup(); + public String getOwnerGroup(); + public Duration getOwnerReconcileInterval(); + public boolean isEnabled(); + public String getEndpoints(); + public String getTopic(); + } +} diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/settings/SshSettings.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/settings/SshSettings.java new file mode 100644 index 00000000000..c88528d9935 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/managedagent/settings/SshSettings.java @@ -0,0 +1,28 @@ +package com.alibaba.qwen.code.managedagent.settings; + +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeNode; + +import java.util.List; + +public interface SshSettings { + public boolean isEnabled(); + public List getNodes(); + public interface Access { + public String tenantId(); + public String actorId(); + } + public interface Node { + public String nodeId(); + public String displayName(); + public String sshHost(); + public String nodeExecutable(); + public String qwenEntry(); + public String stateDirectory(); + public List allowedRoots(); + public int publicationPort(); + public String qwenVersion(); + public List access(); + public boolean admits(String tenantId, String actorId); + public SshRuntimeNode runtimeNode(); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/BrokerValues.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/BrokerValues.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/BrokerValues.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/BrokerValues.java index e9b663c55cc..3cbbe0bf60c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/BrokerValues.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/BrokerValues.java @@ -43,7 +43,7 @@ public final class BrokerValues { * every Runtime Session through that envelope, so an id the worker would * refuse must not be acquired. */ - static String requirePathSafe(String value, String name) { + public static String requirePathSafe(String value, String name) { if (value == null || !PATH_SAFE_ID.matcher(value).matches() || value.equals(".") || value.contains("..")) { throw new IllegalArgumentException(name diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiFilesRetirementProfile.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiFilesRetirementProfile.java similarity index 88% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiFilesRetirementProfile.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiFilesRetirementProfile.java index 6799959b926..639afb5a1dd 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiFilesRetirementProfile.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiFilesRetirementProfile.java @@ -1,6 +1,7 @@ package com.alibaba.qwen.code.runtimebroker; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; + import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -51,4 +52,12 @@ public final class CsiFilesRetirementProfile { throw new IllegalStateException("SHA-256 unavailable", impossible); } } + public static boolean isProfile(RuntimeScope scope) { + return scope != null && CAPABILITY_DIGEST.equals(scope.getCapabilityDigest()); + } + + public static RuntimeBrokerException releaseUnavailable() { + return new RuntimeBrokerException(409, "csi_finalize_required", + "The original CSI Session requires retirement finalization.", false); + } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProof.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProof.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProof.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProof.java index 573c6b97d85..490dee1a572 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProof.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProof.java @@ -374,7 +374,7 @@ public final class CsiNativeActivationProof { previous.pendingBatch(), previous.batches(), previous.fileHistory(), previous.intents(), receipts, previous.nextDeltaOrdinal()); } - static JsonNode convertedResult(JsonNode result, FunctionCall call) { + public static JsonNode convertedResult(JsonNode result, FunctionCall call) { closed(result, result.has("error") ? Set.of("executionStatus", "responseParts", "error") : Set.of("executionStatus", "responseParts")); require(utf8(canonical(result)).length <= CsiNativeReadbackProtocol.EXECUTION_RESULT_LIMIT); @@ -411,7 +411,7 @@ public final class CsiNativeActivationProof { response.put("output", output); } else { String fallback = error != null ? error.path("message").textValue() : "Runtime tool " + status + "."; - response.put("error", !CsiNativeToolReservation.trim(output).isEmpty() && !"Tool execution succeeded.".equals(output) ? output : fallback); + response.put("error", !com.alibaba.qwen.code.runtimebroker.CsiNativeToolContent.trim(output).isEmpty() && !"Tool execution succeeded.".equals(output) ? output : fallback); } if (error != null) { response.set("runtimeError", error); @@ -462,7 +462,7 @@ public final class CsiNativeActivationProof { String functionId = id(response, "id"); var call = batch.calls().stream().filter(item -> functionId.equals(item.id())) .findFirst().orElseThrow(CsiNativeActivationProof::invalid); - String reason = CsiNativeToolReservation.refusalReason(original, call); + String reason = com.alibaba.qwen.code.runtimebroker.CsiNativeToolContent.refusalReason(original, call); require(reason != null && reason.equals(text(response.path("response"), "error")) && call.name().equals(text(response, "name"))); Set refused = new HashSet<>(batch.refusedFunctionIds()); @@ -611,7 +611,7 @@ public final class CsiNativeActivationProof { byte[] input = reference(payload.path("argsRef"), "managed-tool-input", resources); byte[] definition = reference(payload.path("toolDefinitionRef"), "managed-tool-definition", resources); require(!id(payload.path("argsRef"), "resourceId").equals(id(payload.path("toolDefinitionRef"), "resourceId"))); - var content = CsiNativeToolReservation.content(original, batch.batch().calls().get((int) ordinal), input, definition); + var content = com.alibaba.qwen.code.runtimebroker.CsiNativeToolContent.content(original, batch.batch().calls().get((int) ordinal), input, definition); var frozen = previous.fileHistory() == null ? null : previous.fileHistory().batches().get(batchId); if (frozen == null) { require("read_file".equals(batch.batch().calls().get((int) ordinal).name())); @@ -921,7 +921,7 @@ public final class CsiNativeActivationProof { require(ordinal < batch.batch().calls().size() && batch.batch().calls().get((int) ordinal).name().equals(text(invocation, "toolName"))); JsonNode ref = historyExecutionReference(original, preparation, invocation); - var input = CsiNativeToolReservation.qualifyContent(original, batch, ref, text(invocation, "requestDigest"), + var input = com.alibaba.qwen.code.runtimebroker.CsiNativeToolContent.qualifyContent(original, batch, ref, text(invocation, "requestDigest"), reference(invocation.path("inputRef"), "managed-tool-input", resources), reference(invocation.path("toolDefinitionRef"), "managed-tool-definition", resources)); if (!"read_file".equals(text(invocation, "toolName"))) { @@ -1374,7 +1374,7 @@ public final class CsiNativeActivationProof { return messageRecord(message, messagePayload, original, completed, resources, true); } - static Set completeOutputTail(Prefix prefix, long sequence) { + public static Set completeOutputTail(Prefix prefix, long sequence) { require(prefix.input() != null && prefix.input().noDeadline() && prefix.pendingBatch() == null && prefix.batches().size() == 1 && prefix.checkpoint() != null && prefix.attempt() != null && "output_committed".equals(prefix.attempt().stage()) && prefix.attempt().finalMessageRef() != null @@ -1835,7 +1835,7 @@ public final class CsiNativeActivationProof { return object(readJson(bytes)); } - static JsonNode readJson(byte[] bytes) { + public static JsonNode readJson(byte[] bytes) { require(bytes != null && bytes.length > 0 && bytes.length <= 8 * 1024 * 1024); try { String text = StandardCharsets.UTF_8.newDecoder().onMalformedInput(CodingErrorAction.REPORT) @@ -1846,7 +1846,7 @@ public final class CsiNativeActivationProof { } } - static byte[] reference(JsonNode ref, String kind, Function resources) { + public static byte[] reference(JsonNode ref, String kind, Function resources) { closed(ref, Set.of("resourceId", "kind", "schemaVersion", "byteLength", "digest")); id(ref, "resourceId"); require(kind.equals(text(ref, "kind")) && number(ref.get("schemaVersion")) == 1); @@ -1883,7 +1883,7 @@ public final class CsiNativeActivationProof { return node; } - static void uuid(String value) { + public static void uuid(String value) { try { require(UUID.fromString(value).toString().equals(value)); } catch (IllegalArgumentException error) { diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolContent.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolContent.java new file mode 100644 index 00000000000..7ad5fb6ac14 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolContent.java @@ -0,0 +1,160 @@ +package com.alibaba.qwen.code.runtimebroker; + +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceException; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceRelativePath; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.canonical; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.closed; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.id; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.number; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.reference; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.sha256; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.text; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.utf8; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.uuid; + +/** Pure allocation-content rules shared with durable CSI validation. */ +public final class CsiNativeToolContent { + private CsiNativeToolContent() { } + + private static final ObjectMapper JSON = new ObjectMapper(); + + private static final Set FIELDS = Set.of("sessionId", "promptId", "callId", "argsDigest", "batchId", + "functionCallId", "partIndex", "ordinal", "inputRef", "toolDefinitionRef", "dispatchMode"); + + private static final String JS_WHITESPACE = "[\\x09-\\x0d\\x20\\x{a0}\\x{1680}\\x{2000}-\\x{200a}" + + "\\x{2028}\\x{2029}\\x{202f}\\x{205f}\\x{3000}\\x{feff}]"; + + private static final Pattern JS_TRIM = Pattern.compile("^" + JS_WHITESPACE + "+|" + JS_WHITESPACE + "+$"); + + private static final JsonNode DEFINITIONS = definitions(); + + public static String trim(String value) { + return JS_TRIM.matcher(value).replaceAll(""); + } + + public static ObjectNode qualifyContent(RuntimeProvisionRequest original, CsiNativeActivationProof.OriginalBatch batch, + JsonNode ref, String requestDigest, byte[] inputBytes, byte[] definitionBytes) { + closed(ref, FIELDS); + String session = original.getIsolationKey(); + uuid(id(ref, "callId")); + uuid(id(ref, "batchId")); + require(session.equals(id(ref, "sessionId")) && batch.promptId().equals(id(ref, "promptId")) + && batch.batch().messageId().equals(id(ref, "batchId")) && "deferred".equals(text(ref, "dispatchMode"))); + long ordinal = number(ref.get("ordinal")); + require(ordinal < batch.batch().calls().size()); + var call = batch.batch().calls().get((int) ordinal); + require(call.id().equals(id(ref, "functionCallId")) && call.partIndex() == number(ref.get("partIndex"))); + require(inputBytes != null && inputBytes.length <= 64 * 1024 + && definitionBytes != null && definitionBytes.length <= 64 * 1024); + reference(ref.path("inputRef"), "managed-tool-input", ignored -> inputBytes); + reference(ref.path("toolDefinitionRef"), "managed-tool-definition", ignored -> definitionBytes); + require(!id(ref.path("inputRef"), "resourceId").equals(id(ref.path("toolDefinitionRef"), "resourceId"))); + var content = content(original, call, inputBytes, definitionBytes); + String digest = "sha256:" + content.digest(); + require(digest.equals(text(ref, "argsDigest")) && digest.equals(requestDigest)); + return content.normalized(); + } + + public record Content(ObjectNode normalized, String digest) { + } + + public static String refusalReason(RuntimeProvisionRequest original, CsiNativeActivationProof.FunctionCall call) { + ObjectNode input; + try { + input = normalizedInput(call.name(), call.args()); + } catch (RuntimeBrokerException | WorkspaceException | IllegalArgumentException invalid) { + return "CSI file input is invalid."; + } + ObjectNode payload = JSON.createObjectNode().put("toolName", call.name()); + payload.set("input", input); + ObjectNode wrapper = JSON.createObjectNode().put("harnessSessionId", original.getIsolationKey()) + .put("runtimeSessionId", original.getIsolationKey()).put("payloadJson", canonical(payload)); + return utf8(canonical(wrapper)).length > 64 * 1024 + ? "CSI file input exceeds the durable Session limit." : null; + } + + public static Content content(RuntimeProvisionRequest original, CsiNativeActivationProof.FunctionCall call, + byte[] inputBytes, byte[] definitionBytes) { + require(inputBytes != null && inputBytes.length <= 64 * 1024 + && definitionBytes != null && definitionBytes.length <= 64 * 1024); + String session = original.getIsolationKey(); + JsonNode wrapper = CsiNativeActivationProof.readObject(inputBytes); + closed(wrapper, Set.of("harnessSessionId", "runtimeSessionId", "payloadJson")); + require(session.equals(id(wrapper, "harnessSessionId")) && session.equals(id(wrapper, "runtimeSessionId")) + && wrapper.path("payloadJson").isTextual()); + byte[] payloadBytes = utf8(wrapper.path("payloadJson").textValue()); + JsonNode payload = CsiNativeActivationProof.readObject(payloadBytes); + closed(payload, Set.of("toolName", "input")); + require(call.name().equals(text(payload, "toolName"))); + ObjectNode normalized = normalizedInput(call.name(), call.args()); + require(canonical(normalized).equals(canonical(payload.path("input")))); + JsonNode definition = CsiNativeActivationProof.readObject(definitionBytes); + JsonNode expected = null; + for (JsonNode item : DEFINITIONS) { + if (call.name().equals(item.path("name").textValue())) { + expected = item; + } + } + require(expected != null && canonical(expected).equals(canonical(definition))); + return new Content(normalized, sha256(payloadBytes)); + } + + private static ObjectNode normalizedInput(String name, JsonNode args) { + Set required = switch (name) { + case "read_file" -> Set.of("file_path"); + case "write_file" -> Set.of("file_path", "content"); + case "edit" -> Set.of("file_path", "old_string", "new_string"); + default -> throw invalid(); + }; + Set optional = "read_file".equals(name) ? Set.of("offset", "limit") + : "edit".equals(name) ? Set.of("replace_all") : Set.of(); + require(args.isObject()); + args.fieldNames().forEachRemaining(field -> require(required.contains(field) || optional.contains(field))); + for (String field : required) { + require(args.path(field).isTextual()); + utf8(args.path(field).textValue()); + } + for (String field : List.of("offset", "limit")) { + if (args.has(field)) { + require(number(args.get(field)) >= ("limit".equals(field) ? 1 : 0)); + } + } + require(!args.has("replace_all") || args.path("replace_all").isBoolean()); + ObjectNode result = args.deepCopy(); + result.put("file_path", WorkspaceRelativePath.normalize(trim(args.path("file_path").textValue()))); + require(!".".equals(result.path("file_path").textValue())); + return result; + } + + private static JsonNode definitions() { + try (InputStream input = CsiNativeToolContent.class.getResourceAsStream("/csi-native-file-declarations.json")) { + require(input != null); + JsonNode definitions = CsiNativeActivationProof.readJson(input.readNBytes(64 * 1024 + 1)); + require(definitions.isArray() && definitions.size() == 3); + return definitions; + } catch (IOException error) { + throw new IllegalStateException("Private CSI declarations are unavailable", error); + } + } + + private static void require(boolean condition) { + if (!condition) { + throw invalid(); + } + } + + private static RuntimeBrokerException invalid() { + return new RuntimeBrokerException(409, "csi_native_reservation_conflict", + "The original CSI native allocation proof is unavailable.", false); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiOwnershipRevision.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiOwnershipRevision.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiOwnershipRevision.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiOwnershipRevision.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ExecutionReconciliation.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ExecutionReconciliation.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ExecutionReconciliation.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ExecutionReconciliation.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnership.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnership.java similarity index 82% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnership.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnership.java index 1042d1471b8..72a241f0ce7 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnership.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnership.java @@ -1,7 +1,5 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; - /** * Says who owns the Harness fences on a Runtime binding. A Session-isolated * binding belongs to the Harness Session its isolation key names; a @@ -17,7 +15,7 @@ public final class HarnessOwnership { return request != null && "workspace".equals(request.getScope().getIsolationClass()) && request.isManagedContext() - && LocalProcessRuntimeProvisioner.KIND.equals(request.getProvisionerKind()) + && com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(request.getProvisionerKind()) && WorkspaceExecutionProfile.admitsCapabilityDigest(request.getScope().getCapabilityDigest()); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessSessionResolver.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessSessionResolver.java similarity index 79% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessSessionResolver.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessSessionResolver.java index 8336ec3b254..362fd7893a7 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessSessionResolver.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/HarnessSessionResolver.java @@ -10,7 +10,7 @@ import java.util.concurrent.CompletionStage; * Session identity. */ public interface HarnessSessionResolver { - CompletionStage resolve(String harnessSessionId); + public CompletionStage resolve(String harnessSessionId); /** * Resolves the scope for a route that admits new work (a warm). The @@ -18,7 +18,7 @@ public interface HarnessSessionResolver { * Session does so here, so teardown routes such as release and * unknown-outcome reconciliation still resolve that Session's scope. */ - default CompletionStage resolveAdmission( + public default CompletionStage resolveAdmission( String harnessSessionId) { return resolve(harnessSessionId); } @@ -28,7 +28,7 @@ public interface HarnessSessionResolver { * admission resolve, so an implementation that overrides only the one-arg * form keeps its fence. */ - default CompletionStage resolveAdmission( + public default CompletionStage resolveAdmission( String harnessSessionId, RuntimeLifecycleAuthority authority) { if (authority == null) { return resolveAdmission(harnessSessionId); @@ -36,7 +36,7 @@ public interface HarnessSessionResolver { return resolve(harnessSessionId, authority); } - default CompletionStage resolve(String harnessSessionId, RuntimeLifecycleAuthority authority) { + public default CompletionStage resolve(String harnessSessionId, RuntimeLifecycleAuthority authority) { if (authority != null) { return java.util.concurrent.CompletableFuture.failedFuture(new RuntimeBrokerException( 409, "runtime_lifecycle_unavailable", "Lifecycle authority is unavailable", false)); @@ -44,7 +44,7 @@ public interface HarnessSessionResolver { return resolve(harnessSessionId); } - default CompletionStage resolveTenant(String harnessSessionId) { + public default CompletionStage resolveTenant(String harnessSessionId) { return resolve(harnessSessionId).thenApply(RuntimeScope::getTenantId); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JsonCodec.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/JsonCodec.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JsonCodec.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/JsonCodec.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistory.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistory.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistory.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistory.java index f95532f3aea..e03b11fe0db 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistory.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistory.java @@ -24,13 +24,13 @@ public final class ManagedCheckpointHistory { @FunctionalInterface public interface HistoricalPages { - PageBytes read(JsonNode ref, long maximumRevision); + public PageBytes read(JsonNode ref, long maximumRevision); } public interface Reader extends Function { - PageBytes historicalPage(JsonNode ref, long maximumRevision); + public PageBytes historicalPage(JsonNode ref, long maximumRevision); - long revision(); + public long revision(); } public static Reader reader(Function current, HistoricalPages history, long revision) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ManagedExtensionExecutionProfile.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ManagedExtensionExecutionProfile.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ManagedExtensionExecutionProfile.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ManagedExtensionExecutionProfile.java diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/NativeCsiExecutionRepository.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/NativeCsiExecutionRepository.java new file mode 100644 index 00000000000..eb7fa5b5f50 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/NativeCsiExecutionRepository.java @@ -0,0 +1,24 @@ +package com.alibaba.qwen.code.runtimebroker; + +import java.util.Map; + +public interface NativeCsiExecutionRepository { + public ToolExecutionRecord prepareCsiExecution( + RuntimeSessionRepository sessions, + ToolExecutionRepository executions, + RuntimeBindingRecord hint, + ToolExecutionRecord candidate, + byte[] input, + byte[] definition); + + public Map readCsiBatch( + RuntimeSessionRepository sessions, ToolExecutionRepository executions, + RuntimeBindingRecord hint, String promptId, String batchId); + + public Map readCsiBatch( + RuntimeSessionRepository sessions, + ToolExecutionRepository executions, + RuntimeBindingRecord hint, + String promptId, + String batchId, Map recoveryOwner); +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ResidentWorkspaceProfile.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ResidentWorkspaceProfile.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ResidentWorkspaceProfile.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ResidentWorkspaceProfile.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAdmission.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAdmission.java similarity index 83% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAdmission.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAdmission.java index d59f6a1c13f..c91a906c4a8 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAdmission.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAdmission.java @@ -1,14 +1,11 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeProvisioner; - /** Checks performed while the parent generation admission lock is held. */ -final class RuntimeAdmission { +public final class RuntimeAdmission { private RuntimeAdmission() { } - static void requireReady(RuntimeBindingRecord binding, long generation) { + public static void requireReady(RuntimeBindingRecord binding, long generation) { if (binding == null || binding.getGeneration() != generation || binding.getState() != RuntimeBindingRecord.State.READY || binding.isDrainRequested()) { @@ -17,7 +14,7 @@ final class RuntimeAdmission { } } - static void requireRelease(RuntimeBindingRecord binding, RuntimeSessionRecord session) { + public static void requireRelease(RuntimeBindingRecord binding, RuntimeSessionRecord session) { if (binding == null || !binding.getBindingId().equals(session.getBindingId()) || binding.getGeneration() != session.getRuntimeGeneration() || !binding.getRequest().getScope().equals(session.getSession().getScope()) @@ -29,7 +26,7 @@ final class RuntimeAdmission { } } - static void requireSession(RuntimeSessionRecord session, + public static void requireSession(RuntimeSessionRecord session, ToolExecutionRecord execution) { if (session == null || session.getState() != RuntimeSessionRecord.State.READY @@ -42,12 +39,12 @@ final class RuntimeAdmission { } } - static void requireStoppedRelease(RuntimeBindingRecord binding, RuntimeBindingRecord claim, + public static void requireStoppedRelease(RuntimeBindingRecord binding, RuntimeBindingRecord claim, RuntimeSessionRecord session, java.time.Instant now) { requireRelease(binding, session); if (!binding.sameIdentity(claim) || !binding.getRequest().isManagedContext() - || !LocalProcessRuntimeProvisioner.KIND.equals(binding.getRequest().getProvisionerKind()) - && !SshRuntimeProvisioner.KIND.equals(binding.getRequest().getProvisionerKind()) + || !com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(binding.getRequest().getProvisionerKind()) + && !com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS.equals(binding.getRequest().getProvisionerKind()) || !(HarnessOwnership.sharesRuntimeSessions(binding.getRequest()) || WorkspaceExecutionProfile.admitsIsolationClass(binding.getRequest().getScope().getIsolationClass()) && binding.getRequest().getIsolationKey().equals(session.getSession().getHarnessSessionId())) diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAttestation.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAttestation.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAttestation.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAttestation.java index f7dbea93f6f..5ad75cbb17d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAttestation.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeAttestation.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; /** Private Runtime proof returned before a restored endpoint is used. */ public final class RuntimeAttestation { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRecord.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRecord.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRecord.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRecord.java index 49937e007e5..a2e6664302c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRecord.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRecord.java @@ -1,7 +1,5 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; - import java.time.Instant; import java.util.Objects; @@ -77,7 +75,7 @@ public final class RuntimeBindingRecord { version, lastHealthAt, lastReconciledAt, lastActiveAt, null, null); } - RuntimeBindingRecord(String bindingId, RuntimeProvisionRequest request, + public RuntimeBindingRecord(String bindingId, RuntimeProvisionRequest request, RuntimeProvisionSeed provisionSeed, long generation, State state, RuntimeLease lease, RuntimeResourceHandle resourceHandle, long attestationGeneration, boolean drainRequested, @@ -91,7 +89,7 @@ public final class RuntimeBindingRecord { lossEvidence, stopEvidence, null); } - RuntimeBindingRecord(String bindingId, RuntimeProvisionRequest request, + public RuntimeBindingRecord(String bindingId, RuntimeProvisionRequest request, RuntimeProvisionSeed provisionSeed, long generation, State state, RuntimeLease lease, RuntimeResourceHandle resourceHandle, long attestationGeneration, boolean drainRequested, @@ -240,7 +238,7 @@ public final class RuntimeBindingRecord { return request; } - RuntimeBindingRecord withLifecycleAuthority(RuntimeLifecycleAuthority authority) { + public RuntimeBindingRecord withLifecycleAuthority(RuntimeLifecycleAuthority authority) { RuntimeProvisionRequest scoped = new RuntimeProvisionRequest(request.getScope().withLifecycleAuthority(authority), request.getIsolationKey(), request.getProvisionerKind(), request.getStorageId()); return new RuntimeBindingRecord(bindingId, scoped, provisionSeed, generation, state, lease, @@ -353,28 +351,28 @@ public final class RuntimeBindingRecord { activeAt); } - RuntimeBindingRecord withOperation(String owner, Instant leaseUntil, + public RuntimeBindingRecord withOperation(String owner, Instant leaseUntil, long nextOperationGeneration) { return copy(state, lease, resourceHandle, attestationGeneration, drainRequested, owner, leaseUntil, nextOperationGeneration, version, lastHealthAt, lastReconciledAt, lastActiveAt); } - RuntimeBindingRecord withVersion(long nextVersion) { + public RuntimeBindingRecord withVersion(long nextVersion) { return copy(state, lease, resourceHandle, attestationGeneration, drainRequested, operationOwner, operationLeaseUntil, operationGeneration, nextVersion, lastHealthAt, lastReconciledAt, lastActiveAt); } - boolean blocksPlacement(RuntimeProvisionRequest candidate) { + public boolean blocksPlacement(RuntimeProvisionRequest candidate) { // A local worker without a lease or attested generation never // admitted a Session; failed startup still blocks its own slot. boolean unreclaimed = state == State.LOST || state == State.DRAINING || state == State.OPERATOR_RECOVERY || state == State.RECOVERY_BLOCKED - && (!LocalProcessRuntimeProvisioner.KIND.equals( + && (!com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals( request.getProvisionerKind()) || lease != null || attestationGeneration > 0) @@ -402,7 +400,7 @@ public final class RuntimeBindingRecord { && lease.getEpoch() == other.getEpoch(); } - void requireSafeReplacement(RuntimeBindingRecord replacement) { + public void requireSafeReplacement(RuntimeBindingRecord replacement) { if (drainReceipt != null && !drainReceipt.equals(replacement.drainReceipt)) { throw new IllegalArgumentException("Drain proof cannot be overwritten"); } @@ -445,14 +443,14 @@ public final class RuntimeBindingRecord { } } - boolean sameIdentity(RuntimeBindingRecord other) { + public boolean sameIdentity(RuntimeBindingRecord other) { return other != null && bindingId.equals(other.bindingId) && request.equals(other.request) && Objects.equals(provisionSeed, other.provisionSeed) && generation == other.generation; } - boolean sameOperation(RuntimeBindingRecord other) { + public boolean sameOperation(RuntimeBindingRecord other) { return other != null && Objects.equals(operationOwner, other.operationOwner) && Objects.equals(operationLeaseUntil, @@ -460,7 +458,7 @@ public final class RuntimeBindingRecord { && operationGeneration == other.operationGeneration; } - boolean hasLiveOperationAt(Instant now) { + public boolean hasLiveOperationAt(Instant now) { return operationOwner != null && operationLeaseUntil.isAfter(now); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRepository.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRepository.java similarity index 64% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRepository.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRepository.java index 4b59a94b227..15b87a79832 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRepository.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBindingRepository.java @@ -7,31 +7,31 @@ import java.util.List; public interface RuntimeBindingRepository { /** Inserts only while the parent generation is READY and not draining. * Must share the generation lock used by compareAndSet. */ - RuntimeSessionRecord admitSession(RuntimeSessionRepository sessions, + public RuntimeSessionRecord admitSession(RuntimeSessionRepository sessions, RuntimeSessionRecord candidate); /** Creates a RELEASED receipt under the same generation lock as admission. */ - default RuntimeSessionRecord createSessionTombstone(RuntimeSessionRepository sessions, + public default RuntimeSessionRecord createSessionTombstone(RuntimeSessionRepository sessions, RuntimeSessionRecord candidate) { throw new RuntimeBrokerException(501, "runtime_session_tombstone_unavailable", "Runtime repository does not support atomic Session retirement", false); } /** Rechecks admission for an existing READY Session without inserting or updating it. */ - default RuntimeSessionRecord requireSessionAdmission(RuntimeSessionRepository sessions, + public default RuntimeSessionRecord requireSessionAdmission(RuntimeSessionRepository sessions, RuntimeSessionRecord expected) { throw new RuntimeBrokerException(501, "runtime_session_admission_unavailable", "Runtime repository does not support atomic Session admission checks", false); } /** Existing idempotency receipts remain readable after admission closes. */ - ToolExecutionRecord admitExecution(RuntimeSessionRepository sessions, + public ToolExecutionRecord admitExecution(RuntimeSessionRepository sessions, ToolExecutionRepository executions, ToolExecutionRecord candidate); /** Authorizes DISPATCHING to EXECUTING under the parent admission lock. * Native repositories retain the locked parent version and dispatch generation. * A dispatch coordinator claim alone grants no execution permission. */ - default ToolExecutionRecord authorizeDispatch(RuntimeSessionRepository sessions, + public default ToolExecutionRecord authorizeDispatch(RuntimeSessionRepository sessions, ToolExecutionRepository executions, ToolExecutionRecord expected, String owner, long dispatchGeneration) { throw new RuntimeBrokerException(501, "runtime_dispatch_admission_unavailable", @@ -40,22 +40,22 @@ public interface RuntimeBindingRepository { /** Performs one bounded recovery transaction under the exact generation * claim. Returns LOST while more work or physical stop proof is needed. */ - RuntimeBindingRecord recoverLost(RuntimeSessionRepository sessions, + public RuntimeBindingRecord recoverLost(RuntimeSessionRepository sessions, ToolExecutionRepository executions, RuntimeBindingRecord expected); /** Finalizes a stopped generation after its provisioner cleared physical holders. */ - RuntimeBindingRecord finishLostRecovery(RuntimeSessionRepository sessions, + public RuntimeBindingRecord finishLostRecovery(RuntimeSessionRepository sessions, ToolExecutionRepository executions, RuntimeBindingRecord expected); /** Bounded maintenance candidates, ordered by binding ID after the exclusive cursor. */ - List findRecoveryCandidates(String provisionerKind, String afterBindingId, int limit); + public List findRecoveryCandidates(String provisionerKind, String afterBindingId, int limit); /** Finalizes release under the parent generation lock; managed LOST requires generation recovery. */ - RuntimeSessionRecord completeSessionRelease(RuntimeSessionRepository sessions, + public RuntimeSessionRecord completeSessionRelease(RuntimeSessionRepository sessions, RuntimeSessionRecord expected); /** Atomically releases one original Session and its holder under a live drain claim and persisted stop receipt. */ - default RuntimeSessionRecord completeStoppedSessionRelease(RuntimeSessionRepository sessions, + public default RuntimeSessionRecord completeStoppedSessionRelease(RuntimeSessionRepository sessions, ToolExecutionRepository executions, RuntimeSessionRecord expected, RuntimeBindingRecord claim) { throw new RuntimeBrokerException(501, "runtime_stopped_release_unavailable", "Runtime repository does not support stopped Session release", false); @@ -71,25 +71,25 @@ public interface RuntimeBindingRepository { * throws {@code runtime_session_not_ready} when the Session is in any * other state. */ - RuntimeSessionRecord beginSessionRelease(RuntimeSessionRepository sessions, + public RuntimeSessionRecord beginSessionRelease(RuntimeSessionRepository sessions, ToolExecutionRepository executions, RuntimeSessionRecord expected); - void requestHarnessDrain(String tenantId, String harnessSessionId); + public void requestHarnessDrain(String tenantId, String harnessSessionId); - boolean isHarnessDraining(String tenantId, String harnessSessionId); + public boolean isHarnessDraining(String tenantId, String harnessSessionId); - default boolean isHarnessAdmissionClosed(String tenantId, String harnessSessionId) { + public default boolean isHarnessAdmissionClosed(String tenantId, String harnessSessionId) { return isHarnessDraining(tenantId, harnessSessionId); } - default void requireHarnessAdmission(RuntimeScope scope, String harnessSessionId, + public default void requireHarnessAdmission(RuntimeScope scope, String harnessSessionId, RuntimeLifecycleAuthority authority) { if (authority != null || isHarnessDraining(scope.getTenantId(), harnessSessionId)) { throw new RuntimeBrokerException(409, "runtime_admission_closed", "Session admission is closed", false); } } - default boolean requireHarnessRecovery(RuntimeScope scope, String harnessSessionId, + public default boolean requireHarnessRecovery(RuntimeScope scope, String harnessSessionId, RuntimeLifecycleAuthority authority) { if (authority != null) { requireHarnessCleanup(scope, harnessSessionId, authority); @@ -97,81 +97,71 @@ public interface RuntimeBindingRepository { return authority != null; } - default void requireHarnessCleanup(RuntimeScope scope, String harnessSessionId, + public default void requireHarnessCleanup(RuntimeScope scope, String harnessSessionId, RuntimeLifecycleAuthority authority) { throw new RuntimeBrokerException(409, "runtime_admission_closed", "Lifecycle cleanup is unavailable", false); } - default void requireHookAdmission(RuntimeScope scope, String harnessSessionId, + public default void requireHookAdmission(RuntimeScope scope, String harnessSessionId, RuntimeLifecycleAuthority authority) { requireHarnessAdmission(scope, harnessSessionId, authority); } /** As the scope-only check, for a Runtime Session on {@code bindingRequest}; see {@link HarnessOwnership}. */ - default void requireHarnessAdmission(RuntimeProvisionRequest bindingRequest, RuntimeScope scope, + public default void requireHarnessAdmission(RuntimeProvisionRequest bindingRequest, RuntimeScope scope, String harnessSessionId, RuntimeLifecycleAuthority authority) { requireHarnessAdmission(scope, harnessSessionId, authority); } /** As the scope-only check, for a Runtime Session on {@code bindingRequest}; see {@link HarnessOwnership}. */ - default boolean requireHarnessRecovery(RuntimeProvisionRequest bindingRequest, RuntimeScope scope, + public default boolean requireHarnessRecovery(RuntimeProvisionRequest bindingRequest, RuntimeScope scope, String harnessSessionId, RuntimeLifecycleAuthority authority) { return requireHarnessRecovery(scope, harnessSessionId, authority); } /** As the scope-only check, for a Runtime Session on {@code bindingRequest}; see {@link HarnessOwnership}. */ - default void requireHarnessCleanup(RuntimeProvisionRequest bindingRequest, RuntimeScope scope, + public default void requireHarnessCleanup(RuntimeProvisionRequest bindingRequest, RuntimeScope scope, String harnessSessionId, RuntimeLifecycleAuthority authority) { requireHarnessCleanup(scope, harnessSessionId, authority); } /** As the scope-only check, for a Runtime Session on {@code bindingRequest}; see {@link HarnessOwnership}. */ - default void requireHookAdmission(RuntimeProvisionRequest bindingRequest, RuntimeScope scope, + public default void requireHookAdmission(RuntimeProvisionRequest bindingRequest, RuntimeScope scope, String harnessSessionId, RuntimeLifecycleAuthority authority) { requireHookAdmission(scope, harnessSessionId, authority); } - default List verifyHarnessResources(java.sql.Connection connection, - String tenantId, String harnessSessionId) throws java.sql.SQLException { - throw new RuntimeBrokerException(409, "workspace_close_identity_unverified", "Resource verification is unavailable", false); - } - - default void verifyHarnessStopped(java.sql.Connection connection, String tenantId, String harnessSessionId) - throws java.sql.SQLException { - throw new RuntimeBrokerException(409, "workspace_close_identity_unverified", "Stop verification is unavailable", false); - } - - List findByHarnessSession(String tenantId, String harnessSessionId, + public List findByHarnessSession(String tenantId, String harnessSessionId, String afterBindingId, int limit); - void requestStorageFence(String tenantId, String storageId, String operationId); + public void requestStorageFence(String tenantId, String storageId, String operationId); - boolean isStorageFenced(String tenantId, String storageId, String operationId); + public boolean isStorageFenced(String tenantId, String storageId, String operationId); - List findByStorage(String tenantId, String storageId, String afterBindingId, int limit); + public List findByStorage(String tenantId, String storageId, String afterBindingId, int limit); - RuntimeBindingRecord findOrCreate(RuntimeProvisionRequest request); + public RuntimeBindingRecord findOrCreate(RuntimeProvisionRequest request); - RuntimeBindingRecord findActive(RuntimeProvisionRequest request); + public RuntimeBindingRecord findActive(RuntimeProvisionRequest request); - List findActiveByIsolationKey(RuntimeScope scope, + public List findActiveByIsolationKey(RuntimeScope scope, String isolationKey); - RuntimeBindingRecord findById(String bindingId); + public RuntimeBindingRecord findById(String bindingId); - RuntimeBindingRecord compareAndSet(RuntimeBindingRecord expected, + public RuntimeBindingRecord compareAndSet(RuntimeBindingRecord expected, RuntimeBindingRecord replacement); - RuntimeBindingRecord claimOperation(String bindingId, String owner, + public RuntimeBindingRecord claimOperation(String bindingId, String owner, Duration leaseDuration); - RuntimeBindingRecord renewOperation(String bindingId, String owner, + public RuntimeBindingRecord renewOperation(String bindingId, String owner, long operationGeneration, Duration leaseDuration); /** Clears the caller's operation claim so another Broker can take over * without waiting for the lease to lapse. Returns the updated record, * or null when the claim no longer matches. Releasing a lapsed claim is * permitted cleanup. */ - RuntimeBindingRecord releaseOperation(String bindingId, String owner, + public RuntimeBindingRecord releaseOperation(String bindingId, String owner, long operationGeneration); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerException.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerException.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerException.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerException.java diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerOperations.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerOperations.java new file mode 100644 index 00000000000..3f0727a83a1 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerOperations.java @@ -0,0 +1,123 @@ +package com.alibaba.qwen.code.runtimebroker; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletionStage; + +public interface RuntimeBrokerOperations { + public RuntimeBindingRecord findLatestBindingByHarnessSession( + String tenantId, String harnessSessionId); + + public RuntimeBindingRecord findLatestBindingByHarnessSessionAnyState( + String tenantId, String harnessSessionId); + + public boolean supportsDrainedStop(); + + public void requestHarnessDrain(String tenantId, String harnessSessionId); + + public CompletionStage drainHarnessSession(String tenantId, String harnessSessionId); + + public CompletionStage closeResidentHarnessSession(RuntimeProvisionRequest request, String harnessSessionId); + + public CompletionStage drainHarnessResources(String tenantId, String harnessSessionId, + RuntimeLifecycleAuthority authority, boolean stoppedOnly); + + public CompletionStage prepareHarnessResources(String tenantId, String harnessSessionId, + RuntimeLifecycleAuthority authority); + + public CompletionStage retireStorageBinding(String tenantId, String storageId, + String operationId, String bindingId, long generation); + + public CompletionStage warm( + String harnessSessionId); + + public CompletionStage capabilities( + String harnessSessionId, RuntimeLifecycleAuthority authority); + + public CompletionStage>> mcpDefinitions(String harnessSessionId, String runtimeSessionId); + + public CompletionStage> extensionControl(String harnessSessionId, String expectedBindingId, + Long expectedGeneration, Map operation); + + public CompletionStage warm(String harnessSessionId, RuntimeLifecycleAuthority authority); + + public CompletionStage acquire(String harnessSessionId, String runtimeSessionId, + String turnKind, RuntimeLifecycleAuthority authority, String mountClaimId); + + public CompletionStage acquireRecovery(String harnessSessionId, String runtimeSessionId, + String bindingId, long generation, RuntimeLifecycleAuthority authority, String mountClaimId); + + public CompletionStage authorizeLifecycle(String harnessSessionId, RuntimeLifecycleAuthority authority); + + public CompletionStage control(String harnessSessionId, + String runtimeSessionId, Map operation, RuntimeLifecycleAuthority authority); + + public CompletionStage createExecution( + String harnessSessionId, String runtimeSessionId, + String idempotencyKey, Map reference); + + public CompletionStage prepareCsiExecution(String harnessSessionId, String runtimeSessionId, + String idempotencyKey, Map reference, byte[] inputBytes, byte[] definitionBytes); + + public CompletionStage> readCsiBatch(String harnessSessionId, String runtimeSessionId, + String promptId, String batchId, Map recoveryOwner); + + public CompletionStage prepareExecution( + String harnessSessionId, String runtimeSessionId, + String idempotencyKey, Map reference, + String payloadDigest, String publicationId); + + public CompletionStage startExecution( + String harnessSessionId, String runtimeSessionId, + String executionCallId, String payloadJson, + String publicationId, String publicationToken); + + public CompletionStage startExecution( + String harnessSessionId, String runtimeSessionId, String executionCallId); + + public CompletionStage getExecution( + String harnessSessionId, String runtimeSessionId, + String executionCallId); + + public CompletionStage> listActiveHoldRuntimeSessionIds( + String harnessSessionId); + + public CompletionStage> installPublisher(String harnessSessionId, + String runtimeSessionId, Map publisher); + + public CompletionStage> acknowledgeExecution( + String harnessSessionId, String runtimeSessionId, + String executionCallId, Map receipt); + + public CompletionStage cancelExecution( + String harnessSessionId, String runtimeSessionId, + String executionCallId); + + public CompletionStage reconcileExecution( + String harnessSessionId, String runtimeSessionId, + String executionCallId); + + public CompletionStage observeExecution( + String harnessSessionId, String runtimeSessionId, + String executionCallId); + + public CompletionStage releaseOriginal(String harnessSessionId, String runtimeSessionId, + String bindingId, long generation, RuntimeLifecycleAuthority authority, boolean drain); + + public CompletionStage release(String harnessSessionId, + String runtimeSessionId, boolean drain, RuntimeLifecycleAuthority authority); + + public CompletionStage releaseMount(String harnessSessionId, + String runtimeSessionId, String mountClaimId, + RuntimeLifecycleAuthority authority); + + public CompletionStage observeBackgroundProcess( + String harnessSessionId, String runtimeSessionId, + String executionCallId); + + public CompletionStage stopBackgroundProcess( + String tenantId, String harnessSessionId, + String executionCallId); + + public CompletionStage recoverBinding(String bindingId, long expectedGeneration); +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeDrainReceipt.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeDrainReceipt.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeDrainReceipt.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeDrainReceipt.java index 8273aeffea9..d10117aed57 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeDrainReceipt.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeDrainReceipt.java @@ -43,7 +43,7 @@ public record RuntimeDrainReceipt(String bindingId, long generation, && (binding.getResourceHandle() == null || resourceHandle.equals(binding.getResourceHandle())); } - String toJson() { + public String toJson() { Map value = new java.util.LinkedHashMap<>(); value.put("version", executionMode == null ? 1 : 2); value.put("bindingId", bindingId); @@ -60,7 +60,7 @@ public record RuntimeDrainReceipt(String bindingId, long generation, return new String(JsonCodec.encode(value), StandardCharsets.UTF_8); } - static RuntimeDrainReceipt fromJson(String json) { + public static RuntimeDrainReceipt fromJson(String json) { if (json == null) { return null; } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionCommandVerifier.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionCommandVerifier.java similarity index 68% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionCommandVerifier.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionCommandVerifier.java index 109e5826393..3f224c57853 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionCommandVerifier.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionCommandVerifier.java @@ -5,5 +5,5 @@ import java.util.Map; /** Checks the committed ordinary-tool permission before an extension command dispatch. */ @FunctionalInterface public interface RuntimeExtensionCommandVerifier { - void verify(RuntimeBindingRecord runtime, RuntimeSession session, Map operation); + public void verify(RuntimeBindingRecord runtime, RuntimeSession session, Map operation); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeLease.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeLease.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeLease.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeLease.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeLifecycleAuthority.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeLifecycleAuthority.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeLifecycleAuthority.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeLifecycleAuthority.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeObservation.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeObservation.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeObservation.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeObservation.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionRequest.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionRequest.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionRequest.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionRequest.java index b34f21403b4..5602f41f73f 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionRequest.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionRequest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import java.util.Objects; @@ -55,7 +55,7 @@ public final class RuntimeProvisionRequest { } public String requestKey() { - return JdbcRepositorySupport.requestKey(this); + return RuntimeRequestKeys.requestKey(this); } public boolean isManagedContext() { @@ -79,7 +79,7 @@ public final class RuntimeProvisionRequest { * binding for them is not required to carry seed, handle, and * attestation facts. */ - boolean requiresDurableIdentity() { + public boolean requiresDurableIdentity() { return !"legacy".equals(provisionerKind) && !"static".equals(provisionerKind); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeed.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeed.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeed.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeed.java index 0bfc6baac0d..7c69fb87d03 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeed.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeed.java @@ -90,7 +90,7 @@ public final class RuntimeProvisionSeed { && provisionalRuntimeId.equals(lease.getRuntimeInstanceId()); } - byte[] encode() { + public byte[] encode() { Map value = new LinkedHashMap<>(); value.put("provisionRequestId", provisionRequestId); value.put("provisionalRuntimeId", provisionalRuntimeId); @@ -101,7 +101,7 @@ public final class RuntimeProvisionSeed { return JsonCodec.encode(value); } - static RuntimeProvisionSeed decode(byte[] encoded) { + public static RuntimeProvisionSeed decode(byte[] encoded) { Map value = JsonCodec.parseObject(encoded, "Runtime provision seed"); if (value.size() != 6) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisioner.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisioner.java similarity index 81% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisioner.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisioner.java index ccc97605f92..261662ccf68 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisioner.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisioner.java @@ -6,24 +6,24 @@ import java.util.concurrent.CompletionStage; /** Ensures one physical Runtime resource for a claimed placement. */ public interface RuntimeProvisioner extends AutoCloseable { /** Retries for the same request must converge on one live resource. */ - CompletionStage provision(RuntimeProvisionRequest request); + public CompletionStage provision(RuntimeProvisionRequest request); /** * Stable kind identifier persisted next to the resource handle so a * restored binding is never reconciled by a different provisioner. The * default marks a placement without durable identity. */ - default String kind() { + public default String kind() { return "legacy"; } /** Exact provider identities a routing provisioner may dispatch. */ - default boolean supportsKind(String provisionerKind) { + public default boolean supportsKind(String provisionerKind) { return kind().equals(provisionerKind); } /** Creates placement identity from trusted embedding configuration. */ - default RuntimeProvisionRequest createRequest(RuntimeScope scope, + public default RuntimeProvisionRequest createRequest(RuntimeScope scope, String isolationKey) { return new RuntimeProvisionRequest(scope, isolationKey, kind()); } @@ -34,13 +34,13 @@ public interface RuntimeProvisioner extends AutoCloseable { * shared (Workspace-isolated) request. The default keeps the isolation-key * contract. */ - default RuntimeProvisionRequest createRequest(RuntimeScope scope, + public default RuntimeProvisionRequest createRequest(RuntimeScope scope, String isolationKey, String harnessSessionId) { return createRequest(scope, isolationKey); } /** Short, bounded pre-create admission only; never creates a resource or calls a worker. */ - default void reserveResource(RuntimeBindingRecord binding) { + public default void reserveResource(RuntimeBindingRecord binding) { } /** @@ -48,7 +48,7 @@ public interface RuntimeProvisioner extends AutoCloseable { * later Broker process can prove the same identity. The default ignores * the seed; such a provisioner can never pass reconciliation. */ - default CompletionStage provision( + public default CompletionStage provision( RuntimeProvisionRequest request, RuntimeProvisionSeed seed) { return provision(request); } @@ -58,7 +58,7 @@ public interface RuntimeProvisioner extends AutoCloseable { * scheduler handle. The default fails: a provisioner that opts into a * durable kind must implement the durable provisioning path. */ - default CompletionStage ensureResource( + public default CompletionStage ensureResource( RuntimeProvisionRequest request, RuntimeProvisionSeed seed, RuntimeResourceHandle knownHandle) { CompletableFuture failed = @@ -83,7 +83,7 @@ public interface RuntimeProvisioner extends AutoCloseable { * work, so hand long waits to another executor and return a pending * stage. */ - default CompletionStage reconcile( + public default CompletionStage reconcile( RuntimeProvisionRequest request, RuntimeProvisionSeed seed, RuntimeResourceHandle handle, RuntimeLease lastLease) { return CompletableFuture.completedFuture( @@ -95,7 +95,7 @@ public interface RuntimeProvisioner extends AutoCloseable { * The same backstop as {@link #reconcile} applies: the claim stays * renewed for the call, and a never-answering call is retried later. */ - default CompletionStage recoverResources(RuntimeBindingRecord binding) { + public default CompletionStage recoverResources(RuntimeBindingRecord binding) { if (binding.getRequest().isManagedContext()) { return CompletableFuture.failedFuture(new RuntimeBrokerException(409, "runtime_broker_recovery_blocked", "Workspace recovery cleanup is unavailable.", false)); @@ -103,22 +103,22 @@ public interface RuntimeProvisioner extends AutoCloseable { return CompletableFuture.completedFuture(null); } - default boolean supportsDrainedStop() { + public default boolean supportsDrainedStop() { return false; } /** Stop eligibility belongs to the saved placement, not another routed provider. */ - default boolean supportsDrainedStop(RuntimeBindingRecord binding) { + public default boolean supportsDrainedStop(RuntimeBindingRecord binding) { return binding != null && supportsKind(binding.getRequest().getProvisionerKind()) && supportsDrainedStop(); } - default CompletionStage stopDrained(RuntimeBindingRecord binding) { + public default CompletionStage stopDrained(RuntimeBindingRecord binding) { return CompletableFuture.failedFuture(new RuntimeBrokerException(409, "workspace_close_identity_unverified", "Durable worker stop is unavailable.", false)); } /** Whether saved startup identity can be observed without relaunching it. */ - default boolean supportsStartupRecovery(RuntimeResourceHandle handle) { + public default boolean supportsStartupRecovery(RuntimeResourceHandle handle) { return false; } @@ -126,7 +126,7 @@ public interface RuntimeProvisioner extends AutoCloseable { * Proves a lease this process already treats as ready still answers * attestation. The default accepts the in-memory lease. */ - default CompletionStage confirm(RuntimeProvisionRequest request, + public default CompletionStage confirm(RuntimeProvisionRequest request, RuntimeLease lease) { return CompletableFuture.completedFuture(null); } @@ -135,7 +135,7 @@ public interface RuntimeProvisioner extends AutoCloseable { * Whether a failed confirmation can be retried against the same owned, * still-live resource. Unknown implementations keep the binding fenced. */ - default boolean canRetryFailedConfirm(RuntimeLease lease) { + public default boolean canRetryFailedConfirm(RuntimeLease lease) { return false; } @@ -145,7 +145,7 @@ public interface RuntimeProvisioner extends AutoCloseable { * never kill the winning resource for the same request. The default * has nothing to release. */ - default CompletionStage release(RuntimeProvisionRequest request, + public default CompletionStage release(RuntimeProvisionRequest request, RuntimeLease lease) { return CompletableFuture.completedFuture(null); } @@ -154,7 +154,7 @@ public interface RuntimeProvisioner extends AutoCloseable { * Cheap local check whether the resource behind a lease is still * usable. The default has no resource that can die. */ - default boolean isUsable(RuntimeLease lease) { + public default boolean isUsable(RuntimeLease lease) { return true; } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimePublicationGrant.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimePublicationGrant.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimePublicationGrant.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimePublicationGrant.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimePublicationVerifier.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimePublicationVerifier.java similarity index 53% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimePublicationVerifier.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimePublicationVerifier.java index 3c61aeaf92b..5113037af32 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimePublicationVerifier.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimePublicationVerifier.java @@ -3,14 +3,14 @@ package com.alibaba.qwen.code.runtimebroker; /** Verifies a saved publication before a v3 dispatch can start. */ @FunctionalInterface public interface RuntimePublicationVerifier { - RuntimePublicationGrant verify(ToolExecutionRecord execution, + public RuntimePublicationGrant verify(ToolExecutionRecord execution, String publicationId, String token); - default java.util.Map finished(ToolExecutionRecord execution) { + public default java.util.Map finished(ToolExecutionRecord execution) { return null; } - default java.util.Map receipt(ToolExecutionRecord execution) { + public default java.util.Map receipt(ToolExecutionRecord execution) { return null; } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryEvidence.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryEvidence.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryEvidence.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryEvidence.java index fbc46ab139b..8ed13929edb 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryEvidence.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryEvidence.java @@ -31,7 +31,7 @@ public record RuntimeRecoveryEvidence(String evidenceId, Fact fact, } } - boolean matches(RuntimeProvisionSeed seed, RuntimeResourceHandle handle, + public boolean matches(RuntimeProvisionSeed seed, RuntimeResourceHandle handle, RuntimeLease lease) { return seed != null && provisionRequestId.equals(seed.getProvisionRequestId()) @@ -43,7 +43,7 @@ public record RuntimeRecoveryEvidence(String evidenceId, Fact fact, && resourceHandle.equals(handle); } - String toJson() { + public String toJson() { Map value = new LinkedHashMap<>(); value.put("version", 1); value.put("evidenceId", evidenceId); @@ -62,7 +62,7 @@ public record RuntimeRecoveryEvidence(String evidenceId, Fact fact, return new String(JsonCodec.encode(value), StandardCharsets.UTF_8); } - static RuntimeRecoveryEvidence fromJson(String json) { + public static RuntimeRecoveryEvidence fromJson(String json) { if (json == null) { return null; } diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRequestKeys.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRequestKeys.java new file mode 100644 index 00000000000..2d23ef97b22 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRequestKeys.java @@ -0,0 +1,56 @@ +package com.alibaba.qwen.code.runtimebroker; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public final class RuntimeRequestKeys { + private RuntimeRequestKeys() { } + + public static String requestKey(RuntimeProvisionRequest request) { + RuntimeScope scope = request.getScope(); + if (request.isManagedContext()) { + return digest("managed-context/1", scope.getTenantId(), + scope.getWorkspaceId(), scope.getWorkspaceGeneration(), + scope.getCanonicalCwd(), scope.getCapabilityDigest(), + scope.getIsolationClass(), request.getIsolationKey(), + request.getProvisionerKind(), request.getStorageId()); + } + return digest(scope.getTenantId(), scope.getWorkspaceId(), + scope.getWorkspaceGeneration(), scope.getCanonicalCwd(), + scope.getCapabilityDigest(), scope.getIsolationClass(), + request.getIsolationKey(), request.getProvisionerKind()); + } + + public static String scopeKey(RuntimeScope scope) { + return digest(scope.getTenantId(), scope.getWorkspaceId(), + scope.getWorkspaceGeneration(), scope.getCanonicalCwd(), + scope.getCapabilityDigest(), scope.getIsolationClass()); + } + + public static String valueKey(String value) { + return digest(value); + } + + private static String digest(String... values) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + for (String value : values) { + if (value == null) { + digest.update(ByteBuffer.allocate(Integer.BYTES) + .putInt(-1).array()); + } else { + byte[] bytes = value.getBytes(StandardCharsets.UTF_8); + digest.update(ByteBuffer.allocate(Integer.BYTES) + .putInt(bytes.length).array()); + digest.update(bytes); + } + } + return java.util.HexFormat.of().formatHex(digest.digest()); + } catch (NoSuchAlgorithmException exception) { + throw new IllegalStateException("SHA-256 is unavailable", + exception); + } + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeResourceHandle.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeResourceHandle.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeResourceHandle.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeResourceHandle.java diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRetention.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRetention.java new file mode 100644 index 00000000000..00010d1a1f3 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeRetention.java @@ -0,0 +1,16 @@ +package com.alibaba.qwen.code.runtimebroker; + +import java.time.Duration; +import java.time.Instant; + +public interface RuntimeRetention { + public enum Phase { BINDINGS, EXECUTIONS, SESSIONS } + + public record Cursor(String bindingState, Instant lastActiveAt, String bindingId, + Phase phase, String childState, String childKey) { } + + public record BatchResult(Cursor cursor, int bindingsScanned, int childrenScanned, int skipped, + int executionsDeleted, int sessionsDeleted, int bindingsDeleted) { } + + public BatchResult sweep(Duration maxAge, int batchSize, Cursor cursor); +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeScope.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeScope.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeScope.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeScope.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSession.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSession.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSession.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSession.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRecord.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRecord.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRecord.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRecord.java index 569d620aa5d..af471199b30 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRecord.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRecord.java @@ -88,12 +88,12 @@ public final class RuntimeSessionRecord { runtimeGeneration, nextState, version, activeAt); } - RuntimeSessionRecord withVersion(long nextVersion) { + public RuntimeSessionRecord withVersion(long nextVersion) { return new RuntimeSessionRecord(session, bindingId, runtimeGeneration, state, nextVersion, lastActiveAt); } - boolean sameIdentity(RuntimeSessionRecord other) { + public boolean sameIdentity(RuntimeSessionRecord other) { if (other == null || !bindingId.equals(other.bindingId) || runtimeGeneration != other.runtimeGeneration) { return false; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRepository.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRepository.java similarity index 67% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRepository.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRepository.java index 6493c0f9f67..5d3e9d0bbc7 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRepository.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionRepository.java @@ -2,11 +2,11 @@ package com.alibaba.qwen.code.runtimebroker; /** Persistence boundary for logical Runtime Sessions. */ public interface RuntimeSessionRepository { - RuntimeSessionRecord findOrCreate(RuntimeSessionRecord candidate); + public RuntimeSessionRecord findOrCreate(RuntimeSessionRecord candidate); - RuntimeSessionRecord findById(RuntimeScope scope, String runtimeSessionId); + public RuntimeSessionRecord findById(RuntimeScope scope, String runtimeSessionId); - RuntimeSessionRecord findHistorical(String tenantId, String harnessSessionId, String runtimeSessionId); + public RuntimeSessionRecord findHistorical(String tenantId, String harnessSessionId, String runtimeSessionId); /** * The Runtime Sessions among {@code runtimeSessionIds} that exist for one @@ -18,25 +18,25 @@ public interface RuntimeSessionRepository { * inventory resolve the whole candidate set without one point read per * candidate (#13986 review R1-9). */ - default java.util.List findHistoricalByIds(String tenantId, + public default java.util.List findHistoricalByIds(String tenantId, String harnessSessionId, java.util.Collection runtimeSessionIds) { throw new RuntimeBrokerException(409, "workspace_close_identity_unverified", "Harness Session inventory is unavailable", false); } - RuntimeSessionRecord compareAndSet(RuntimeSessionRecord expected, + public RuntimeSessionRecord compareAndSet(RuntimeSessionRecord expected, RuntimeSessionRecord replacement); - java.util.List findByBinding(String bindingId, long generation, + public java.util.List findByBinding(String bindingId, long generation, String afterSessionId, int limit); - long countActiveByBinding(String bindingId, long runtimeGeneration); + public long countActiveByBinding(String bindingId, long runtimeGeneration); /** * Active Runtime Sessions of one Harness Session in {@code (scope, runtimeSessionId)} * order after {@code after}. Callers re-verify the tenant and Harness Session. */ - default java.util.List findActiveByHarnessSession(String tenantId, + public default java.util.List findActiveByHarnessSession(String tenantId, String harnessSessionId, RuntimeSessionRecord after, int limit) { throw new RuntimeBrokerException(409, "workspace_close_identity_unverified", "Harness Session inventory is unavailable", false); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeTransport.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeTransport.java similarity index 83% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeTransport.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeTransport.java index 6f58926315e..1bd79ba10ab 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeTransport.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeTransport.java @@ -1,6 +1,7 @@ package com.alibaba.qwen.code.runtimebroker; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; + import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -18,13 +19,13 @@ import java.util.concurrent.CompletionStage; * completion only with {@code true}. */ public interface RuntimeTransport { - default CompletionStage> extensionControl(RuntimeBindingRecord runtime, + public default CompletionStage> extensionControl(RuntimeBindingRecord runtime, String harnessSessionId, Map operation) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, "managed_extension_unavailable", "Extension metadata controls are unavailable.", false)); } - default CompletionStage>> mcpDefinitions(RuntimeBindingRecord runtime) { + public default CompletionStage>> mcpDefinitions(RuntimeBindingRecord runtime) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, "managed_mcp_management_unavailable", "Runtime MCP definitions are unavailable.", false)); } @@ -33,7 +34,7 @@ public interface RuntimeTransport { * Re-proves the identity behind a restored lease. The default fails * closed: a transport that cannot attest can never adopt a binding. */ - default CompletionStage attest(RuntimeLease lease, + public default CompletionStage attest(RuntimeLease lease, RuntimeProvisionRequest request, RuntimeProvisionSeed seed) { CompletableFuture failed = new CompletableFuture<>(); @@ -44,42 +45,42 @@ public interface RuntimeTransport { } /** Separate CSI receipt; context attestation alone cannot prove the mounted storage. */ - default CompletionStage> attestCsi(RuntimeLease lease, + public default CompletionStage> attestCsi(RuntimeLease lease, RuntimeProvisionRequest request, RuntimeProvisionSeed seed, Map storage, Map pod) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, "workspace_csi_attestation_unavailable", "Workspace CSI attestation is unavailable.", false)); } - default CompletionStage> attestCsiFiles(RuntimeLease lease, + public default CompletionStage> attestCsiFiles(RuntimeLease lease, RuntimeProvisionRequest request, RuntimeProvisionSeed seed, Map boot, Map pod) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, "workspace_csi_attestation_unavailable", "Original file boot attestation is unavailable.", false)); } - default CompletionStage> csiFileHistory(RuntimeLease lease, + public default CompletionStage> csiFileHistory(RuntimeLease lease, RuntimeProvisionRequest request, RuntimeProvisionSeed seed, Map boot, ContextBinding binding, Map operation) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, "csi_file_history_unavailable", "Original CSI file history is unavailable.", false)); } - default CompletionStage> csiFileExecute(RuntimeLease lease, + public default CompletionStage> csiFileExecute(RuntimeLease lease, RuntimeProvisionRequest request, RuntimeProvisionSeed seed, Map boot, ContextBinding binding, String executionId) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, "csi_native_execution_unavailable", "Original CSI execution is unavailable.", false)); } - default CompletionStage csiFileFinalize(RuntimeBindingRecord runtime, ContextBinding binding, + public default CompletionStage csiFileFinalize(RuntimeBindingRecord runtime, ContextBinding binding, Map operation) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, "csi_file_finalization_unavailable", "Original CSI file finalization is unavailable.", false)); } /** Explicit comparison inputs are component expectations, not trusted CSI placement provenance. */ - default CompletionStage> acknowledgeCsi(RuntimeLease lease, RuntimeSession session, + public default CompletionStage> acknowledgeCsi(RuntimeLease lease, RuntimeSession session, Map boot, Map expectedPod, Map request, Map expectedCaptureIdentity) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, @@ -93,7 +94,7 @@ public interface RuntimeTransport { * and hold the Session's placement: its scope and, under session * isolation, its Harness Session. */ - default CompletionStage> installContext( + public default CompletionStage> installContext( RuntimeBindingRecord runtime, RuntimeSessionRecord session, String operationId, ContextBinding binding) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, @@ -101,17 +102,17 @@ public interface RuntimeTransport { "Runtime transport does not support context installation.", false)); } - CompletionStage acquire(RuntimeLease lease, + public CompletionStage acquire(RuntimeLease lease, RuntimeSession session); - default CompletionStage installPublisher(RuntimeLease lease, + public default CompletionStage installPublisher(RuntimeLease lease, RuntimeSession session, Map publisher) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, "managed_tool_publisher_unavailable", "Runtime transport does not support output publication.", false)); } - default CompletionStage> acknowledge(RuntimeLease lease, + public default CompletionStage> acknowledge(RuntimeLease lease, RuntimeSession session, Map reference, Map receipt) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, @@ -119,26 +120,26 @@ public interface RuntimeTransport { "Runtime transport does not support result acknowledgement.", false)); } - CompletionStage control(RuntimeLease lease, + public CompletionStage control(RuntimeLease lease, RuntimeSession session, Map operation); - CompletionStage> execute(RuntimeLease lease, + public CompletionStage> execute(RuntimeLease lease, RuntimeSession session, Map reference); /** Releases the worker journal payload only after the original v2 result is durable. */ - default CompletionStage acknowledgeV2(RuntimeLease lease, + public default CompletionStage acknowledgeV2(RuntimeLease lease, RuntimeSession session, Map reference) { return CompletableFuture.completedFuture(null); } - default CompletionStage installPublication(RuntimeLease lease, + public default CompletionStage installPublication(RuntimeLease lease, RuntimeSession session, RuntimePublicationGrant grant) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, "runtime_publication_unsupported", "Runtime transport does not support publication grants.", false)); } - default CompletionStage> executeV3(RuntimeLease lease, + public default CompletionStage> executeV3(RuntimeLease lease, RuntimeSession session, Map reference, Map payload, Map capture) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, @@ -146,27 +147,27 @@ public interface RuntimeTransport { "Runtime transport does not support Tool v3.", false)); } - default CompletionStage> statusV3(RuntimeLease lease, + public default CompletionStage> statusV3(RuntimeLease lease, RuntimeSession session, Map reference, long afterSequence) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, "runtime_tool_v3_unsupported", "Runtime transport does not support Tool v3 status.", false)); } - default CompletionStage> cancelV3(RuntimeLease lease, + public default CompletionStage> cancelV3(RuntimeLease lease, RuntimeSession session, Map reference) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, "runtime_tool_v3_unsupported", "Runtime transport does not support Tool v3 cancel.", false)); } - default CompletionStage> acknowledgeV3(RuntimeLease lease, + public default CompletionStage> acknowledgeV3(RuntimeLease lease, RuntimeSession session, Map reference, Map receipt) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, "runtime_tool_v3_unsupported", "Runtime transport does not support Tool v3 ACK.", false)); } - default CompletionStage> execute(RuntimeLease lease, + public default CompletionStage> execute(RuntimeLease lease, RuntimeSession session, Map reference, Map payload) { Map request = new java.util.LinkedHashMap<>(reference); @@ -175,7 +176,7 @@ public interface RuntimeTransport { return execute(lease, session, Map.copyOf(request)); } - CompletionStage> cancel(RuntimeLease lease, + public CompletionStage> cancel(RuntimeLease lease, RuntimeSession session, Map reference); /** @@ -198,7 +199,7 @@ public interface RuntimeTransport { * this shape, stripping {@code protocolVersion} and {@code lastSequence}. * The Broker does not consume the Runtime's response cursor yet. */ - default CompletionStage> status(RuntimeLease lease, + public default CompletionStage> status(RuntimeLease lease, RuntimeSession session, Map reference, long afterSequence) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, @@ -207,7 +208,7 @@ public interface RuntimeTransport { false)); } - CompletionStage release(RuntimeLease lease, + public CompletionStage release(RuntimeLease lease, RuntimeSession session); /** @@ -219,7 +220,7 @@ public interface RuntimeTransport { * same storage wedges behind it (#13800). Only a Workspace-owning * transport vouches for that distinction, so the default fails closed. */ - default CompletionStage releaseMount(RuntimeLease lease, + public default CompletionStage releaseMount(RuntimeLease lease, RuntimeSession session) { return CompletableFuture.failedFuture(new RuntimeBrokerException(501, "workspace_mount_release_unsupported", @@ -234,7 +235,7 @@ public interface RuntimeTransport { * executions — and must never call this on a mere transport failure. A * transport without durable ownership has nothing to clear. */ - default CompletionStage releaseRetired(RuntimeLease lease, + public default CompletionStage releaseRetired(RuntimeLease lease, RuntimeSession session) { return CompletableFuture.completedFuture(true); } @@ -245,7 +246,7 @@ public interface RuntimeTransport { * Session still holds is left as it is. A transport without Workspace * ownership has no mount to claim. */ - default CompletionStage reclaimMount(RuntimeLease lease, + public default CompletionStage reclaimMount(RuntimeLease lease, RuntimeSession session) { return CompletableFuture.completedFuture(null); } diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/StoreFailureException.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/StoreFailureException.java new file mode 100644 index 00000000000..7af467820f3 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/StoreFailureException.java @@ -0,0 +1,8 @@ +package com.alibaba.qwen.code.runtimebroker; + +/** A store outage, distinct from a permanent repository invariant violation. */ +public final class StoreFailureException extends IllegalStateException { + public StoreFailureException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecord.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecord.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecord.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecord.java index 3765642da96..2a02188a2b7 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecord.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecord.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.provisioning.ProviderRuntimeProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ProviderRuntimeProtocol; import java.time.Instant; import java.util.Map; @@ -22,7 +22,7 @@ public final class ToolExecutionRecord { || ProviderRuntimeProtocol.isReference(reference); } - Map cancellationBeforeDispatch() { + public Map cancellationBeforeDispatch() { if (!Integer.valueOf(3).equals(reference.get("runtimeProtocol")) && !"deferred_v3".equals(reference.get("dispatchMode"))) { return Map.of("executionStatus", "cancelled"); @@ -65,7 +65,7 @@ public final class ToolExecutionRecord { private final Long authorizedDispatchGeneration; private final Long authorizedBindingVersion; - ToolExecutionRecord(String executionCallId, String idempotencyKey, + public ToolExecutionRecord(String executionCallId, String idempotencyKey, String bindingId, long runtimeGeneration, String harnessSessionId, String runtimeSessionId, String turnId, String toolCallId, String requestDigest, @@ -81,7 +81,7 @@ public final class ToolExecutionRecord { dispatchGeneration, version, settledAt, null, null); } - ToolExecutionRecord(String executionCallId, String idempotencyKey, + public ToolExecutionRecord(String executionCallId, String idempotencyKey, String bindingId, long runtimeGeneration, String harnessSessionId, String runtimeSessionId, String turnId, String toolCallId, String requestDigest, Map reference, @@ -97,7 +97,7 @@ public final class ToolExecutionRecord { null, null); } - ToolExecutionRecord(String executionCallId, String idempotencyKey, + public ToolExecutionRecord(String executionCallId, String idempotencyKey, String bindingId, long runtimeGeneration, String harnessSessionId, String runtimeSessionId, String turnId, String toolCallId, String requestDigest, Map reference, @@ -319,19 +319,19 @@ public final class ToolExecutionRecord { return isSettled() || state == State.ABANDONED; } - boolean needsReconciliation() { + public boolean needsReconciliation() { return state == State.EXECUTING || state == State.CANCEL_REQUESTED || state == State.UNKNOWN; } - boolean belongsTo(RuntimeSessionRecord session) { + public boolean belongsTo(RuntimeSessionRecord session) { return bindingId.equals(session.getBindingId()) && runtimeGeneration == session.getRuntimeGeneration() && harnessSessionId.equals(session.getSession().getHarnessSessionId()) && runtimeSessionId.equals(session.getRuntimeSessionId()); } - ToolExecutionRecord abandon(RuntimeBindingRecord binding, Instant time) { + public ToolExecutionRecord abandon(RuntimeBindingRecord binding, Instant time) { if (isTerminal() || binding.getState() != RuntimeBindingRecord.State.LOST || !bindingId.equals(binding.getBindingId()) || runtimeGeneration != binding.getGeneration() @@ -365,7 +365,7 @@ public final class ToolExecutionRecord { && authorizedBindingVersion < sealedBindingVersion; } - ToolExecutionRecord authorizeDispatch(long bindingVersion) { + public ToolExecutionRecord authorizeDispatch(long bindingVersion) { if (state != State.DISPATCHING || cancelRequested || dispatchOwner == null || authorizedDispatchGeneration != null) { throw new IllegalArgumentException("Dispatch authorization requires an unmarked claim"); @@ -378,7 +378,7 @@ public final class ToolExecutionRecord { dispatchGeneration, bindingVersion); } - boolean sameAuthorization(ToolExecutionRecord other) { + public boolean sameAuthorization(ToolExecutionRecord other) { return other != null && Objects.equals(authorizedDispatchGeneration, other.authorizedDispatchGeneration) && Objects.equals(authorizedBindingVersion, other.authorizedBindingVersion); @@ -410,7 +410,7 @@ public final class ToolExecutionRecord { dispatchGeneration, version, completionTime); } - static Map cancelledBeforeV3Start() { + public static Map cancelledBeforeV3Start() { Map result = new java.util.LinkedHashMap<>(); result.put("executionStatus", "not_started"); result.put("responseParts", java.util.List.of()); @@ -434,7 +434,7 @@ public final class ToolExecutionRecord { return resolveUnsettled(resolutionResult, resolutionTime); } - ToolExecutionRecord resolveUnsettled( + public ToolExecutionRecord resolveUnsettled( Map resolutionResult, Instant resolutionTime) { if (!needsReconciliation()) { throw new IllegalStateException("execution does not require reconciliation"); @@ -454,20 +454,20 @@ public final class ToolExecutionRecord { resolutionTime); } - ToolExecutionRecord withDispatch(String owner, Instant leaseUntil, + public ToolExecutionRecord withDispatch(String owner, Instant leaseUntil, long nextDispatchGeneration, State nextState) { return copy(nextState, executionStatus, result, lastSequence, cancelRequested, owner, leaseUntil, nextDispatchGeneration, version, settledAt); } - ToolExecutionRecord withVersion(long nextVersion) { + public ToolExecutionRecord withVersion(long nextVersion) { return copy(state, executionStatus, result, lastSequence, cancelRequested, dispatchOwner, dispatchLeaseUntil, dispatchGeneration, nextVersion, settledAt); } - boolean sameIdentity(ToolExecutionRecord other) { + public boolean sameIdentity(ToolExecutionRecord other) { return other != null && executionCallId.equals(other.executionCallId) && idempotencyKey.equals(other.idempotencyKey) @@ -481,7 +481,7 @@ public final class ToolExecutionRecord { && BrokerValues.sameJsonMap(reference, other.reference); } - boolean sameDispatch(ToolExecutionRecord other) { + public boolean sameDispatch(ToolExecutionRecord other) { return other != null && Objects.equals(dispatchOwner, other.dispatchOwner) && Objects.equals(dispatchLeaseUntil, @@ -489,7 +489,7 @@ public final class ToolExecutionRecord { && dispatchGeneration == other.dispatchGeneration; } - boolean hasLiveDispatchAt(Instant now) { + public boolean hasLiveDispatchAt(Instant now) { return dispatchOwner != null && dispatchLeaseUntil.isAfter(now); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRepository.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRepository.java similarity index 81% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRepository.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRepository.java index 39267d00461..e00b5c07c19 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRepository.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRepository.java @@ -2,23 +2,23 @@ package com.alibaba.qwen.code.runtimebroker; import java.time.Duration; import java.time.Instant; -import java.util.Map; import java.util.List; +import java.util.Map; /** Persistence boundary for idempotent Tool execution state. */ public interface ToolExecutionRepository { - ToolExecutionRecord findOrCreate(ToolExecutionRecord candidate); + public ToolExecutionRecord findOrCreate(ToolExecutionRecord candidate); - ToolExecutionRecord findByExecutionCallId(String executionCallId); + public ToolExecutionRecord findByExecutionCallId(String executionCallId); - ToolExecutionRecord findByIdempotencyKey(String idempotencyKey); + public ToolExecutionRecord findByIdempotencyKey(String idempotencyKey); /** Succeeds only while the stored record still matches {@code expected} * on immutable identity, dispatch claim and version, the record is * neither terminal nor UNKNOWN, and the caller presents the stored owner * and generation with an unexpired lease; returns null otherwise. * Implementations must compare and write atomically. */ - ToolExecutionRecord compareAndSet(ToolExecutionRecord expected, + public ToolExecutionRecord compareAndSet(ToolExecutionRecord expected, ToolExecutionRecord replacement, String owner, long dispatchGeneration); @@ -28,17 +28,17 @@ public interface ToolExecutionRepository { * a record that is neither terminal nor UNKNOWN is never written: its * owner gets the stored record back and any other caller gets null. For * a terminal or UNKNOWN record the call returns null. */ - ToolExecutionRecord claimDispatch(String executionCallId, String owner, + public ToolExecutionRecord claimDispatch(String executionCallId, String owner, Duration leaseDuration); - ToolExecutionRecord renewDispatch(String executionCallId, String owner, + public ToolExecutionRecord renewDispatch(String executionCallId, String owner, long dispatchGeneration, Duration leaseDuration); /** Records cancellation intent without requiring the dispatch claim. A * PREPARED execution settles as cancelled immediately, since no * dispatcher exists to observe the intent. Returns null when the record * is missing, already terminal, or no longer at expectedVersion. */ - ToolExecutionRecord requestCancel(String executionCallId, + public ToolExecutionRecord requestCancel(String executionCallId, long expectedVersion); /** Evidence settlement of a PREPARED execution that was never @@ -46,26 +46,26 @@ public interface ToolExecutionRepository { * through the owner, never through a dispatch claim. Requires the * immutable identity, the current version and state PREPARED; returns * null when already terminal or the guard mismatches. */ - ToolExecutionRecord settlePrepared(ToolExecutionRecord expected, + public ToolExecutionRecord settlePrepared(ToolExecutionRecord expected, Map result, Instant settlementTime); /** Settles an UNKNOWN execution through recovery reconciliation. Requires * the immutable identity, the current version and state UNKNOWN, but no * dispatch claim: a takeover-fenced record's claim is expired by * construction, so implementations must not add a lease predicate. */ - ToolExecutionRecord resolveUnknown(ToolExecutionRecord expected, + public ToolExecutionRecord resolveUnknown(ToolExecutionRecord expected, Map resolutionResult, Instant resolutionTime); /** Evidence-only settlement of EXECUTING, CANCEL_REQUESTED or UNKNOWN. * Atomically checks identity and version, without claiming or fencing a * dispatch. Preserves the stored dispatch identity and cancellation intent. */ - ToolExecutionRecord resolveUnsettled(ToolExecutionRecord expected, + public ToolExecutionRecord resolveUnsettled(ToolExecutionRecord expected, Map resolutionResult, Instant resolutionTime); /** At most 100 potentially dispatched executions belonging to this exact * Session and binding generation, ordered by execution ID hash. The * exclusive cursor is an execution ID, including one already settled. */ - List findUnsettled(RuntimeSessionRecord session, + public List findUnsettled(RuntimeSessionRecord session, String afterExecutionCallId, int limit); /** Non-terminal background-process ledger rows belonging to this exact @@ -76,7 +76,7 @@ public interface ToolExecutionRepository { * the process's physical end. At most 100 rows per page, and a short * page means the scan is exhausted; the exclusive cursor is an * execution ID, including one already settled. */ - List findBackgroundProcesses(RuntimeSessionRecord session, + public List findBackgroundProcesses(RuntimeSessionRecord session, String afterExecutionCallId, int limit); /** The hold rows (a background Shell process or a Monitor watch) of @@ -93,7 +93,7 @@ public interface ToolExecutionRepository { * materialize non-hold rows it scans past (#13986 review R1-7). At most * 100 rows per page, and a short page means the scan is exhausted; the * exclusive cursor is an execution ID, including one already settled. */ - List findHoldRowsByHarnessSession(String harnessSessionId, + public List findHoldRowsByHarnessSession(String harnessSessionId, String afterExecutionCallId, int limit); /** @@ -105,21 +105,21 @@ public interface ToolExecutionRepository { * This inventory is not a snapshot or proof that physical writers stopped. * Custom repositories must implement it before supporting retirement. */ - default List findByBinding(String bindingId, + public default List findByBinding(String bindingId, long runtimeGeneration, String afterExecutionCallId, int limit) { throw new UnsupportedOperationException( "Binding execution inventory is unavailable"); } - boolean hasActiveByRuntimeSession(String runtimeSessionId); + public boolean hasActiveByRuntimeSession(String runtimeSessionId); - boolean hasActiveByRuntimeSession(String bindingId, long runtimeGeneration, + public boolean hasActiveByRuntimeSession(String bindingId, long runtimeGeneration, String runtimeSessionId); /** Like {@link #hasActiveByRuntimeSession(String, long, String)}, but * skipping the named executions: the release drain asks whether anything * but the Session's own background processes still runs. */ - boolean hasActiveByRuntimeSession(String bindingId, long runtimeGeneration, + public boolean hasActiveByRuntimeSession(String bindingId, long runtimeGeneration, String runtimeSessionId, java.util.Set excludingExecutionCallIds); @@ -131,13 +131,13 @@ public interface ToolExecutionRepository { * fences on the mount. Settled rows never change, so this stays true * until that recovery runs. The default fails closed like * findByBinding's. */ - default boolean hasUnprovenCaptureByRuntimeSession(String bindingId, + public default boolean hasUnprovenCaptureByRuntimeSession(String bindingId, long runtimeGeneration, String runtimeSessionId) { throw new UnsupportedOperationException( "Unproven-capture probe is unavailable"); } - static boolean captureStopUnproven(Map result) { + public static boolean captureStopUnproven(Map result) { return result != null && result.get("capture") instanceof Map capture && !"complete".equals(capture.get("captureStatus")) && !"detached".equals(capture.get("captureStatus")); @@ -145,5 +145,5 @@ public interface ToolExecutionRepository { /** Any nonterminal execution still points at this binding generation. * UNKNOWN counts as active; terminal uncertainty is not physical stop proof. */ - boolean hasActiveByBinding(String bindingId, long runtimeGeneration); + public boolean hasActiveByBinding(String bindingId, long runtimeGeneration); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolHoldRow.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolHoldRow.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolHoldRow.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/ToolHoldRow.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspaceCsiRuntimeIdentity.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspaceCsiRuntimeIdentity.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspaceCsiRuntimeIdentity.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspaceCsiRuntimeIdentity.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspaceExecutionProfile.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspaceExecutionProfile.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspaceExecutionProfile.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspaceExecutionProfile.java diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspacePhysicalTransport.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspacePhysicalTransport.java new file mode 100644 index 00000000000..467f6b8bd81 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/WorkspacePhysicalTransport.java @@ -0,0 +1,29 @@ +package com.alibaba.qwen.code.runtimebroker; + +import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; + +import java.util.Map; +import java.util.concurrent.CompletionStage; + +public interface WorkspacePhysicalTransport extends RuntimeTransport { + public interface ExtensionPreparation { + CompletionStage> prepare(RuntimeBindingRecord runtime, String harnessSessionId, + Map operation); + } + + void configureExtensionPreparation(ExtensionPreparation preparation); + CompletionStage> installExtensionPackage(RuntimeBindingRecord runtime, String digest, java.nio.file.Path archive); + + public CompletionStage> extensionControl(RuntimeBindingRecord runtime, String harnessSessionId, + ContextBinding binding, Map operation); + + public CompletionStage activateWorkspace(RuntimeBindingRecord runtime, + RuntimeSessionRecord sessionRecord, ContextBinding binding, boolean active); + + public CompletionStage installPublisherV3(RuntimeLease lease, + RuntimeSession session, Map publisher); + + public CompletionStage> executeV3(RuntimeLease lease, + RuntimeSession session, Map reference, + Map capture); +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBinding.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBinding.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBinding.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBinding.java index cfac14b676f..2558d37df58 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBinding.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBinding.java @@ -16,7 +16,7 @@ import java.util.Objects; * fixtures in {@code managed-workspace-binding-v1.fixtures.json} pin both. */ public final class ContextBinding { - static final String DOMAIN_TAG = "qwen-managed-context-binding-v1"; + public static final String DOMAIN_TAG = "qwen-managed-context-binding-v1"; private final String tenantId; private final String workspaceId; @@ -95,7 +95,7 @@ public final class ContextBinding { // Each item is a 4-byte big-endian length and its UTF-8 bytes. Integers // are decimal strings, so a JavaScript peer never needs a 64-bit Number. - byte[] encode() { + public byte[] encode() { ByteArrayOutputStream encoded = new ByteArrayOutputStream(); for (String item : new String[] {DOMAIN_TAG, tenantId, workspaceId, Long.toString(workspaceGeneration), storageId, cwdRelative, diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ResolvedWorkspace.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ResolvedWorkspace.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ResolvedWorkspace.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ResolvedWorkspace.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccess.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccess.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccess.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccess.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessPolicy.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessPolicy.java similarity index 78% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessPolicy.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessPolicy.java index 402c79190a9..e1398762121 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessPolicy.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessPolicy.java @@ -7,5 +7,5 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; */ @FunctionalInterface public interface WorkspaceAccessPolicy { - WorkspaceAccess accessFor(WorkspaceActor actor, WorkspaceRecord workspace); + public WorkspaceAccess accessFor(WorkspaceActor actor, WorkspaceRecord workspace); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceActor.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceActor.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceActor.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceActor.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalog.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalog.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalog.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalog.java index f4677d6624c..a6cc1d12047 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalog.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalog.java @@ -37,9 +37,9 @@ public final class WorkspaceCatalog { public WorkspacePage list(WorkspaceActor actor, String afterWorkspaceId, int limit) { requireActor(actor); - if (limit < 1 || limit > ConfiguredWorkspaceRegistry.MAXIMUM_PAGE_SIZE) { + if (limit < 1 || limit > WorkspaceRegistry.MAXIMUM_PAGE_SIZE) { throw new IllegalArgumentException("limit must be between 1 and " - + ConfiguredWorkspaceRegistry.MAXIMUM_PAGE_SIZE); + + WorkspaceRegistry.MAXIMUM_PAGE_SIZE); } if (afterWorkspaceId != null && !WorkspaceValues.isIdentifier(afterWorkspaceId)) { @@ -52,7 +52,7 @@ public final class WorkspaceCatalog { // Full batches regardless of the caller's limit: records the actor // cannot read are skipped, and small batches would turn a sparse // listing into one Registry call per record. - int batchSize = ConfiguredWorkspaceRegistry.MAXIMUM_PAGE_SIZE; + int batchSize = WorkspaceRegistry.MAXIMUM_PAGE_SIZE; scan: while (true) { List batch = registry.page(actor.getTenantId(), diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceException.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceException.java similarity index 81% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceException.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceException.java index cf681c8d5c0..071bb4ba3a5 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceException.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceException.java @@ -14,27 +14,27 @@ public final class WorkspaceException extends RuntimeException { this.code = code; } - static WorkspaceException workspaceRequired() { + public static WorkspaceException workspaceRequired() { return new WorkspaceException(400, "workspace_required", "Select a Workspace; the tenant has no usable default"); } - static WorkspaceException invalidCwd() { + public static WorkspaceException invalidCwd() { return new WorkspaceException(400, "invalid_cwd", "cwdRelative is not a valid Workspace-relative directory"); } - static WorkspaceException workspaceNotFound() { + public static WorkspaceException workspaceNotFound() { return new WorkspaceException(404, "workspace_not_found", "Workspace not found"); } - static WorkspaceException workspaceForbidden() { + public static WorkspaceException workspaceForbidden() { return new WorkspaceException(403, "workspace_forbidden", "Creating a Session in this Workspace is not allowed"); } - static WorkspaceException workspaceUnavailable() { + public static WorkspaceException workspaceUnavailable() { return new WorkspaceException(409, "workspace_unavailable", "Workspace does not accept new Sessions"); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspacePage.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspacePage.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspacePage.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspacePage.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecord.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecord.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecord.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecord.java index 6a09d2ae7aa..da710f5e408 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecord.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecord.java @@ -9,8 +9,8 @@ import java.util.Objects; * with. */ public final class WorkspaceRecord { - static final int MAXIMUM_STORAGE_ID_LENGTH = 256; - static final int MAXIMUM_REFERENCE_LENGTH = 512; + public static final int MAXIMUM_STORAGE_ID_LENGTH = 256; + public static final int MAXIMUM_REFERENCE_LENGTH = 512; private final String tenantId; private final String workspaceId; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRegistry.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRegistry.java similarity index 78% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRegistry.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRegistry.java index 562b95227b4..1de0f6c641a 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRegistry.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRegistry.java @@ -10,8 +10,9 @@ import java.util.Optional; * product directory, never from a caller-supplied path. */ public interface WorkspaceRegistry { + int MAXIMUM_PAGE_SIZE = 1000; /** Returns the record, or empty when this tenant has no such Workspace. */ - Optional find(String tenantId, String workspaceId); + public Optional find(String tenantId, String workspaceId); /** * Returns up to {@code limit} of the tenant's records in byte order of @@ -20,9 +21,9 @@ public interface WorkspaceRegistry { * means that no records follow. Implementations accept any limit from 1 * to 1000. */ - List page(String tenantId, String afterWorkspaceId, + public List page(String tenantId, String afterWorkspaceId, int limit); /** Returns the tenant's configured default Workspace ID, if any. */ - Optional defaultWorkspaceId(String tenantId); + public Optional defaultWorkspaceId(String tenantId); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRelativePath.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRelativePath.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRelativePath.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRelativePath.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceSelection.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceSelection.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceSelection.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceSelection.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceState.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceState.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceState.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceState.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceValues.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceValues.java similarity index 81% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceValues.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceValues.java index b4b35fdd314..24e8737f99c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceValues.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceValues.java @@ -2,8 +2,8 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; import java.util.regex.Pattern; -final class WorkspaceValues { - static final int MAXIMUM_TEXT_CODE_POINTS = 512; +public final class WorkspaceValues { + public static final int MAXIMUM_TEXT_CODE_POINTS = 512; private static final Pattern IDENTIFIER = Pattern.compile( "[A-Za-z0-9._:-]{1,128}"); @@ -11,11 +11,11 @@ final class WorkspaceValues { private WorkspaceValues() { } - static boolean isIdentifier(String value) { + public static boolean isIdentifier(String value) { return value != null && IDENTIFIER.matcher(value).matches(); } - static String requireIdentifier(String value, String name) { + public static String requireIdentifier(String value, String name) { if (!isIdentifier(value)) { throw new IllegalArgumentException(name + " must match [A-Za-z0-9._:-]{1,128}"); @@ -23,7 +23,7 @@ final class WorkspaceValues { return value; } - static String requirePrintableAscii(String value, String name, + public static String requirePrintableAscii(String value, String name, int maximumLength) { boolean valid = value != null && !value.isEmpty() && value.length() <= maximumLength; @@ -38,7 +38,7 @@ final class WorkspaceValues { return value; } - static String requireText(String value, String name) { + public static String requireText(String value, String name) { if (value == null || !isWellFormed(value) || hasControl(value)) { throw new IllegalArgumentException(name + " must be well-formed text without control characters"); @@ -51,14 +51,14 @@ final class WorkspaceValues { return value; } - static long requirePositive(long value, String name) { + public static long requirePositive(long value, String name) { if (value < 1) { throw new IllegalArgumentException(name + " must be positive"); } return value; } - static boolean isWellFormed(String value) { + public static boolean isWellFormed(String value) { int index = 0; while (index < value.length()) { char character = value.charAt(index); @@ -78,7 +78,7 @@ final class WorkspaceValues { } // Unicode category Cc: C0 controls, DEL and C1 controls. - static boolean hasControl(String value) { + public static boolean hasControl(String value) { return value.codePoints().anyMatch(Character::isISOControl); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceView.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceView.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceView.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceView.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeKinds.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/ExternalRuntimeKinds.java similarity index 61% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeKinds.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/ExternalRuntimeKinds.java index f4e24ea9d69..d78ba2861e7 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeKinds.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/ExternalRuntimeKinds.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.provisioning; +package com.alibaba.qwen.code.runtimebroker.protocol; /** * The external Runtime kind family: the shipped {@code external-scratch} profile @@ -16,17 +16,17 @@ public final class ExternalRuntimeKinds { } public static boolean isExternal(String provisionerKind) { - return ExternalRuntimeProvisioner.KIND.equals(provisionerKind) - || ExternalWorkspaceProvisioner.KIND.equals(provisionerKind); + return com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH.equals(provisionerKind) + || com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE.equals(provisionerKind); } /** Whether the kind binds registered EXTERNAL storage. */ public static boolean isWorkspace(String provisionerKind) { - return ExternalWorkspaceProvisioner.KIND.equals(provisionerKind); + return com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE.equals(provisionerKind); } /** The provisioner names an operator can configure today, for messages. */ public static String supported() { - return ExternalRuntimeProvisioner.KIND + " or " + ExternalWorkspaceProvisioner.KIND; + return com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH + " or " + com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE; } } diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/KubernetesNames.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/KubernetesNames.java new file mode 100644 index 00000000000..187d8792c82 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/KubernetesNames.java @@ -0,0 +1,20 @@ +package com.alibaba.qwen.code.runtimebroker.protocol; + +public final class KubernetesNames { + private KubernetesNames() { } + + public static void dnsLabel(String value) { + if (value == null || !value.matches("[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?")) { + throw new IllegalArgumentException("Kubernetes name must be a DNS label"); + } + } + + public static void dnsSubdomain(String value) { + if (value == null || value.length() > 253) { + throw new IllegalArgumentException("Kubernetes name must be a DNS subdomain"); + } + for (String label : value.split("\\.", -1)) { + dnsLabel(label); + } + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ManagedContextProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/ManagedContextProtocol.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ManagedContextProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/ManagedContextProtocol.java index 2d4af5666a4..32ec0dbe66b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ManagedContextProtocol.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/ManagedContextProtocol.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.provisioning; +package com.alibaba.qwen.code.runtimebroker.protocol; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONReader; @@ -126,7 +126,7 @@ public final class ManagedContextProtocol { } } - static URI ready(Map ready, Map boot) { + public static URI ready(Map ready, Map boot) { require(ready.keySet().equals(READY_KEYS) && "ready".equals(ready.get("type")) && exactNumber(ready.get("version"), 2) @@ -214,7 +214,7 @@ public final class ManagedContextProtocol { } } - static RuntimeBrokerException invalidResponse() { + public static RuntimeBrokerException invalidResponse() { return new RuntimeBrokerException(502, "managed_runtime_attestation_invalid", "Managed context response is invalid.", false); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ProviderRuntimeProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/ProviderRuntimeProtocol.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ProviderRuntimeProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/ProviderRuntimeProtocol.java index adddd08f1a9..a2914f369bd 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ProviderRuntimeProtocol.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/ProviderRuntimeProtocol.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.provisioning; +package com.alibaba.qwen.code.runtimebroker.protocol; import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; @@ -12,8 +12,8 @@ import java.util.regex.Pattern; public final class ProviderRuntimeProtocol { public static final String NAME = "managed-runtime-provider/1"; public static final String PATH = "/internal/managed-runtime/provider/v1/control"; - static final int CONTROL_LIMIT_BYTES = 1024 * 1024; - static final int HISTORY_LIMIT_BYTES = 8 * 1024 * 1024; + public static final int CONTROL_LIMIT_BYTES = 1024 * 1024; + public static final int HISTORY_LIMIT_BYTES = 8 * 1024 * 1024; private static final Set IDENTITY_FIELDS = Set.of("sessionId", "promptId", "callId", "capabilityDigest", "policyRevision"); private static final Set REFERENCE_FIELDS = Set.of("sessionId", diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/RuntimeProviderKinds.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/RuntimeProviderKinds.java new file mode 100644 index 00000000000..49cdd3986a4 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/protocol/RuntimeProviderKinds.java @@ -0,0 +1,9 @@ +package com.alibaba.qwen.code.runtimebroker.protocol; + +public final class RuntimeProviderKinds { + public static final String LOCAL_PROCESS = "local-process"; + public static final String SSH_PROCESS = "ssh-process"; + public static final String EXTERNAL_SCRATCH = "external-scratch"; + public static final String EXTERNAL_WORKSPACE = "external-workspace"; + private RuntimeProviderKinds() { } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaContract.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/schema/ManagedSchemaContract.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaContract.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/schema/ManagedSchemaContract.java index 1aacd052515..821a35b65cb 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaContract.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/schema/ManagedSchemaContract.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.managedschema; +package com.alibaba.qwen.code.runtimebroker.schema; import java.util.Set; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaKeys.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/schema/ManagedSchemaKeys.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaKeys.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/schema/ManagedSchemaKeys.java index 799270c8e56..c630cbc8090 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaKeys.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/schema/ManagedSchemaKeys.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.managedschema; +package com.alibaba.qwen.code.runtimebroker.schema; import java.nio.ByteBuffer; import java.nio.CharBuffer; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/security/ProtectedSecret.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/security/ProtectedSecret.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/security/ProtectedSecret.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/security/ProtectedSecret.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/security/SecretProtector.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/security/SecretProtector.java similarity index 52% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/security/SecretProtector.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/security/SecretProtector.java index ad06349028c..50fb19abf71 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/security/SecretProtector.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/security/SecretProtector.java @@ -2,7 +2,7 @@ package com.alibaba.qwen.code.runtimebroker.security; /** Protects Runtime credentials before they enter durable storage. */ public interface SecretProtector { - ProtectedSecret protect(String context, byte[] plaintext); + public ProtectedSecret protect(String context, byte[] plaintext); - byte[] unprotect(String context, ProtectedSecret protectedSecret); + public byte[] unprotect(String context, ProtectedSecret protectedSecret); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeClient.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/ExternalRuntimeClient.java similarity index 69% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeClient.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/ExternalRuntimeClient.java index dfa61d4b6a4..d51f71cdd28 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeClient.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/ExternalRuntimeClient.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.provisioning; +package com.alibaba.qwen.code.runtimebroker.spi; import java.util.Map; import java.util.concurrent.CompletionStage; @@ -10,8 +10,8 @@ import java.util.concurrent.CompletionStage; */ public interface ExternalRuntimeClient { /** Idempotent on {@code provisionRequestId}; returns {@code handle} and {@code endpoint}. */ - CompletionStage> provision(Map request); + public CompletionStage> provision(Map request); /** Returns {@code state} RUNNING, STARTING, GONE or UNKNOWN, and {@code endpoint} when RUNNING. */ - CompletionStage> observe(Map request); + public CompletionStage> observe(Map request); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalStorageRegistry.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/ExternalStorageRegistry.java similarity index 90% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalStorageRegistry.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/ExternalStorageRegistry.java index f59fe78c3b5..038f474087d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalStorageRegistry.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/ExternalStorageRegistry.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.provisioning; +package com.alibaba.qwen.code.runtimebroker.spi; import java.util.Optional; @@ -21,10 +21,10 @@ public interface ExternalStorageRegistry { * @param tenantWorkspaceId the alias/Workspace id the request selected * @return the registration, or empty when this tenant registered no such alias */ - Optional find(String tenantId, String tenantWorkspaceId); + public Optional find(String tenantId, String tenantWorkspaceId); /** One persisted registration, in the shape the boot document needs. */ - record RegisteredExternalStorage(String providerId, String physicalKey, + public record RegisteredExternalStorage(String providerId, String physicalKey, long registrationRevision, String tenantWorkspaceId, String aliasPath, String mountRoot) { /** diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/KubernetesMaintenanceClient.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/KubernetesMaintenanceClient.java new file mode 100644 index 00000000000..63a9a5f5167 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/KubernetesMaintenanceClient.java @@ -0,0 +1,30 @@ +package com.alibaba.qwen.code.runtimebroker.spi; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletionStage; +public interface KubernetesMaintenanceClient extends KubernetesRuntimeClient { + com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentReclamation.Baseline reclamationBaseline( + com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord original, + com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker.ResidentWorkspace selected, + String finalizer, java.time.Instant now); + com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentReclamation.Receipt reclamationReceipt( + com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord original, + com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker.ResidentWorkspace selected, + com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentReclamation.Baseline baseline, + Map pod, java.time.Instant now); + + record DeleteResponse(int status, byte[] body, boolean complete) { } + java.util.concurrent.CompletionStage verifyProtection( + com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration registration, + com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity protection); + CompletionStage> get(String resource, String namespace, String name); + CompletionStage> getCluster(String resource, String name); + CompletionStage> create(String resource, String namespace, Map body); + CompletionStage deletePod(String namespace, String podName, byte[] deleteOptions); + CompletionStage patchPodFinalizers(String namespace, String podName, byte[] patch); + CompletionStage> getNodeLease(String nodeName); + CompletionStage>> listPersistentVolumes(); + CompletionStage>> listVolumeAttachments(); + CompletionStage>> listPods(String namespace); + CompletionStage readCsiPodLog(String podName); +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeClient.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/KubernetesRuntimeClient.java similarity index 64% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeClient.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/KubernetesRuntimeClient.java index 88f4c5d0d8d..b1d7846302c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeClient.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/KubernetesRuntimeClient.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.provisioning; +package com.alibaba.qwen.code.runtimebroker.spi; import java.util.Map; import java.util.concurrent.CompletionStage; @@ -6,18 +6,18 @@ import java.util.concurrent.CompletionStage; /** Explicit API operations needed for Runtime resources and their storage identity. */ public interface KubernetesRuntimeClient { /** Returns null only when the API authoritatively answers 404. */ - CompletionStage> get(String resource, String namespace, String name); + public CompletionStage> get(String resource, String namespace, String name); /** Read-only cluster objects; implementations must reject unsupported resource kinds. */ - default CompletionStage> getCluster(String resource, String name) { + public default CompletionStage> getCluster(String resource, String name) { return java.util.concurrent.CompletableFuture.failedFuture( new UnsupportedOperationException("Cluster reads are unavailable")); } - CompletionStage> create(String resource, String namespace, Map body); + public CompletionStage> create(String resource, String namespace, Map body); /** Bounded current log segment; rotation and kubelet container selection are not proven here. */ - default CompletionStage readCsiPodLog(String podName) { + public default CompletionStage readCsiPodLog(String podName) { return java.util.concurrent.CompletableFuture.failedFuture( new UnsupportedOperationException("CSI log reads are unavailable")); } diff --git a/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/SshConnections.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/SshConnections.java new file mode 100644 index 00000000000..d1a3a8d5c95 --- /dev/null +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/SshConnections.java @@ -0,0 +1,24 @@ +package com.alibaba.qwen.code.runtimebroker.spi; + +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; + +import java.net.URI; +import java.util.Map; + +public interface SshConnections extends AutoCloseable { + public Map bootstrap(SshRuntimeNode node, Map fields); + + public Map upload(SshRuntimeNode node, Map fields); + + public void register(RuntimeLease lease, SshRuntimeNode node); + + public void registerDirect(RuntimeLease lease); + + public URI resolveEndpoint(RuntimeLease lease); + + public URI publicationEndpoint(SshRuntimeNode node); + + public void forget(RuntimeLease lease); + + public void close(); +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeNode.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/SshRuntimeNode.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeNode.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/SshRuntimeNode.java index 5dff8646ac7..947b02397c1 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeNode.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/SshRuntimeNode.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.provisioning; +package com.alibaba.qwen.code.runtimebroker.spi; import com.alibaba.qwen.code.runtimebroker.BrokerValues; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeStorage.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/SshRuntimeStorage.java similarity index 89% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeStorage.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/SshRuntimeStorage.java index e9d6a39fe3e..24381454dd7 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeStorage.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/spi/SshRuntimeStorage.java @@ -1,6 +1,7 @@ -package com.alibaba.qwen.code.runtimebroker.provisioning; +package com.alibaba.qwen.code.runtimebroker.spi; import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import java.util.Map; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocol.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocol.java index 77fc451cdb4..0c69a1fca3d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocol.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocol.java @@ -6,7 +6,8 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; + import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; @@ -32,7 +33,7 @@ public final class CsiFileFinalizeProtocol { WorkspaceCsiRuntimeIdentity.map(handle.get("mount")), operation); } - static Map request(Map boot, RuntimeProvisionRequest original, ContextBinding binding, + public static Map request(Map boot, RuntimeProvisionRequest original, ContextBinding binding, Map pod, Map mount, Map operation) { ManagedCsiFilesProtocol.validateBoot(boot); require(Long.valueOf(6).equals(BrokerValues.exactLong(boot.get("version"))) diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileHistoryProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileHistoryProtocol.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileHistoryProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileHistoryProtocol.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocol.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocol.java index ad2a11248e8..cedaaaa8e31 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocol.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocol.java @@ -6,7 +6,7 @@ import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import com.fasterxml.jackson.databind.ObjectMapper; import java.nio.charset.StandardCharsets; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocol.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocol.java index ec8af92d63e..4c388424ba4 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocol.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocol.java @@ -4,7 +4,7 @@ import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.type.TypeReference; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocol.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocol.java index fce539a41cb..3197d839ba8 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocol.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocol.java @@ -6,8 +6,7 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; -import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesHttpRuntimeClient; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.type.TypeReference; @@ -71,7 +70,7 @@ public final class ManagedCsiProtocol { text.codePoints().forEach(point -> require(point > 31 && (point < 127 || point > 159) && (point < 0xd800 || point > 0xdfff))); } - KubernetesHttpRuntimeClient.dnsLabel(string(storage, "namespace")); + com.alibaba.qwen.code.runtimebroker.protocol.KubernetesNames.dnsLabel(string(storage, "namespace")); require("diskplugin.csi.alibabacloud.com".equals(storage.get("driver"))); require(string(storage, "diskSerial").matches("[A-Za-z0-9._:-]{1,128}")); for (String field : Set.of("registrationRevision", "reservationRevision")) { @@ -145,7 +144,7 @@ public final class ManagedCsiProtocol { require(pod.keySet().equals(Set.of("uid", "namespace", "nodeName")) && string(pod, "uid").matches("[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}") && namespace.equals(pod.get("namespace"))); - KubernetesHttpRuntimeClient.dnsSubdomain(string(pod, "nodeName")); + com.alibaba.qwen.code.runtimebroker.protocol.KubernetesNames.dnsSubdomain(string(pod, "nodeName")); } catch (RuntimeException failure) { throw new IllegalArgumentException("Managed CSI Pod identity is unavailable."); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionCommandProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionCommandProtocol.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionCommandProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionCommandProtocol.java index f0aa4885164..98c5043a726 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionCommandProtocol.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionCommandProtocol.java @@ -4,14 +4,14 @@ import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.ManagedExtensionExecutionProfile; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import java.util.Map; import java.util.Set; /** Extension preprocessing retains the ordinary tool Session and its workspace mount. */ public final class ManagedExtensionCommandProtocol { - static final String PATH = "/internal/managed-runtime/v3/extensions/commands"; + public static final String PATH = "/internal/managed-runtime/v3/extensions/commands"; private ManagedExtensionCommandProtocol() { } @@ -43,7 +43,7 @@ public final class ManagedExtensionCommandProtocol { } } - static Map response(byte[] bytes, RuntimeSession session, Map request) { + public static Map response(byte[] bytes, RuntimeSession session, Map request) { var envelope = ManagedContextProtocol.parse(bytes); if (!envelope.keySet().equals(Set.of("protocolVersion", "runtimeSessionId", "result")) || !Integer.valueOf(1).equals(envelope.get("protocolVersion")) diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocol.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocol.java index 0b74feb416d..a505eae129b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocol.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocol.java @@ -1,11 +1,11 @@ package com.alibaba.qwen.code.runtimebroker.transport; import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.ManagedExtensionExecutionProfile; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; -import com.alibaba.qwen.code.runtimebroker.ManagedExtensionExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import java.util.HashSet; import java.util.List; @@ -14,9 +14,9 @@ import java.util.Set; /** Narrow Hosted controls; executable settings are added only by the Java preparation gateway. */ public final class ManagedExtensionProtocol { - static final String PATH = "/internal/managed-runtime/v3/extensions"; - static final int REQUEST_LIMIT = 512 * 1024; - static final int RESPONSE_LIMIT = 8 * 1024 * 1024; + public static final String PATH = "/internal/managed-runtime/v3/extensions"; + public static final int REQUEST_LIMIT = 512 * 1024; + public static final int RESPONSE_LIMIT = 8 * 1024 * 1024; private static final Set KINDS = Set.of("extension-prepare", "extension-activate", "extension-status", "extension-reconcile", "extension-asset-read", "extension-asset-glob"); @@ -117,7 +117,7 @@ public final class ManagedExtensionProtocol { } } - static Map response(byte[] bytes, RuntimeScope scope, String harnessSessionId, Map request) { + public static Map response(byte[] bytes, RuntimeScope scope, String harnessSessionId, Map request) { Map envelope = ManagedContextProtocol.parse(bytes); Map sessionKey = Map.of("tenantId", scope.getTenantId(), "workspaceId", scope.getWorkspaceId(), "sessionId", harnessSessionId); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHoldControlProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHoldControlProtocol.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHoldControlProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHoldControlProtocol.java index 5e1d1cb70fe..cc09c032d0b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHoldControlProtocol.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHoldControlProtocol.java @@ -4,7 +4,7 @@ import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import java.util.Map; import java.util.Set; @@ -17,7 +17,7 @@ import java.util.Set; * those stay per-family parameters, and the kinds derive from {@link * ManagedHoldFamily} instead of being re-typed here (#13930 R1-21). */ -final class ManagedHoldControlProtocol { +public final class ManagedHoldControlProtocol { private static final Set VIEW_FIELDS = Set.of("operationId", "state", "unitName", "evidence"); private static final Set STATES = Set.of("running", "exited", @@ -29,23 +29,23 @@ final class ManagedHoldControlProtocol { private final String family; private final String responseErrorCode; - ManagedHoldControlProtocol(Set kinds, String family, + public ManagedHoldControlProtocol(Set kinds, String family, String responseErrorCode) { this.kinds = kinds; this.family = family; this.responseErrorCode = responseErrorCode; } - boolean isOperation(Map operation) { + public boolean isOperation(Map operation) { return operation.get("kind") instanceof String kind && kinds.contains(kind); } - boolean isRecovery(Map operation) { + public boolean isRecovery(Map operation) { return isOperation(operation); } - void validateSession(RuntimeSession session, + public void validateSession(RuntimeSession session, Map operation) { if (!BrokerValues.isWellFormedJson(operation)) { throw invalid(family + " operation is invalid."); @@ -65,7 +65,7 @@ final class ManagedHoldControlProtocol { } } - Map response(byte[] bytes, RuntimeSession session, + public Map response(byte[] bytes, RuntimeSession session, Map request) { Map envelope = ManagedContextProtocol.parse(bytes); Object expectedId = request.get("targetOperationId"); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHoldFamily.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHoldFamily.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHoldFamily.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHoldFamily.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHookProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHookProtocol.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHookProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHookProtocol.java index f19782959f1..e6b2f0ccd21 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHookProtocol.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedHookProtocol.java @@ -4,14 +4,14 @@ import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import java.util.Map; import java.util.Set; /** Session ownership and bounded wire envelope for private Hook controls. */ public final class ManagedHookProtocol { - static final String PATH = "/internal/managed-runtime/v3/hooks"; + public static final String PATH = "/internal/managed-runtime/v3/hooks"; private static final Set KINDS = Set.of("hook-catalog", "hook-execute", "hook-status", "hook-cancel"); private static final Set RECOVERY_KINDS = Set.of("hook-status", "hook-cancel"); private static final Set VIEW_FIELDS = Set.of("operationId", "state", "catalog", "result", "error"); @@ -41,7 +41,7 @@ public final class ManagedHookProtocol { } } - static Map response(byte[] bytes, RuntimeSession session, Map request) { + public static Map response(byte[] bytes, RuntimeSession session, Map request) { Map envelope = ManagedContextProtocol.parse(bytes); Object expectedId = Set.of("hook-status", "hook-cancel").contains(request.get("kind")) ? request.get("targetOperationId") : request.get("operationId"); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMcpProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMcpProtocol.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMcpProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMcpProtocol.java index 731169e726d..4abfa6bfd25 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMcpProtocol.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMcpProtocol.java @@ -4,7 +4,7 @@ import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import java.util.ArrayList; import java.util.HashSet; @@ -14,8 +14,8 @@ import java.util.Set; /** Session ownership and bounded wire envelope for private MCP controls. */ public final class ManagedMcpProtocol { - static final String PATH = "/internal/managed-runtime/v3/mcp"; - static final String DEFINITIONS_PATH = PATH + "/definitions"; + public static final String PATH = "/internal/managed-runtime/v3/mcp"; + public static final String DEFINITIONS_PATH = PATH + "/definitions"; private static final Set KINDS = Set.of("mcp-configure", "mcp-discover", "mcp-invoke", "mcp-status", "mcp-cancel", "mcp-release"); private static final Set RECOVERY_KINDS = Set.of("mcp-status", "mcp-cancel", "mcp-release"); @@ -25,7 +25,7 @@ public final class ManagedMcpProtocol { private ManagedMcpProtocol() { } - static List> definitions(byte[] bytes) { + public static List> definitions(byte[] bytes) { Map envelope = ManagedContextProtocol.parse(bytes); if (!envelope.keySet().equals(Set.of("protocolVersion", "servers")) || !Integer.valueOf(1).equals(envelope.get("protocolVersion")) @@ -79,7 +79,7 @@ public final class ManagedMcpProtocol { } } - static Map response(byte[] bytes, RuntimeSession session, Map request) { + public static Map response(byte[] bytes, RuntimeSession session, Map request) { Map envelope = ManagedContextProtocol.parse(bytes); Object expectedId = Set.of("mcp-status", "mcp-cancel").contains(request.get("kind")) ? request.get("targetOperationId") : request.get("operationId"); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocol.java similarity index 90% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocol.java index 94ea1096118..0c36a6bee7e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocol.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocol.java @@ -15,7 +15,7 @@ import java.util.Set; * (#13930 R1-21). */ public final class ManagedMonitorProtocol { - static final String PATH = "/internal/managed-runtime/v3/monitors"; + public static final String PATH = "/internal/managed-runtime/v3/monitors"; private static final ManagedHoldControlProtocol PROTOCOL = new ManagedHoldControlProtocol( Set.of(ManagedHoldFamily.MONITOR.statusKind(), ManagedHoldFamily.MONITOR.stopKind()), @@ -37,7 +37,7 @@ public final class ManagedMonitorProtocol { PROTOCOL.validateSession(session, operation); } - static Map response(byte[] bytes, RuntimeSession session, + public static Map response(byte[] bytes, RuntimeSession session, Map request) { return PROTOCOL.response(bytes, session, request); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocol.java b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocol.java similarity index 90% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocol.java rename to packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocol.java index 93a4e36de84..f941e151ca2 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocol.java +++ b/packages/managed-agent-server/contracts/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocol.java @@ -14,7 +14,7 @@ import java.util.Set; * family's kinds and response error code (#13930 R1-21). */ public final class ManagedShellProtocol { - static final String PATH = "/internal/managed-runtime/v3/shells"; + public static final String PATH = "/internal/managed-runtime/v3/shells"; private static final ManagedHoldControlProtocol PROTOCOL = new ManagedHoldControlProtocol( Set.of(ManagedHoldFamily.SHELL.statusKind(), ManagedHoldFamily.SHELL.stopKind()), @@ -36,7 +36,7 @@ public final class ManagedShellProtocol { PROTOCOL.validateSession(session, operation); } - static Map response(byte[] bytes, RuntimeSession session, + public static Map response(byte[] bytes, RuntimeSession session, Map request) { return PROTOCOL.response(bytes, session, request); } diff --git a/packages/managed-agent-server/src/main/resources/csi-native-file-declarations.json b/packages/managed-agent-server/contracts/src/main/resources/csi-native-file-declarations.json similarity index 100% rename from packages/managed-agent-server/src/main/resources/csi-native-file-declarations.json rename to packages/managed-agent-server/contracts/src/main/resources/csi-native-file-declarations.json diff --git a/packages/managed-agent-server/src/main/resources/openapi/managed-agent-public-api.openapi.json b/packages/managed-agent-server/contracts/src/main/resources/openapi/managed-agent-public-api.openapi.json similarity index 100% rename from packages/managed-agent-server/src/main/resources/openapi/managed-agent-public-api.openapi.json rename to packages/managed-agent-server/contracts/src/main/resources/openapi/managed-agent-public-api.openapi.json diff --git a/packages/managed-agent-server/control-plane/pom.xml b/packages/managed-agent-server/control-plane/pom.xml new file mode 100644 index 00000000000..37b470ca109 --- /dev/null +++ b/packages/managed-agent-server/control-plane/pom.xml @@ -0,0 +1,87 @@ + + 4.0.0 + + com.alibaba + qwen-managed-agent-parent + 0.1.0-alpha + ../pom.xml + + qwen-managed-agent-control-plane + Qwen Managed Agent Control-Plane + + ${project.basedir}/.. + + + + com.alibaba + qwen-managed-agent-contracts + + + org.springframework + spring-context + + + org.springframework + spring-tx + + + org.springframework + spring-webmvc + + + org.springframework + spring-web + + + org.springframework.boot + spring-boot-autoconfigure + + + jakarta.annotation + jakarta.annotation-api + + + jakarta.servlet + jakarta.servlet-api + + + org.slf4j + slf4j-api + + + com.fasterxml.jackson.core + jackson-databind + + + org.apache.commons + commons-lang3 + + + commons-codec + commons-codec + + + com.alibaba.fastjson2 + fastjson2 + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + com.github.spotbugs + spotbugs-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactService.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactService.java index e498068b062..b0c9b22a40c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactService.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.artifact; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ManagedArtifactPolicy; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.ArtifactAccess; import com.alibaba.qwen.code.managedagent.model.ApiModels.ArtifactResponse; @@ -8,10 +8,11 @@ import com.alibaba.qwen.code.managedagent.model.ApiModels.ToolResultResponse; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellPage; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.model.TenantContext; -import com.alibaba.qwen.code.managedagent.persistence.ManagedArtifactReader; -import com.alibaba.qwen.code.managedagent.persistence.ManagedToolResultStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedToolResultStore.Artifact; +import com.alibaba.qwen.code.managedagent.repository.ManagedArtifactReader; +import com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.Artifact; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.servlet.http.HttpServletResponse; @@ -19,7 +20,6 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpRange; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.io.IOException; @@ -42,11 +42,11 @@ public class ManagedArtifactService { private final ManagedToolResultStore results; private final ManagedArtifactReader reader; private final ManagedArtifactPolicy policy; - private final ManagedAgentProperties.Artifacts settings; + private final ManagedAgentSettings.Artifacts settings; private final Semaphore readers; public ManagedArtifactService(ManagedAgentService sessions, ManagedToolResultStore results, - ManagedArtifactReader reader, ManagedArtifactPolicy policy, ManagedAgentProperties properties) { + ManagedArtifactReader reader, ManagedArtifactPolicy policy, ManagedAgentSettings properties) { this.sessions = sessions; this.results = results; this.reader = reader; @@ -184,7 +184,7 @@ public class ManagedArtifactService { throw notFound(); } if (!policy.readOriginal(tenant.tenantId(), tenant.actorId(), source.workspaceId(), source.sessionId())) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, "artifact_content_forbidden", + throw new ManagedAgentException(403, "artifact_content_forbidden", "The current actor cannot read original artifact bytes."); } } @@ -208,7 +208,7 @@ public class ManagedArtifactService { } String etag = "\"" + descriptor.path("sha256").asText() + "\""; if (ifMatch != null && !matches(ifMatch, etag)) { - throw new ManagedAgentException(HttpStatus.PRECONDITION_FAILED, "artifact_revision_mismatch", + throw new ManagedAgentException(412, "artifact_revision_mismatch", "The artifact validator does not match."); } if (ifRange != null && !etag.equals(ifRange.trim())) { @@ -219,14 +219,14 @@ public class ManagedArtifactService { try { selection = select(range, size); } catch (ManagedAgentException error) { - if (error.getStatus() == HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE) { + if (error.getStatus() == 416) { response.setHeader("Content-Range", "bytes */" + size); } throw error; } if (!readers.tryAcquire()) { response.setHeader("Retry-After", "1"); - throw new ManagedAgentException(HttpStatus.TOO_MANY_REQUESTS, "artifact_read_limit", + throw new ManagedAgentException(429, "artifact_read_limit", "The artifact reader limit has been reached."); } acquired = true; @@ -346,7 +346,7 @@ public class ManagedArtifactService { long start = parsed.getRangeStart(size); long end = parsed.getRangeEnd(size); if (size == 0 || start >= size || end < start) { - throw new ManagedAgentException(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE, "range_not_satisfiable", + throw new ManagedAgentException(416, "range_not_satisfiable", "The range does not intersect this representation."); } long length = end - start + 1; @@ -366,14 +366,14 @@ public class ManagedArtifactService { } private static ManagedAgentException badRequest(String code, String message) { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, code, message); + return new ManagedAgentException(400, code, message); } private static ManagedAgentException notFound() { - return new ManagedAgentException(HttpStatus.NOT_FOUND, "artifact_not_found", "The resource was not found."); + return new ManagedAgentException(404, "artifact_not_found", "The resource was not found."); } private static ManagedAgentException unavailable() { - return new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, "artifact_unavailable", "Artifact content is unavailable."); + return new ManagedAgentException(503, "artifact_unavailable", "Artifact content is unavailable."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedToolDetailsService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedToolDetailsService.java similarity index 85% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedToolDetailsService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedToolDetailsService.java index 4d477a1566c..863ae58e374 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedToolDetailsService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedToolDetailsService.java @@ -4,9 +4,8 @@ import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellToolDetails; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.model.TenantContext; -import com.alibaba.qwen.code.managedagent.persistence.ManagedToolDetailsStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedToolDetailsStore; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @Service @@ -32,7 +31,7 @@ public class ManagedToolDetailsService { tenant.requireActorId(); SessionRecord session = sessions.requireReadableSession(tenant.tenantId(), tenant.actorId(), sessionId); if (session.workspace() == null || "DELETING".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "tool_details_not_found", "Tool details are unavailable."); + throw new ManagedAgentException(404, "tool_details_not_found", "Tool details are unavailable."); } return session; } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ToolPublicationRetentionObserver.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ToolPublicationRetentionObserver.java similarity index 80% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ToolPublicationRetentionObserver.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ToolPublicationRetentionObserver.java index 6710fac1dec..ea563290e83 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ToolPublicationRetentionObserver.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ToolPublicationRetentionObserver.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.artifact; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationRetentionStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationRetention; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; @@ -11,10 +11,10 @@ import java.util.Map; public final class ToolPublicationRetentionObserver { private static final Logger LOG = LoggerFactory.getLogger(ToolPublicationRetentionObserver.class); - private final ToolPublicationRetentionStore retention; - private final ManagedAgentProperties properties; + private final ToolPublicationRetention retention; + private final ManagedAgentSettings properties; - public ToolPublicationRetentionObserver(ToolPublicationRetentionStore retention, ManagedAgentProperties properties) { + public ToolPublicationRetentionObserver(ToolPublicationRetention retention, ManagedAgentSettings properties) { this.retention = retention; this.properties = properties; } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/automation/AutomationScanner.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/automation/AutomationScanner.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/automation/AutomationScanner.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/automation/AutomationScanner.java index 2bee4db87c4..ad5b52375d2 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/automation/AutomationScanner.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/automation/AutomationScanner.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.managedagent.automation; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.OccurrenceRow; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.OccurrenceView; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.ScheduleRow; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.OccurrenceRow; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.OccurrenceView; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.ScheduleRow; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -75,14 +75,14 @@ public class AutomationScanner { private final AutomationLedgerStore store; private final HarnessConnector harness; - private final ManagedAgentProperties.Automation settings; + private final ManagedAgentSettings.Automation settings; private final Supplier clock; private final String owner; private final Executor executor; @Autowired public AutomationScanner(AutomationLedgerStore store, - HarnessConnector harness, ManagedAgentProperties properties, + HarnessConnector harness, ManagedAgentSettings properties, @Qualifier("automationScannerExecutor") ThreadPoolTaskExecutor pool) { this(store, harness, properties.getAutomation(), @@ -91,13 +91,13 @@ public class AutomationScanner { } AutomationScanner(AutomationLedgerStore store, HarnessConnector harness, - ManagedAgentProperties.Automation settings, + ManagedAgentSettings.Automation settings, Supplier clock, String owner) { this(store, harness, settings, clock, owner, Runnable::run); } AutomationScanner(AutomationLedgerStore store, HarnessConnector harness, - ManagedAgentProperties.Automation settings, + ManagedAgentSettings.Automation settings, Supplier clock, String owner, Executor executor) { this.store = store; this.harness = harness; @@ -458,7 +458,7 @@ public class AutomationScanner { || "failed".equals(state) || "cancelled".equals(state)) { resolved.add(blocking.occurrence().occurrenceKey()); } - } catch (DaemonHttpException error) { + } catch (HarnessHttpFailure error) { String code = errorCode(error); if ("automation_retired".equals(code) || "automation_not_found".equals(code) @@ -549,7 +549,7 @@ public class AutomationScanner { try { answer = harness.runAutomationOperation(row.tenantId(), row.sessionId(), body); - } catch (DaemonHttpException error) { + } catch (HarnessHttpFailure error) { String code = errorCode(error); if ("automation_revision_stale".equals(code)) { // A definition changed after this scanner read it: reload @@ -674,7 +674,7 @@ public class AutomationScanner { occurrence.occurrenceKey(), attempts, error.getMessage()); } - static String errorCode(DaemonHttpException error) { + static String errorCode(HarnessHttpFailure error) { String body = error.getResponseBody(); if (body == null) { return null; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/automation/CronSlots.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/automation/CronSlots.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/automation/CronSlots.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/automation/CronSlots.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationService.java similarity index 89% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationService.java index 43026fd452a..936877e5772 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationService.java @@ -1,10 +1,12 @@ package com.alibaba.qwen.code.managedagent.automation; -import com.alibaba.qwen.code.daemon.DaemonException; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.session.HarnessConflictCodes; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; import com.alibaba.qwen.code.managedagent.model.ApiModels.AutomationDefinitionRequest; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicAutomation; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicAutomationGateStatus; @@ -12,26 +14,23 @@ import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicAutomationRun; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicList; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.CommandRow; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.OccurrenceCursor; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.OccurrencePage; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.OccurrenceRow; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.OccurrenceView; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.ScheduleCursor; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.SchedulePage; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.ScheduleRow; -import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords; -import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.session.HarnessConflictCodes; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.CommandRow; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.OccurrenceCursor; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.OccurrencePage; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.OccurrenceRow; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.OccurrenceView; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.ScheduleCursor; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.SchedulePage; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.ScheduleRow; +import com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.nio.charset.StandardCharsets; @@ -66,13 +65,13 @@ public class ManagedAutomationService { private static final int MAX_DEFINITIONS = 32; private final AutomationLedgerStore ledger; - private final ManagedAgentStore sessions; + private final AgentStateStore sessions; private final ManagedWorkspaceRegistry workspaces; private final HarnessConnector harness; private final AutomationScanner scanner; private final RequestDigests digests; private final ObjectMapper mapper; - private final ManagedAgentProperties.Automation settings; + private final ManagedAgentSettings.Automation settings; private final boolean sessionStoreEnabled; private final Supplier clock; /** The lease this instance holds a definition under for a manual run. */ @@ -80,10 +79,10 @@ public class ManagedAutomationService { @Autowired public ManagedAutomationService(AutomationLedgerStore ledger, - ManagedAgentStore sessions, ManagedWorkspaceRegistry workspaces, + AgentStateStore sessions, ManagedWorkspaceRegistry workspaces, HarnessConnector harness, AutomationScanner scanner, RequestDigests digests, @Qualifier("managedAgentObjectMapper") ObjectMapper mapper, - ManagedAgentProperties properties) { + ManagedAgentSettings properties) { this(ledger, sessions, workspaces, harness, scanner, digests, mapper, properties.getAutomation(), properties.getSessionStore().isEnabled(), @@ -91,10 +90,10 @@ public class ManagedAutomationService { } ManagedAutomationService(AutomationLedgerStore ledger, - ManagedAgentStore sessions, ManagedWorkspaceRegistry workspaces, + AgentStateStore sessions, ManagedWorkspaceRegistry workspaces, HarnessConnector harness, AutomationScanner scanner, RequestDigests digests, ObjectMapper mapper, - ManagedAgentProperties.Automation settings, + ManagedAgentSettings.Automation settings, boolean sessionStoreEnabled, Supplier clock) { this.ledger = ledger; this.sessions = sessions; @@ -118,7 +117,7 @@ public class ManagedAutomationService { requireActor(actorId); ManagedAgentService.validateIdempotencyKey(idempotencyKey); if (request.sessionId() == null || request.sessionId().isEmpty()) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_automation", "session_id is required."); } Map definition = definition(request, true); @@ -146,13 +145,13 @@ public class ManagedAutomationService { if (landed.isEmpty() && ledger.countLive(tenantId, session.sessionId()) >= MAX_DEFINITIONS) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "automation_count_limit", "A Session holds at most " + MAX_DEFINITIONS + " live automation definitions."); } if (landed.isPresent() && !landed.get().sessionId().equals(request.sessionId())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "automation_operation_conflict", "The Idempotency-Key already landed on another Session."); } @@ -203,7 +202,7 @@ public class ManagedAutomationService { requireLive(row); if (request.sessionId() != null && !request.sessionId().equals(row.sessionId())) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_automation", "session_id cannot change after creation."); } @@ -338,7 +337,7 @@ public class ManagedAutomationService { occurrenceKey, null, AutomationScanner.TRIGGER_MANUAL, AutomationLedgerStore.OUTCOME_SKIPPED, refusal, fence, now), owner); - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "automation_run_skipped", "The manual run was skipped: " + refusal + "."); } @@ -348,7 +347,7 @@ public class ManagedAutomationService { occurrenceKey, null, AutomationScanner.TRIGGER_MANUAL, AutomationLedgerStore.OUTCOME_FIRING, null, fence, now), owner)) - .orElseThrow(() -> new ManagedAgentException(HttpStatus.CONFLICT, + .orElseThrow(() -> new ManagedAgentException(409, "automation_busy", "The automation is being scanned; retry.")); if (AutomationLedgerStore.OUTCOME_FIRING.equals( @@ -379,7 +378,7 @@ public class ManagedAutomationService { String reason = ledger.findOccurrence(tenantId, automationId, occurrenceKey).orElse(occurrence) .reason(); - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "automation_run_skipped", "The manual run was skipped: " + reason + "."); } @@ -512,16 +511,16 @@ public class ManagedAutomationService { /** The answer to an operation whose outcome the Harness did not settle. */ private static RuntimeException unobtained(RuntimeException error) { - if (error instanceof DaemonHttpException refusal) { + if (error instanceof HarnessHttpFailure refusal) { return translate(refusal); } if (error instanceof UnsupportedOperationException) { - return new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, + return new ManagedAgentException(503, "automation_unavailable", "The Hosted Harness is not available."); } - if (error instanceof DaemonException) { - return new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, + if (error instanceof HarnessFailure) { + return new ManagedAgentException(503, "automation_operation_unknown", "The Hosted Harness answer was lost; retry the same key."); } @@ -547,7 +546,7 @@ public class ManagedAutomationService { break; } } - throw new ManagedAgentException(HttpStatus.CONFLICT, "automation_busy", + throw new ManagedAgentException(409, "automation_busy", "The automation is being scanned; retry."); } @@ -556,7 +555,7 @@ public class ManagedAutomationService { Map answer) { JsonNode schedule = mapper.valueToTree(answer.get("schedule")); if (schedule == null || !schedule.isObject()) { - throw new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, + throw new ManagedAgentException(503, "automation_operation_failed", "The Hosted Harness answered without the definition."); } @@ -565,7 +564,7 @@ public class ManagedAutomationService { schedule.required("scheduleId").asText()).filter(committed -> committed.sessionId().equals(session.sessionId()) && committed.recordRevision() >= recordRevision) - .orElseThrow(() -> new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, + .orElseThrow(() -> new ManagedAgentException(503, "automation_projection_unavailable", "The committed automation definition is unavailable.")); if (row.recordRevision() > recordRevision) { @@ -625,20 +624,20 @@ public class ManagedAutomationService { String gate = request.gate().textValue(); if (gate.getBytes(StandardCharsets.UTF_8).length > ManagedExtensionRecords.SCHEDULE_GATE_MAX_BYTES) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_automation", "gate exceeds " + ManagedExtensionRecords.SCHEDULE_GATE_MAX_BYTES + " bytes."); } if (gate.chars().anyMatch(character -> character <= 0x1f || (character >= 0x7f && character <= 0x9f))) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_automation", "gate must not contain control characters."); } definition.put("gate", gate); } else { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_automation", "gate must be a non-empty shell command or null."); } @@ -646,19 +645,19 @@ public class ManagedAutomationService { if (create) { for (String field : List.of("goal", "cron", "timezone", "prompt")) { if (!definition.containsKey(field)) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_automation", field + " is required."); } } } if (request.prompt() != null && request.prompt() .getBytes(StandardCharsets.UTF_8).length > 64 * 1024) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_automation", "prompt exceeds 64 KiB."); } if (request.timezone() != null && CronSlots.resolve(request.timezone()).isEmpty()) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "automation_timezone_unknown", "The timezone does not resolve on this deployment."); } @@ -669,19 +668,19 @@ public class ManagedAutomationService { // control plane can never mirror, list, fire or retire. if (request.cron().getBytes(StandardCharsets.UTF_8).length > 400) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_automation", "cron exceeds 400 bytes."); } try { CronSlots.compile(request.cron()); } catch (IllegalArgumentException error) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_automation", "The cron is invalid."); } } if (request.sessionMode() != null && !"persistent".equals(request.sessionMode())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "automation_mode_disabled", "Only the persistent target mode is enabled."); } @@ -697,7 +696,7 @@ public class ManagedAutomationService { private static void requireLive(ScheduleRow row) { if (!AutomationLedgerStore.STATE_LIVE.equals(row.state())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "automation_retired", + throw new ManagedAgentException(409, "automation_retired", "The automation is retired."); } } @@ -707,7 +706,7 @@ public class ManagedAutomationService { // scanner reads and the overlap count joins on live; without it a // fired run would never be seen to end. if (!settings.isEnabled() || !sessionStoreEnabled) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "automation_unavailable", "Automation is not enabled on this deployment."); } @@ -715,7 +714,7 @@ public class ManagedAutomationService { private static void requireActor(String actorId) { if (actorId == null || actorId.isEmpty()) { - throw new ManagedAgentException(HttpStatus.UNAUTHORIZED, "actor_required", + throw new ManagedAgentException(401, "actor_required", "A trusted actor is required."); } } @@ -723,14 +722,14 @@ public class ManagedAutomationService { private static void requireScheduleId(String automationId) { if (automationId == null || !SCHEDULE_ID.matcher(automationId) .matches()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, + throw new ManagedAgentException(404, "automation_not_found", "The automation was not found."); } } private static void requireLimit(int limit) { if (limit < 1 || limit > 100) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_limit", + throw new ManagedAgentException(400, "invalid_limit", "Limit must be between 1 and 100."); } } @@ -745,7 +744,7 @@ public class ManagedAutomationService { String actorId, String sessionId) { Optional found = sessions.findSession(tenantId, sessionId); - ManagedAgentException notFound = new ManagedAgentException(HttpStatus.NOT_FOUND, + ManagedAgentException notFound = new ManagedAgentException(404, "session_not_found", "The Session was not found."); if (found.isEmpty() || "DELETED".equals(found.get().status()) || found.get().workspace() == null @@ -757,7 +756,7 @@ public class ManagedAutomationService { // The owner family's refusal, as lifecycle and cwd answer a // non-creator (SurfaceRegistry rule class OWNER). if (!workspaces.createdSession(tenantId, actorId, sessionId)) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, + throw new ManagedAgentException(403, "session_operation_forbidden", "Only the Session's creator manages its automations."); } @@ -765,12 +764,12 @@ public class ManagedAutomationService { .findReadable(tenantId, actorId, session.workspace().getWorkspaceId()); if (summary == null || !summary.canCreateSession()) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, + throw new ManagedAgentException(403, "session_operation_forbidden", "The caller may not create in the Session's Workspace."); } if (!"ACTIVE".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "session_not_active", "The Session is not active."); } return session; @@ -779,11 +778,11 @@ public class ManagedAutomationService { private ScheduleRow requireReadable(String tenantId, String actorId, String automationId) { ScheduleRow row = ledger.findSchedule(tenantId, automationId) - .orElseThrow(() -> new ManagedAgentException(HttpStatus.NOT_FOUND, + .orElseThrow(() -> new ManagedAgentException(404, "automation_not_found", "The automation was not found.")); if (!workspaces.canRead(tenantId, actorId, row.workspaceId())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, + throw new ManagedAgentException(404, "automation_not_found", "The automation was not found."); } return row; @@ -798,7 +797,7 @@ public class ManagedAutomationService { } if (!command.get().requestDigest().equals(requestDigest) || !command.get().actorId().equals(actorId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "idempotency_conflict", + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key was reused with different content."); } if (command.get().resultJson().isEmpty()) { @@ -838,7 +837,7 @@ public class ManagedAutomationService { * and the command row meets the definition it already created. */ public static String scheduleIdFor(String tenantId, String idempotencyKey) { - return "asch_" + AutomationLedgerStore.sha256(tenantId + '\0' + return "asch_" + com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.sha256(tenantId + '\0' + idempotencyKey).substring(0, 32); } @@ -874,7 +873,7 @@ public class ManagedAutomationService { boolean retryable = "automation_operation_unknown".equals(code) || HarnessConflictCodes.isTransient(code); return new ManagedAgentException( - retryable ? HttpStatus.SERVICE_UNAVAILABLE : HttpStatus.CONFLICT, + retryable ? 503 : 409, code, retryable ? "The Hosted Harness answer is pending; retry" @@ -882,19 +881,19 @@ public class ManagedAutomationService { : "The Hosted Harness refused the automation operation."); } - static ManagedAgentException translate(DaemonHttpException error) { + static ManagedAgentException translate(HarnessHttpFailure error) { String code = AutomationScanner.errorCode(error); // A self-healing Harness conflict (a dead predecessor's writer // lease, a busy or briefly unavailable Workspace, a closing // Session) answers the retryable 503 every other unobtained // outcome takes: the same request under the same key succeeds // once it lapses, so a 409 would tell the caller to stop. - HttpStatus status = switch (error.getStatusCode()) { - case 404 -> HttpStatus.NOT_FOUND; + int status = switch (error.getStatusCode()) { + case 404 -> 404; case 409 -> HarnessConflictCodes.isTransient(code) - ? HttpStatus.SERVICE_UNAVAILABLE : HttpStatus.CONFLICT; - case 400 -> HttpStatus.BAD_REQUEST; - default -> HttpStatus.SERVICE_UNAVAILABLE; + ? 503 : 409; + case 400 -> 400; + default -> 503; }; return new ManagedAgentException(status, code == null ? "automation_operation_failed" : code, @@ -943,7 +942,7 @@ public class ManagedAutomationService { Long.parseLong(parts[0]); return parts; } catch (IllegalArgumentException error) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_cursor", + throw new ManagedAgentException(400, "invalid_cursor", message); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionService.java similarity index 88% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionService.java index a1f90fde3fe..247a8a206ad 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionService.java @@ -4,16 +4,15 @@ import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.AgentDefinition; import com.alibaba.qwen.code.managedagent.model.ApiModels.AgentDefinitionRequest; import com.alibaba.qwen.code.managedagent.model.RequestDigests; -import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore.Admission; -import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore.ConcurrentWriteException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore.DefinitionRevision; +import com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore.Admission; +import com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore.ConcurrentWriteException; +import com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore.DefinitionRevision; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.util.LinkedHashMap; @@ -83,13 +82,13 @@ public class ManagedAgentDefinitionService { DefinitionRevision found; if (revision == null) { found = store.latest(tenantId, agentId).orElseThrow( - ManagedAgentDefinitionStore::notFound); + com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore::notFound); } else { if (!REVISION.matcher(revision).matches()) { - throw ManagedAgentDefinitionStore.notFound(); + throw com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore.notFound(); } found = store.find(tenantId, agentId, Long.parseLong(revision)) - .orElseThrow(ManagedAgentDefinitionStore::notFound); + .orElseThrow(com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore::notFound); } return publicDefinition(found); } @@ -106,7 +105,7 @@ public class ManagedAgentDefinitionService { } catch (ConcurrentWriteException conflict) { admission = store.replayCommitted(tenantId, idempotencyKey, requestDigest).orElseThrow(() -> new ManagedAgentException( - HttpStatus.CONFLICT, conflict.code(), + 409, conflict.code(), conflict.getMessage())); } return new Result(publicDefinition(admission.revision()), @@ -166,7 +165,7 @@ public class ManagedAgentDefinitionService { private static void requireAgentId(String agentId) { if (agentId == null || !AGENT_ID.matcher(agentId).matches()) { - throw ManagedAgentDefinitionStore.notFound(); + throw com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore.notFound(); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogService.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogService.java index e4f3c48d6e5..b73acce8811 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogService.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.catalog; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogService.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogService.java index 7d922d7068a..30e168f1334 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogService.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.catalog; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementService.java similarity index 84% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementService.java index cf3ad01cb8a..1007af321d4 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementService.java @@ -1,18 +1,17 @@ package com.alibaba.qwen.code.managedagent.catalog; -import com.alibaba.qwen.code.daemon.DaemonException; -import com.alibaba.qwen.code.daemon.DaemonHttpException; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; import com.alibaba.qwen.code.managedagent.model.ApiModels.ManagedMcpManagementCommand; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.util.LinkedHashMap; @@ -64,7 +63,7 @@ public class ManagedMcpManagementService { || command.expectedRevision() >= 9_007_199_254_740_991L || Set.of("add", "edit").contains(command.action()) != (command.server() != null) || command.server() != null && !command.serverId().equals(command.server().serverId())) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", "Invalid MCP management command."); + throw new ManagedAgentException(400, "invalid_request", "Invalid MCP management command."); } Map request = new LinkedHashMap<>(); try { @@ -75,7 +74,7 @@ public class ManagedMcpManagementService { } SessionRecord session = store.requireMcpManagement(tenantId, actorId, sessionId, (String) request.get("operationId")); if (!harness.supportsMcpManagement() || !harness.supportsLifecycle(session.toolProfile())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "MCP management is unavailable."); + throw new ManagedAgentException(409, "unsupported_feature", "MCP management is unavailable."); } request.put("actorKey", DigestUtils.sha256Hex(actorId)); request.put("action", command.action()); @@ -91,11 +90,11 @@ public class ManagedMcpManagementService { public JsonNode operation(String tenantId, String actorId, String sessionId, String operationId) { sessions.requireReadableSession(tenantId, actorId, sessionId); if (operationId == null || !operationId.matches("(?i)[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}")) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", "Invalid MCP operation identity."); + throw new ManagedAgentException(400, "invalid_request", "Invalid MCP operation identity."); } SessionRecord session = store.requireMcpManagement(tenantId, actorId, sessionId, null); if (!harness.supportsMcpManagement() || !harness.supportsLifecycle(session.toolProfile())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "MCP management is unavailable."); + throw new ManagedAgentException(409, "unsupported_feature", "MCP management is unavailable."); } return invoke(() -> harness.mcpManagementOperation(tenantId, sessionId, operationId)); } @@ -112,17 +111,17 @@ public class ManagedMcpManagementService { JsonNode result = json.valueToTree(request.get()); requireSnapshot(result); return result; - } catch (DaemonHttpException refusal) { - HttpStatus status = HttpStatus.resolve(refusal.getStatusCode()); + } catch (HarnessHttpFailure refusal) { + org.springframework.http.HttpStatus status = org.springframework.http.HttpStatus.resolve(refusal.getStatusCode()); String code = refusal.getErrorCode(); if (status == null || !status.is4xxClientError() || code == null || !code.matches("[a-z][a-z0-9_]{0,127}")) { - throw new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, "managed_mcp_management_unavailable", + throw new ManagedAgentException(503, "managed_mcp_management_unavailable", "The original MCP operation cannot be confirmed."); } - throw new ManagedAgentException(status, code, "hosted_mcp_session_load_required".equals(code) + throw new ManagedAgentException(status.value(), code, "hosted_mcp_session_load_required".equals(code) ? "Continue or recover the Session before managing MCP servers." : "MCP management was refused."); - } catch (DaemonException unavailable) { - throw new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, "managed_mcp_management_unavailable", + } catch (HarnessFailure unavailable) { + throw new ManagedAgentException(503, "managed_mcp_management_unavailable", "The original MCP operation cannot be confirmed."); } } @@ -132,7 +131,7 @@ public class ManagedMcpManagementService { || !body.path("sessionId").isTextual() || !body.path("supported").isBoolean() || !counter(body.path("revision"), 0) || !body.path("servers").isArray() || body.path("servers").size() > 32 || !body.path("availableServers").isArray()) { - throw new DaemonException("Invalid MCP management snapshot"); + throw new HarnessFailure("Invalid MCP management snapshot"); } for (String field : List.of("servers", "availableServers")) { boolean published = "availableServers".equals(field); @@ -143,7 +142,7 @@ public class ManagedMcpManagementService { || !server.path("definitionDigest").asText().matches("[a-f0-9]{64}") || !(published ? Set.of("stdio", "streamable-http", "sse").contains(server.path("transport").asText()) : Set.of("enabled", "disabled", "removed").contains(server.path("state").asText()))) { - throw new DaemonException("Invalid MCP management server"); + throw new HarnessFailure("Invalid MCP management server"); } } } @@ -158,7 +157,7 @@ public class ManagedMcpManagementService { || operation.has("error") && (!operation.path("error").isObject() || operation.path("error").size() != 1 || !operation.path("error").path("code").isTextual() || !operation.path("error").path("code").asText().matches("[a-z][a-z0-9_]{0,127}"))) { - throw new DaemonException("Invalid MCP management operation"); + throw new HarnessFailure("Invalid MCP management operation"); } } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedSkillCatalogService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedSkillCatalogService.java similarity index 89% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedSkillCatalogService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedSkillCatalogService.java index a6f83035b02..a6753553009 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedSkillCatalogService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/catalog/ManagedSkillCatalogService.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent.catalog; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellSessionSkills; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellSkill; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionSkills; +import com.alibaba.qwen.code.managedagent.repository.ManagedSessionSkills; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; import org.springframework.stereotype.Service; import java.util.List; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelService.java similarity index 88% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelService.java index b6e2f7c3a57..db084ba4301 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelService.java @@ -1,44 +1,42 @@ package com.alibaba.qwen.code.managedagent.channel; -import com.alibaba.qwen.code.daemon.DaemonException; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ChannelInstanceView; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ClaimResponse; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ClaimedDelivery; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ClaimedSegment; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.DeliveryView; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.InboundAdmission; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.InboundEventRequest; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ReceiptRequest; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.RegisterChannelRequest; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ResendResponse; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ChannelInstanceView; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ClaimResponse; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ClaimedDelivery; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ClaimedSegment; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.DeliveryView; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.InboundAdmission; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.InboundEventRequest; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ReceiptRequest; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.RegisterChannelRequest; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ResendResponse; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository.ChannelDelivery; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository.DeliveryCursor; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository.DeliveryPage; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelRouteRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelRouteRepository.ChannelRoute; +import com.alibaba.qwen.code.managedagent.contracts.session.HarnessConflictCodes; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; import com.alibaba.qwen.code.managedagent.model.ApiModels.CommandAdmission; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicChannel; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicChannelDelivery; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicChannelRoute; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicList; -import com.alibaba.qwen.code.managedagent.persistence.ChannelDeliveryRepository; -import com.alibaba.qwen.code.managedagent.persistence.ChannelDeliveryRepository.ChannelDelivery; -import com.alibaba.qwen.code.managedagent.persistence.ChannelDeliveryRepository.DeliveryCursor; -import com.alibaba.qwen.code.managedagent.persistence.ChannelDeliveryRepository.DeliveryPage; -import com.alibaba.qwen.code.managedagent.persistence.ChannelInstanceStore; -import com.alibaba.qwen.code.managedagent.persistence.ChannelInstanceStore.ChannelBinding; -import com.alibaba.qwen.code.managedagent.persistence.ChannelInstanceStore.ChannelClaim; -import com.alibaba.qwen.code.managedagent.persistence.ChannelInstanceStore.ChannelInstance; -import com.alibaba.qwen.code.managedagent.persistence.ChannelInstanceStore.InstanceCursor; -import com.alibaba.qwen.code.managedagent.persistence.ChannelInstanceStore.InstancePage; -import com.alibaba.qwen.code.managedagent.persistence.ChannelInstanceStore.PendingDelivery; -import com.alibaba.qwen.code.managedagent.persistence.ChannelRouteRepository; -import com.alibaba.qwen.code.managedagent.persistence.ChannelRouteRepository.ChannelRoute; -import com.alibaba.qwen.code.managedagent.persistence.JdbcChannelDeliveryRepository; -import com.alibaba.qwen.code.managedagent.persistence.JdbcChannelRouteRepository; -import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.session.HarnessConflictCodes; +import com.alibaba.qwen.code.managedagent.repository.ChannelInstanceStore; +import com.alibaba.qwen.code.managedagent.repository.ChannelInstanceStore.ChannelBinding; +import com.alibaba.qwen.code.managedagent.repository.ChannelInstanceStore.ChannelClaim; +import com.alibaba.qwen.code.managedagent.repository.ChannelInstanceStore.ChannelInstance; +import com.alibaba.qwen.code.managedagent.repository.ChannelInstanceStore.InstanceCursor; +import com.alibaba.qwen.code.managedagent.repository.ChannelInstanceStore.InstancePage; +import com.alibaba.qwen.code.managedagent.repository.ChannelInstanceStore.PendingDelivery; +import com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; @@ -46,8 +44,6 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; -import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -97,11 +93,10 @@ public class ManagedChannelService { @org.springframework.beans.factory.annotation.Autowired public ManagedChannelService(ChannelInstanceStore instances, - @Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, ManagedAgentService sessions, + ChannelRouteRepository routes, ChannelDeliveryRepository deliveries, ManagedAgentService sessions, ManagedWorkspaceRegistry workspaces, HarnessConnector harness, - @Qualifier("managedAgentObjectMapper") ObjectMapper mapper, ManagedAgentProperties properties) { - this(instances, new JdbcChannelRouteRepository(jdbc), - new JdbcChannelDeliveryRepository(jdbc), sessions, workspaces, + @Qualifier("managedAgentObjectMapper") ObjectMapper mapper, ManagedAgentSettings properties) { + this(instances, routes, deliveries, sessions, workspaces, harness, mapper, properties.getChannels().getClaimLease(), System::currentTimeMillis); } @@ -131,7 +126,7 @@ public class ManagedChannelService { try { policyJson = mapper.writeValueAsString(request.policy()); } catch (com.fasterxml.jackson.core.JsonProcessingException error) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", + throw new ManagedAgentException(400, "invalid_request", "The channel policy is not serializable."); } String cwd = request.cwdRelative() == null @@ -141,7 +136,7 @@ public class ManagedChannelService { try { new WorkspaceSelection(request.workspaceId(), cwd); } catch (IllegalArgumentException error) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", + throw new ManagedAgentException(400, "invalid_request", "The Workspace selection is invalid."); } ChannelInstance stored; @@ -152,7 +147,7 @@ public class ManagedChannelService { request.actorId(), request.workspaceId(), cwd, policyJson, 0, 0)); } catch (IllegalStateException error) { - throw new ManagedAgentException(HttpStatus.CONFLICT, error.getMessage(), + throw new ManagedAgentException(409, error.getMessage(), "The channel registration conflicts with the stored" + " connection."); } @@ -170,12 +165,12 @@ public class ManagedChannelService { InboundEventRequest event) { ChannelInstance instance = requireInstance(tenantId, channelId); if (!"connected".equals(instance.state())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "channel_disconnected", "The channel is not connected."); } if (event.accountGeneration() != instance.accountGeneration()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, event.accountGeneration() < instance.accountGeneration() ? "channel_generation_stale" : "channel_generation_unregistered", @@ -223,11 +218,11 @@ public class ManagedChannelService { result = harness.runChannelOperation(tenantId, binding.sessionId(), submitBody(instance, event, inputId, kind, senderId, chatId, threadId)); - } catch (DaemonException error) { + } catch (HarnessFailure error) { throw translate(error); } if (routes.admit(tenantId, routeKey, inputId) == null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "channel_route_conflict", "The ingress row was admitted with another input."); } @@ -238,7 +233,7 @@ public class ManagedChannelService { int limit) { ChannelInstance instance = requireInstance(tenantId, channelId); if (!"connected".equals(instance.state())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "channel_disconnected", "The channel is not connected."); } @@ -277,7 +272,7 @@ public class ManagedChannelService { if (request.ordinal() == null || request.providerMessageId() == null || request.providerMessageId().isBlank()) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_request", "An accepted receipt names its segment ordinal and" + " provider message id."); @@ -296,7 +291,7 @@ public class ManagedChannelService { try { result = harness.runChannelOperation(tenantId, claim.sessionId(), body); - } catch (DaemonException error) { + } catch (HarnessFailure error) { throw translate(error); } String state = deliveryState(result); @@ -317,7 +312,7 @@ public class ManagedChannelService { try { result = harness.runChannelOperation(tenantId, claim.sessionId(), body); - } catch (DaemonException error) { + } catch (HarnessFailure error) { throw translate(error); } Map delivery = (Map) result.get("delivery"); @@ -405,7 +400,7 @@ public class ManagedChannelService { return; } Map body = new LinkedHashMap<>(); - body.put("operationId", ChannelInstanceStore.reconciliationOperationId(claim)); + body.put("operationId", com.alibaba.qwen.code.managedagent.repository.ChannelInstanceStore.reconciliationOperationId(claim)); body.put("kind", "settle_delivery"); body.put("deliveryId", deliveryId); body.put("outcome", "unknown"); @@ -496,7 +491,7 @@ public class ManagedChannelService { requireReadableInstance(tenantId, actorId, channelId); return deliveries.find(tenantId, channelId, deliveryId) .map(ManagedChannelService::publicDelivery) - .orElseThrow(() -> new ManagedAgentException(HttpStatus.NOT_FOUND, + .orElseThrow(() -> new ManagedAgentException(404, "delivery_not_found", "The delivery was not found.")); } @@ -596,7 +591,7 @@ public class ManagedChannelService { String deliveryId, String state, String providerReceipt, Long claimGeneration) { ChannelDelivery current = deliveries.find(tenantId, channelId, deliveryId).orElseThrow(() -> new ManagedAgentException( - HttpStatus.NOT_FOUND, "delivery_not_found", + 404, "delivery_not_found", "The delivery was not found.")); if (state.equals(current.state())) { return current; @@ -694,7 +689,7 @@ public class ManagedChannelService { try { bytes = Base64.getDecoder().decode(attachment.bytesBase64()); } catch (IllegalArgumentException error) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_request", "An attachment is not base64."); } @@ -707,7 +702,7 @@ public class ManagedChannelService { String channelId) { requireChannelId(channelId); return instances.findInstance(tenantId, channelId).orElseThrow(() -> - new ManagedAgentException(HttpStatus.NOT_FOUND, "channel_not_found", + new ManagedAgentException(404, "channel_not_found", "The channel was not found.")); } @@ -715,7 +710,7 @@ public class ManagedChannelService { String actorId, String channelId) { ChannelInstance instance = requireInstance(tenantId, channelId); if (!workspaces.canRead(tenantId, actorId, instance.workspaceId())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "channel_not_found", + throw new ManagedAgentException(404, "channel_not_found", "The channel was not found."); } return instance; @@ -724,37 +719,37 @@ public class ManagedChannelService { private ChannelClaim requireClaim(String tenantId, String channelId, String deliveryId) { return instances.findClaim(tenantId, channelId, deliveryId) - .orElseThrow(() -> new ManagedAgentException(HttpStatus.NOT_FOUND, + .orElseThrow(() -> new ManagedAgentException(404, "delivery_not_found", "The delivery was not found.")); } private static void requireChannelId(String channelId) { if (channelId == null || !channelId.matches("[A-Za-z0-9._:@+-]{1,128}")) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", + throw new ManagedAgentException(400, "invalid_request", "The channel id is invalid."); } } private static void requireLimit(int limit) { if (limit < 1 || limit > 100) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_limit", + throw new ManagedAgentException(400, "invalid_limit", "Limit must be between 1 and 100."); } } private static void forbidSeparator(String value, String name) { if (value.contains(SEPARATOR)) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", + throw new ManagedAgentException(400, "invalid_request", name + " must not contain NUL."); } } - private static ManagedAgentException translate(DaemonException error) { - if (!(error instanceof DaemonHttpException http)) { + private static ManagedAgentException translate(HarnessFailure error) { + if (!(error instanceof HarnessHttpFailure http)) { // A generation clash or dropped connection is transient for the // adapter: the same retryable envelope an HTTP refusal takes. - return new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, + return new ManagedAgentException(503, "channel_operation_failed", "The Hosted Harness failed the channel operation."); } @@ -776,11 +771,11 @@ public class ManagedChannelService { // lease, a busy or briefly unavailable Workspace, a closing // Session) are the opposite: the adapter drops every 409, so they // cross as the retryable 503. - HttpStatus status = http.getStatusCode() == 409 + int status = http.getStatusCode() == 409 ? (HarnessConflictCodes.isTransient(code) - ? HttpStatus.SERVICE_UNAVAILABLE : HttpStatus.CONFLICT) - : http.getStatusCode() == 400 ? HttpStatus.BAD_REQUEST - : HttpStatus.SERVICE_UNAVAILABLE; + ? 503 : 409) + : http.getStatusCode() == 400 ? 400 + : 503; return new ManagedAgentException(status, code, "The Hosted Harness refused the channel operation."); } @@ -842,7 +837,7 @@ public class ManagedChannelService { return new String[] {decoded.substring(0, separator), decoded.substring(separator + 1)}; } catch (IllegalArgumentException error) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_cursor", + throw new ManagedAgentException(400, "invalid_cursor", message); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisher.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisher.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisher.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisher.java index 2d61014b762..d9d3f9642e5 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisher.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisher.java @@ -1,5 +1,7 @@ package com.alibaba.qwen.code.managedagent.event; +import com.alibaba.qwen.code.managedagent.contracts.event.CommittedEventPublisher; +import com.alibaba.qwen.code.managedagent.contracts.event.EventTransport; import com.alibaba.qwen.code.managedagent.model.StoreModels.EventRecord; import com.alibaba.qwen.code.managedagent.session.SessionEventHub; import jdk.jfr.Enabled; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumer.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumer.java similarity index 89% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumer.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumer.java index 4568b62439d..c54e0694b0e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumer.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumer.java @@ -1,14 +1,13 @@ package com.alibaba.qwen.code.managedagent.event; -import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelope; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelopes; +import com.alibaba.qwen.code.managedagent.repository.TransportConsumerStore; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.rocketmq.client.apis.consumer.ConsumeResult; -import org.apache.rocketmq.client.apis.message.MessageView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; -import java.nio.ByteBuffer; import java.util.OptionalLong; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -37,7 +36,7 @@ import java.util.concurrent.RejectedExecutionException; * never one per notice in parallel, and the listener thread never waits on * the store's row locks. */ -public class EventTransportConsumer implements Closeable { +public class EventTransportConsumer implements Closeable, com.alibaba.qwen.code.managedagent.contracts.event.EventMessageListener { private static final Logger LOG = LoggerFactory.getLogger( EventTransportConsumer.class); private static final ObjectMapper JSON = new ObjectMapper(); @@ -88,16 +87,16 @@ public class EventTransportConsumer implements Closeable { TransportExecutors.close(materializations); } - public ConsumeResult consume(MessageView message) { + public void consume(byte[] body, String messageId) { ManagedEventEnvelope envelope; try { envelope = ManagedEventEnvelopes.parse(JSON.readTree( - bytes(message.getBody()))); + body)); } catch (Exception notAnEnvelope) { LOG.warn("dropped a transport message that is not a contract" + " envelope messageId={} cause={}", - message.getMessageId(), notAnEnvelope.getClass().getName()); - return ConsumeResult.SUCCESS; + messageId, notAnEnvelope.getClass().getName()); + return; } try { dispatch(envelope); @@ -107,7 +106,7 @@ public class EventTransportConsumer implements Closeable { envelope.sessionId(), envelope.stream(), envelope.sequence(), failure.toString()); } - return ConsumeResult.SUCCESS; + return; } void dispatch(ManagedEventEnvelope envelope) { @@ -159,11 +158,4 @@ public class EventTransportConsumer implements Closeable { return MaterializeOutcome.DROPPED; } } - - private static byte[] bytes(ByteBuffer body) { - ByteBuffer view = body.duplicate(); - byte[] bytes = new byte[view.remaining()]; - view.get(bytes); - return bytes; - } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializer.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializer.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializer.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializer.java index 99d7c1e36ef..51804d61456 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializer.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializer.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.event; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.MaterializationTarget; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @@ -37,7 +37,7 @@ public class MessageMaterializer { // Driven by ManagedArtifactConfiguration.messageMaterializerTask on the // dedicated single-thread scheduler at - // ManagedAgentProperties.Events.materializeInterval (bound from + // ManagedAgentSettings.Events.materializeInterval (bound from // application.yml's materialize-interval, 100 ms by default). public void materialize() { // Saturation means a row sits behind the selection window, so diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/TransportExecutors.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/TransportExecutors.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/TransportExecutors.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/TransportExecutors.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivator.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivator.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivator.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivator.java index 9561016f46f..d756af27ce6 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivator.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivator.java @@ -1,8 +1,10 @@ package com.alibaba.qwen.code.managedagent.event; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore; -import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore.WakeFence; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelope; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelopes; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.repository.TransportConsumerStore; +import com.alibaba.qwen.code.managedagent.repository.TransportConsumerStore.WakeFence; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionManagementService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionManagementService.java similarity index 83% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionManagementService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionManagementService.java index d10f9379c6f..dfa2ac1d7d7 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionManagementService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionManagementService.java @@ -1,15 +1,14 @@ package com.alibaba.qwen.code.managedagent.extensions; -import com.alibaba.qwen.code.daemon.DaemonException; -import com.alibaba.qwen.code.daemon.DaemonHttpException; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedConfigInstall; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.persistence.ManagedConfigInstall; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords; -import com.alibaba.qwen.code.managedagent.persistence.ManagedMcpSettings; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -17,8 +16,6 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; -import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import java.nio.charset.StandardCharsets; @@ -35,18 +32,16 @@ public class ManagedExtensionManagementService { private final ManagedAgentService sessions; private final ManagedExtensionRecordStore records; private final ManagedExtensionPackageService packages; - private final JdbcTemplate jdbc; private final ObjectMapper json; private final HarnessConnector harness; public ManagedExtensionManagementService(AgentStateStore store, ManagedAgentService sessions, - ManagedExtensionRecordStore records, ManagedExtensionPackageService packages, @Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, + ManagedExtensionRecordStore records, ManagedExtensionPackageService packages, @Qualifier("managedAgentObjectMapper") ObjectMapper json, HarnessConnector harness) { this.store = store; this.sessions = sessions; this.records = records; this.packages = packages; - this.jdbc = jdbc; this.json = json; this.harness = harness; } @@ -58,7 +53,7 @@ public class ManagedExtensionManagementService { } boolean supported = packages.list(tenant, actor, session.workspace().getWorkspaceId()).supported() && harness.supportsSessionExtensions(); - JsonNode latest = ManagedMcpSettings.find(jdbc, tenant, sessionId); + JsonNode latest = records.currentMcpSettings(tenant, sessionId); if (!ManagedConfigInstall.isPackageConfig(latest)) { if (operationId != null) { throw missing(); @@ -121,7 +116,7 @@ public class ManagedExtensionManagementService { throw invalid(); } SessionRecord session = store.requireExtensionManagement(tenant, actor, sessionId, operationId, true); - JsonNode current = ManagedMcpSettings.find(jdbc, tenant, sessionId); + JsonNode current = records.currentMcpSettings(tenant, sessionId); if (!ManagedConfigInstall.isPackageConfig(current) || !operationId.equals(current.path("operationId").asText())) { throw missing(); } @@ -181,7 +176,7 @@ public class ManagedExtensionManagementService { private JsonNode read(String tenant, String sessionId, JsonNode ref) { var resource = records.readCommittedResource(tenant, sessionId, ref); - JsonNode value = ManagedExtensionRecordStore.parse(new String(resource.bytes(), StandardCharsets.UTF_8)); + JsonNode value = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(new String(resource.bytes(), StandardCharsets.UTF_8)); if (value == null) { throw corrupt(); } @@ -202,21 +197,21 @@ public class ManagedExtensionManagementService { throw corrupt(); } return result; - } catch (DaemonHttpException error) { - HttpStatus status = HttpStatus.resolve(error.getStatusCode()); + } catch (HarnessHttpFailure error) { + org.springframework.http.HttpStatus status = org.springframework.http.HttpStatus.resolve(error.getStatusCode()); if (status != null && status.is4xxClientError() && error.getErrorCode() != null && error.getErrorCode().matches("[a-z][a-z0-9_]{0,127}")) { - throw new ManagedAgentException(status, error.getErrorCode(), "Extension configuration was refused."); + throw new ManagedAgentException(status.value(), error.getErrorCode(), "Extension configuration was refused."); } throw unavailable(); - } catch (DaemonException error) { + } catch (HarnessFailure error) { throw unavailable(); } } - private static ManagedAgentException invalid() { return new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", "Invalid extension configuration."); } - private static ManagedAgentException missing() { return new ManagedAgentException(HttpStatus.NOT_FOUND, "extension_operation_not_found", "The extension operation was not found."); } - private static ManagedAgentException corrupt() { return new ManagedAgentException(HttpStatus.CONFLICT, "session_config_corrupt", "The saved configuration cannot be verified."); } - private static ManagedAgentException unsupported() { return new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "Extension configuration is unavailable."); } - private static ManagedAgentException unavailable() { return new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, "session_config_outcome_unknown", "Query or continue the original extension operation."); } + private static ManagedAgentException invalid() { return new ManagedAgentException(400, "invalid_request", "Invalid extension configuration."); } + private static ManagedAgentException missing() { return new ManagedAgentException(404, "extension_operation_not_found", "The extension operation was not found."); } + private static ManagedAgentException corrupt() { return new ManagedAgentException(409, "session_config_corrupt", "The saved configuration cannot be verified."); } + private static ManagedAgentException unsupported() { return new ManagedAgentException(409, "unsupported_feature", "Extension configuration is unavailable."); } + private static ManagedAgentException unavailable() { return new ManagedAgentException(503, "session_config_outcome_unknown", "Query or continue the original extension operation."); } } diff --git a/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageService.java new file mode 100644 index 00000000000..4bc27305341 --- /dev/null +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageService.java @@ -0,0 +1,222 @@ +package com.alibaba.qwen.code.managedagent.extensions; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ExtensionArchives; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ExtensionPackages; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Catalog; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Manifest; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Operation; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Package; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Pin; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Settings; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.SettingsCommand; +import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; +import com.alibaba.qwen.code.managedagent.repository.ExtensionPackageRepository; +import com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry; +import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceAccess; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Base64; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.UUID; + +@Service +public class ManagedExtensionPackageService implements ExtensionPackages { + private final ExtensionPackageRepository repository; + private final ManagedWorkspaceRegistry workspaces; + private final ManagedAgentSettings properties; + private final ExtensionArchives archives; + private final ObjectMapper json; + + public ManagedExtensionPackageService(ExtensionPackageRepository repository, + ManagedWorkspaceRegistry workspaces, ManagedAgentSettings properties, + ExtensionArchives archives, @Qualifier("managedAgentObjectMapper") ObjectMapper json) { + this.repository = repository; + this.workspaces = workspaces; + this.properties = properties; + this.archives = archives; + this.json = json; + } + public Catalog list(String tenant, String actor, String workspace) { + requireAccess(tenant, actor, workspace, false); + var packages = repository.list(tenant, workspace); + return new Catalog(properties.getExtensions().isEnabled() && archives.configured(), packages); + } + + public void requireExecutionAccess(String tenant, String actor, String workspace) { + requireAccess(tenant, actor, workspace, true); + } + + public void requireSelection(String tenant, String actor, String workspace, List pins) { + requireExecutionAccess(tenant, actor, workspace); + if (pins == null || pins.size() > 32 || pins.stream().anyMatch(java.util.Objects::isNull) + || pins.stream().map(Pin::extensionId).distinct().count() != pins.size()) { + throw invalid(); + } + for (Pin pin : pins) { + repository.get(tenant, workspace, pin); + if (pin.settingsRevision() != null && !repository.hasSettings(tenant, workspace, pin)) { + throw missing(); + } + } + } + + public Manifest decodeManifest(String encoded) { + try { + if (encoded == null || encoded.length() > ((ManagedExtensionPackages.MAX_MANIFEST_BYTES + 2) / 3) * 4) { + throw invalid(); + } + byte[] bytes = Base64.getUrlDecoder().decode(encoded); + if (bytes.length > ManagedExtensionPackages.MAX_MANIFEST_BYTES) { + throw invalid(); + } + Manifest manifest = json.readValue(bytes, Manifest.class); + if (manifest == null || manifest.schemaVersion() != 1 || manifest.compilerVersion() != 1 + || manifest.name() == null || !manifest.name().matches("[A-Za-z0-9][A-Za-z0-9._-]{0,127}") + || manifest.version() == null || manifest.version().isBlank() || manifest.version().length() > 128 + || manifest.target() == null || manifest.target().platform() == null || manifest.target().arch() == null + || !Set.of("linux", "darwin").contains(manifest.target().platform()) + || !Set.of("x64", "arm64", "any").contains(manifest.target().arch()) + || manifest.components() == null || manifest.components().size() > ManagedExtensionPackages.COMPONENTS.size() + || manifest.components().stream().anyMatch(java.util.Objects::isNull) + || !ManagedExtensionPackages.COMPONENTS.containsAll(manifest.components()) + || Set.copyOf(manifest.components()).size() != manifest.components().size() + || (manifest.sourceType() == null) != (manifest.sourceRevision() == null) + || manifest.sourceType() != null && (!Set.of("git", "github-release", "npm", "directory", "archive", + "archive-url", "link", "snapshot").contains(manifest.sourceType()) + || manifest.sourceRevision().isBlank() || manifest.sourceRevision().length() > 256 + || manifest.sourceRevision().chars().anyMatch(Character::isISOControl))) { + throw invalid(); + } + return manifest; + } catch (IOException | IllegalArgumentException error) { + throw invalid(); + } + } + + public Operation publish(String tenant, String actor, String workspace, String extensionId, + String digest, String idempotencyKey, Manifest manifest, InputStream archive) throws IOException { + ManagedAgentService.validateIdempotencyKey(idempotencyKey); + requireDigest(digest); + String operationId = operationId(tenant, actor, workspace, "publish", idempotencyKey); + String id = extensionId == null ? "ext_" + UUID.nameUUIDFromBytes(operationId.getBytes(StandardCharsets.UTF_8)) : extensionId; + requireId(id); + String encodedManifest = encode(manifest); + String requestDigest = DigestUtils.sha256Hex(encode(List.of(id, digest, manifest))); + Operation operation = repository.preparePublish(tenant, actor, workspace, extensionId, id, digest, + operationId, requestDigest, encodedManifest, write -> requireAccess(tenant, actor, workspace, write)); + if ("settled".equals(operation.state())) { + return operation; + } + long length = archives.put(tenant, workspace, digest, archive); + return repository.finishPublish(tenant, actor, workspace, id, digest, operationId, length, operation, + write -> requireAccess(tenant, actor, workspace, write)); + } + + public Operation operation(String tenant, String actor, String workspace, String operationId) { + return repository.operation(tenant, actor, workspace, operationId, + write -> requireAccess(tenant, actor, workspace, write)); + } + + public Package get(String tenant, String actor, String workspace, String extensionId, long revision, String digest) { + requireAccess(tenant, actor, workspace, false); + return repository.get(tenant, workspace, new Pin(extensionId, revision, digest, null)); + } + + public Path archive(String tenant, String actor, String workspace, String extensionId, long revision, + String digest) throws IOException { + Package saved = get(tenant, actor, workspace, extensionId, revision, digest); + return archives.verify(tenant, workspace, digest, saved.byteLength()); + } + + public Settings settings(String tenant, String actor, String workspace, String extensionId) { + return repository.settings(tenant, actor, workspace, extensionId, + write -> requireAccess(tenant, actor, workspace, write)); + } + + public Operation saveSettings(String tenant, String actor, String workspace, String extensionId, + String idempotencyKey, SettingsCommand command) { + ManagedAgentService.validateIdempotencyKey(idempotencyKey); + requireId(extensionId); + if (command == null || command.expectedRevision() < 0 || command.expectedRevision() >= 9_007_199_254_740_991L + || command.values() == null || command.values().size() > 128 + || command.values().entrySet().stream().anyMatch(entry -> entry.getKey() == null + || !entry.getKey().matches("[A-Za-z_][A-Za-z0-9_]{0,127}") || entry.getValue() == null)) { + throw invalid(); + } + byte[] plaintext = encode(new TreeMap<>(command.values())).getBytes(StandardCharsets.UTF_8); + if (plaintext.length > ManagedExtensionPackages.MAX_SETTINGS_BYTES) { + throw new ManagedAgentException(413, "extension_settings_too_large", + "Extension settings exceed 8 KiB."); + } + String operationId = operationId(tenant, actor, workspace, "settings", idempotencyKey); + String requestDigest = DigestUtils.sha256Hex(encode(List.of(extensionId, command.expectedRevision()))); + return repository.saveSettings(tenant, actor, workspace, extensionId, operationId, requestDigest, command, plaintext, + write -> requireAccess(tenant, actor, workspace, write)); + } + + public ResolvedPackage resolvePin(String tenant, String workspace, Pin pin) throws IOException { + Package saved = repository.get(tenant, workspace, pin); + Map settings = pin.settingsRevision() == null ? Map.of() + : repository.settingsValues(tenant, workspace, pin.extensionId(), pin.settingsRevision()); + return new ResolvedPackage(saved, archives.verify(tenant, workspace, pin.packageDigest(), saved.byteLength()), settings); + } + + private void requireAccess(String tenant, String actor, String workspace, boolean write) { + WorkspaceAccess access = workspaces.accessOf(tenant, actor, workspace); + if (!access.canRead()) { + throw missing(); + } + if (write && (!access.atLeast(WorkspaceAccess.OPERATOR) + || !properties.getExtensions().isEnabled() || !archives.configured() + || !(properties.getExtensions().getAllowedActors().contains(actor) + || properties.getExtensions().getAllowedActors().contains("*")))) { + throw new ManagedAgentException(403, "extension_management_forbidden", + "Extension management requires a Workspace operator and deployment permission."); + } + } + + private String encode(Object value) { + try { + return json.writeValueAsString(value); + } catch (IOException error) { + throw new IllegalStateException("Cannot encode extension record", error); + } + } + + private static String operationId(String tenant, String actor, String workspace, String kind, String key) { + return "eop_" + ManagedSchemaKeys.key(tenant, actor, workspace, kind, key); + } + + private static void requireId(String id) { + if (id == null || !id.matches("ext_[a-f0-9-]{36}")) { + throw invalid(); + } + } + + private static void requireDigest(String digest) { + if (digest == null || !digest.matches("[a-f0-9]{64}")) { + throw invalid(); + } + } + + private static ManagedAgentException invalid() { + return new ManagedAgentException(400, "invalid_request", "Invalid extension package request."); + } + + private static ManagedAgentException missing() { + return new ManagedAgentException(404, "extension_package_not_found", "Extension package was not found."); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/security/PublicSurface.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/security/PublicSurface.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/security/PublicSurface.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/security/PublicSurface.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinator.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinator.java similarity index 92% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinator.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinator.java index abb77e4864f..a7d072bb2ed 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinator.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinator.java @@ -1,13 +1,14 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.daemon.HostedHarnessCapabilityMismatchException; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessCapabilityFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; -import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore.Action; -import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore.Response; +import com.alibaba.qwen.code.managedagent.repository.ManagedActionStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.Action; +import com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.Response; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +34,7 @@ public class ActionResponseCoordinator { private final HarnessConnector harness; private final ExecutorService executor; private final Clock clock; - private final ManagedAgentProperties.Dispatch dispatch; + private final ManagedAgentSettings.Dispatch dispatch; private final String owner = UUID.randomUUID().toString(); private final Set active = ConcurrentHashMap.newKeySet(); private final ScheduledExecutorService renewals = @@ -54,7 +55,7 @@ public class ActionResponseCoordinator { HarnessConnector harness, @Qualifier("managedAgentExecutor") ExecutorService executor, @Qualifier("managedAgentClock") Clock clock, - ManagedAgentProperties properties) { + ManagedAgentSettings properties) { this.sessions = sessions; this.actions = actions; this.harness = harness; @@ -133,7 +134,7 @@ public class ActionResponseCoordinator { try { harness.resolveAction(tenant, session, response.actionId(), response.body()); harnessAnswered = true; - } catch (DaemonHttpException error) { + } catch (HarnessHttpFailure error) { if (settled(op, response)) { return; } @@ -148,7 +149,7 @@ public class ActionResponseCoordinator { return; } throw new DecisionNotYetProjected(); - } catch (HostedHarnessCapabilityMismatchException error) { + } catch (HarnessCapabilityFailure error) { // A Harness whose capability digest no longer matches will // still mismatch on every future negotiation, so returning // this command to the outbox would retry it forever. Complete @@ -308,12 +309,12 @@ public class ActionResponseCoordinator { } boolean matched = "decided".equals(action.state()) - && ManagedActionStore.decisionDigest(response.body()) + && com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.decisionDigest(response.body()) .equals(action.decisionDigest()); actions.complete( op, owner, - matched ? null : ManagedActionStore.endedCode(action.state()), + matched ? null : com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.endedCode(action.state()), matched ? action.decisionReceiptId() : null, true, clock.millis()); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinator.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinator.java similarity index 92% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinator.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinator.java index eee0c873694..0838660febb 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinator.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinator.java @@ -1,9 +1,10 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.fasterxml.jackson.databind.JsonNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +34,7 @@ public class ContextCompressionCoordinator { public ContextCompressionCoordinator(AgentStateStore store, ManagedExtensionRecordStore records, HarnessConnector harness, @Qualifier("managedAgentExecutor") ExecutorService executor, @Qualifier("managedAgentClock") Clock clock, - ManagedAgentProperties properties) { + ManagedAgentSettings properties) { this.store = store; this.records = records; this.harness = harness; @@ -84,7 +85,7 @@ public class ContextCompressionCoordinator { harness.readContextCompression(tenantId, sessionId, operationId); } } catch (RuntimeException error) { - if (evidence == null && error instanceof com.alibaba.qwen.code.daemon.DaemonHttpException refusal + if (evidence == null && error instanceof com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure refusal && refusal.getStatusCode() >= 400 && refusal.getStatusCode() < 500 && evidence(operation) == null) { store.settleContextCompression(operation, owner, "failed", null, diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionService.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionService.java index 446a04e6f53..c7c6c5065b8 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionService.java @@ -1,15 +1,15 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicContextCompressionOperation; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellContextCompressionOperation; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore; import com.fasterxml.jackson.databind.JsonNode; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.util.Locale; @@ -48,12 +48,12 @@ public class ContextCompressionService { private OperationAdmission admit(String tenantId, String actorId, String sessionId, String key, Long revision) { if (actorId == null || actorId.isEmpty()) { - throw new ManagedAgentException(HttpStatus.UNAUTHORIZED, "actor_required", + throw new ManagedAgentException(401, "actor_required", "A trusted actor is required."); } ManagedAgentService.validateIdempotencyKey(key); if (revision == null || revision < 0 || revision > 9_007_199_254_740_991L) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", + throw new ManagedAgentException(400, "invalid_request", "expected_compaction_revision must be a nonnegative safe integer."); } String digest = digests.digest(Map.of("sessionId", sessionId, diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinator.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinator.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinator.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinator.java index cf52d9bc44a..10dd043d05a 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinator.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinator.java @@ -1,34 +1,34 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.daemon.DaemonProtocolException; -import com.alibaba.qwen.code.daemon.DaemonTransportException; -import com.alibaba.qwen.code.daemon.HarnessRuntimeRecovery; -import com.alibaba.qwen.code.daemon.HarnessSessionRefusedException; -import com.alibaba.qwen.code.daemon.HostedHarnessCapabilityMismatchException; -import com.alibaba.qwen.code.daemon.HostedHarnessGenerationException; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.Admission; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.Attachment; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.SourceEvent; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.SourceStream; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessDisabledException; +import com.alibaba.qwen.code.managedagent.contracts.harness.HostedHarnessRecoveryDeclinedException; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.Admission; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.Attachment; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.SourceEvent; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.SourceStream; -import com.alibaba.qwen.code.managedagent.harness.HarnessDisabledException; -import com.alibaba.qwen.code.managedagent.harness.HostedHarnessRecoveryDeclinedException; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessCapabilityFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessGenerationFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessProtocolFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRuntimeRecovery; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessSessionRefusedFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessTransportFailure; import com.alibaba.qwen.code.managedagent.model.StoreModels.DispatchTarget; import com.alibaba.qwen.code.managedagent.model.StoreModels.HarnessEvent; import com.alibaba.qwen.code.managedagent.model.StoreModels.ProjectedEvent; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnRecord; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -132,7 +132,7 @@ public class HarnessCoordinator { public HarnessCoordinator(AgentStateStore store, HarnessConnector harness, HarnessEventProjector projector, RuntimeWarmer runtimeWarmer, @Qualifier("managedAgentExecutor") ExecutorService executor, - @Qualifier("managedAgentClock") Clock clock, ManagedAgentProperties properties) { + @Qualifier("managedAgentClock") Clock clock, ManagedAgentSettings properties) { this.store = store; this.harness = harness; this.projector = projector; @@ -321,7 +321,7 @@ public class HarnessCoordinator { try { terminal = runClaimed(claimed, leaseLost, submissionAttempted, recoveryPath); - } catch (HostedHarnessCapabilityMismatchException error) { + } catch (HarnessCapabilityFailure error) { terminal = failTerminally(claimed, error.getCode(), "Hosted Harness capability policy changed.", error); } catch (HostedHarnessRecoveryDeclinedException error) { @@ -358,7 +358,7 @@ public class HarnessCoordinator { + " Harness cannot take over (" + declineReason + ").", error); } - } catch (HostedHarnessGenerationException error) { + } catch (HarnessGenerationFailure error) { // G3: a generation change is adopted, not failed. The next // dispatch attempt re-attaches through the takeover load; on a // bound Session the wait is bounded by the prior generation's @@ -366,11 +366,11 @@ public class HarnessCoordinator { // churn cap transientFailure applies to generation waits. terminal = transientFailure(claimed, submissionAttempted.get(), error, recoveryPath.get()); - } catch (DaemonProtocolException error) { + } catch (HarnessProtocolFailure error) { terminal = failTerminally(claimed, "hosted_harness_protocol_error", "Hosted Harness returned an invalid protocol response.", error); - } catch (HarnessSessionRefusedException error) { + } catch (HarnessSessionRefusedFailure error) { // A named load refusal is fail-closed and known, but the Turn // still awaits a Harness that can open the Session (a mixed // fleet rolls forward), so it retries like any transient @@ -378,7 +378,7 @@ public class HarnessCoordinator { // out. Not a writer-lease wait, so the budget still applies. terminal = transientFailure(claimed, submissionAttempted.get(), error, false); - } catch (DaemonHttpException error) { + } catch (HarnessHttpFailure error) { // #14387: a takeover refusal for a workflow Turn with the // takeover_unrecovered verdict is terminal in effect — the // workflow's in-process executor died with the generation, and @@ -645,7 +645,7 @@ public class HarnessCoordinator { try { cancelOriginalTurn(session, claimed); requireLease(leaseLost); - } catch (DaemonHttpException error) { + } catch (HarnessHttpFailure error) { // The plain cancel route aborts only a live, in-memory // Turn; on a freshly attached Session whose parked Turn its // old generation owned (a requested approval died with that @@ -816,7 +816,7 @@ public class HarnessCoordinator { session.sessionId(), current.promptId(), current.input(), current.payloadDigest()); requireLease(leaseLost); - } catch (DaemonHttpException error) { + } catch (HarnessHttpFailure error) { // A coded 409 on this prompt proves the mark's lineage: // a prior generation's admission reply was lost, so no // epoch was ever recorded, yet the daemon durably holds @@ -923,7 +923,7 @@ public class HarnessCoordinator { boolean recoveryBlocked = false; try { recoveryBlocked = incoming.isEmpty() && stream.recoveryBlocked(lastEventId); - } catch (DaemonTransportException error) { + } catch (HarnessTransportFailure error) { LOG.warn("Managed Harness recovery probe is unavailable" + " tenant={} session={} turn={} failure={}", turn.tenantId(), turn.sessionId(), turn.turnId(), @@ -1207,7 +1207,7 @@ public class HarnessCoordinator { if (!renewalDriven || cancellations.add(cancelKey)) { try { cancelOriginalTurn(session, turn); - } catch (DaemonHttpException error) { + } catch (HarnessHttpFailure error) { if (error.getStatusCode() != 409 || !"hosted_turn_recovery_required" .equals(error.getErrorCode())) { @@ -1322,7 +1322,7 @@ public class HarnessCoordinator { turn.turnId(), owner, session.harnessBootId())) { try { cancelOriginalTurn(session, current); - } catch (DaemonHttpException parked) { + } catch (HarnessHttpFailure parked) { if (parked.getStatusCode() != 409 || !"hosted_turn_recovery_required".equals(parked.getErrorCode())) { throw parked; @@ -1567,7 +1567,7 @@ public class HarnessCoordinator { // this method derives from it, so no caller can route a generation // change here without the cap (review round 1, R1-5). boolean generationWait = - error instanceof HostedHarnessGenerationException; + error instanceof HarnessGenerationFailure; int budget = submissionAttempted ? maxPostAdmissionRetries : maxPreAdmissionRetries; boolean generationChurn = generationWait && exemptFromRetryBudget @@ -1583,12 +1583,12 @@ public class HarnessCoordinator { + " session={} turn={} failure={}", turn.tenantId(), turn.sessionId(), turn.turnId(), failureLabel(error), error); - if (error instanceof HarnessSessionRefusedException refusal) { + if (error instanceof HarnessSessionRefusedFailure refusal) { return fail(turn, refusal.getCode(), "Hosted Harness refused to open the Session before" + " Turn admission."); } - if (error instanceof DaemonHttpException http + if (error instanceof HarnessHttpFailure http && ("hosted_prompt_recovery_required" .equals(http.getErrorCode()) || ("hosted_turn_recovery_required" @@ -1627,7 +1627,7 @@ public class HarnessCoordinator { // named load refusal and a Runtime Broker failure each keep // their own code — the generic one would name a component that // did not fail. - if (error instanceof HarnessSessionRefusedException refusal) { + if (error instanceof HarnessSessionRefusedFailure refusal) { return fail(turn, refusal.getCode(), "Hosted Harness refused to open the Session after" + " Turn admission."); @@ -1693,12 +1693,12 @@ public class HarnessCoordinator { static boolean isPoolWait(RuntimeException error) { if (error instanceof ManagedAgentException managed) { - return managed.getStatus() == HttpStatus.SERVICE_UNAVAILABLE + return managed.getStatus() == 503 && "harness_pool_unavailable".equals(managed.getCode()) - || managed.getStatus() == HttpStatus.CONFLICT && managed.getCode() != null + || managed.getStatus() == 409 && managed.getCode() != null && POOL_OWNERSHIP_WAIT_CODES.contains(managed.getCode()); } - return error instanceof DaemonHttpException http && http.getStatusCode() == 409 + return error instanceof HarnessHttpFailure http && http.getStatusCode() == 409 && "hosted_harness_quiescing".equals(http.getErrorCode()); } @@ -1747,7 +1747,7 @@ public class HarnessCoordinator { } private static String failureLabel(RuntimeException error) { - return error instanceof HarnessSessionRefusedException refusal + return error instanceof HarnessSessionRefusedFailure refusal ? refusal.getCode() : error.getClass().getSimpleName(); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessEventProjector.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessEventProjector.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessEventProjector.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessEventProjector.java index 76bd2dd9101..81b3cae51c0 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessEventProjector.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/HarnessEventProjector.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.SourceEvent; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.SourceEvent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.EventIdentity; import com.alibaba.qwen.code.managedagent.model.StoreModels.ProjectedEvent; -import com.alibaba.qwen.code.managedagent.persistence.EventIdentity; import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinator.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinator.java similarity index 89% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinator.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinator.java index fc502c769b1..3014c1ed37e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinator.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinator.java @@ -1,13 +1,13 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.daemon.DaemonTransportException; -import com.alibaba.qwen.code.daemon.HostedHarnessGenerationException; -import com.alibaba.qwen.code.daemon.MutationOutcomeUnknownException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.persistence.HostedCleanupDebtStore; -import com.alibaba.qwen.code.managedagent.persistence.HostedCleanupDebtStore.CleanupDebt; -import com.alibaba.qwen.code.managedagent.persistence.HostedCleanupDebtStore.ScanPage; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessGenerationFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessMutationUnknownFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessTransportFailure; +import com.alibaba.qwen.code.managedagent.repository.HostedCleanupDebtStore; +import com.alibaba.qwen.code.managedagent.repository.HostedCleanupDebtStore.CleanupDebt; +import com.alibaba.qwen.code.managedagent.repository.HostedCleanupDebtStore.ScanPage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; @@ -160,12 +160,12 @@ public class HostedCleanupRetryCoordinator { * Turn is still running, so they only back off. */ private static boolean staleAttachment(RuntimeException error) { - if (error instanceof DaemonTransportException - || error instanceof HostedHarnessGenerationException - || error instanceof MutationOutcomeUnknownException) { + if (error instanceof HarnessTransportFailure + || error instanceof HarnessGenerationFailure + || error instanceof HarnessMutationUnknownFailure) { return true; } - if (error instanceof DaemonHttpException http) { + if (error instanceof HarnessHttpFailure http) { int status = http.getStatusCode(); return status == 404 || status >= 500; } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedActionService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedActionService.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedActionService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedActionService.java index 01ef528d7ff..0658c824c00 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedActionService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedActionService.java @@ -1,5 +1,7 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicActionList; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicCommandOperation; @@ -8,18 +10,15 @@ import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellPage; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; -import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore.Action; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.repository.ManagedActionStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.Action; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.nio.charset.StandardCharsets; @@ -109,7 +108,7 @@ public class ManagedActionService { String cursor, int limit) { var root = sessions.requireReadableSession(tenant, actor, session); if (root.publicChildAgentsVersion() != 1 || limit < 1 || limit > 100) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_feature", "Child Action aggregation is unavailable"); + throw new ManagedAgentException(400, "unsupported_feature", "Child Action aggregation is unavailable"); } Long before = null; String beforeSession = null; @@ -133,7 +132,7 @@ public class ManagedActionService { beforeSession = parts[1]; beforeId = parts[2]; } catch (IllegalArgumentException invalid) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_cursor", "Child Action cursor is invalid"); + throw new ManagedAgentException(400, "invalid_cursor", "Child Action cursor is invalid"); } } var rows = actions.listWithChildren(tenant, session, before, beforeSession, beforeId, limit + 1); @@ -169,7 +168,7 @@ public class ManagedActionService { .orElseThrow( () -> new ManagedAgentException( - HttpStatus.NOT_FOUND, + 404, "action_not_found", "The Action was not found.")), web); @@ -192,7 +191,7 @@ public class ManagedActionService { || !revision.isIntegralNumber() || !revision.canConvertToLong()) { throw new ManagedAgentException( - HttpStatus.BAD_REQUEST, + 400, "invalid_action_response", "Only permission responses are supported."); } @@ -267,7 +266,7 @@ public class ManagedActionService { sessions.requireReadableSession(tenant, actor, session); if (limit < 1 || limit > 100) { throw new ManagedAgentException( - HttpStatus.BAD_REQUEST, "invalid_limit", "Limit must be between 1 and 100."); + 400, "invalid_limit", "Limit must be between 1 and 100."); } Long before = null; String beforeId = null; @@ -286,7 +285,7 @@ public class ManagedActionService { beforeId = parts[1]; } catch (IllegalArgumentException error) { throw new ManagedAgentException( - HttpStatus.BAD_REQUEST, "invalid_cursor", "Action cursor is invalid."); + 400, "invalid_cursor", "Action cursor is invalid."); } } return actions.list(tenant, session, before, beforeId, limit + 1); @@ -374,7 +373,7 @@ public class ManagedActionService { return null; } text = wrapper.path("payloadJson").asText(); - JsonNode payload = ManagedExtensionRecordStore.parse(text); + JsonNode payload = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(text); if (payload == null || payload.size() != 2 || !tool.equals(payload.path("toolName").asText()) diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentService.java similarity index 83% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentService.java index 479b7ebd9c4..49dd92ac8f7 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentService.java @@ -1,12 +1,16 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.daemon.HostedPermissionRules; -import com.alibaba.qwen.code.daemon.SubmitHarnessTurn; -import com.alibaba.qwen.code.managedagent.catalog.AgentDefinitionExtensions; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.catalog.AgentDefinitionExtensions; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.session.ReplayCursorExpired; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRules; import com.alibaba.qwen.code.managedagent.model.ApiModels.CommandAdmission; import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicContentPart; @@ -50,17 +54,13 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionPage; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnPage; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnSummary; -import com.alibaba.qwen.code.managedagent.persistence.ManagedArtifactReader; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedArtifactReader; +import com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry; +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.managedagent.workspace.SessionWorktreeService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceAccess; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; @@ -69,14 +69,11 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.dao.DuplicateKeyException; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.nio.charset.StandardCharsets; import java.time.Clock; -import java.util.ArrayList; import java.util.Base64; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; @@ -89,7 +86,10 @@ import java.util.function.BooleanSupplier; import java.util.regex.Pattern; @Service -public class ManagedAgentService { +public class ManagedAgentService implements com.alibaba.qwen.code.managedagent.contracts.session.UserMessagePolicy { + private final ManagedUserMessagePolicy inputPolicy; + private final HarnessRules harnessRules; + private static final Logger LOG = LoggerFactory.getLogger(ManagedAgentService.class); private static final String CREATE = "CREATE_SESSION"; private static final String SUBMIT = "SUBMIT_TURN"; @@ -105,12 +105,10 @@ public class ManagedAgentService { // Catch-up reads of a stream use pages of this size. static final int STREAM_PAGE = 100; // "text" is the spelling that clients used before the contract. - private static final Set INPUT_TYPES = Set.of("input_text", - "text"); + // The per-block cap (1M chars) times the per-list cap (100) would // otherwise admit ~100M characters in a single command. - private static final int MAX_AGGREGATE_INPUT_CHARS = 4 * 1000 * 1000; - private static final int MAX_BLOCK_INPUT_CHARS = 1000 * 1000; + private final AgentStateStore store; private final ManagedWorkspaceRegistry workspaces; private final RequestDigests digests; @@ -119,12 +117,12 @@ public class ManagedAgentService { private RuntimeWarmer runtimeWarmer; private SessionWorktreeService sessionWorktrees; private org.springframework.beans.factory.ObjectProvider recoveryServices; - private ManagedAgentProperties.Dispatch renameDispatch = new ManagedAgentProperties.Dispatch(); + private java.util.function.Supplier renameLeaseDuration = () -> java.time.Duration.ofSeconds(60); private Clock renameClock = Clock.systemUTC(); @Autowired - void configureRenameDelivery(ManagedAgentProperties properties, @Qualifier("managedAgentClock") Clock clock) { - renameDispatch = properties.getDispatch(); + void configureRenameDelivery(ManagedAgentSettings properties, @Qualifier("managedAgentClock") Clock clock) { + renameLeaseDuration = properties.getDispatch()::getLeaseDuration; renameClock = clock; } @@ -166,7 +164,7 @@ public class ManagedAgentService { private BooleanSupplier artifactReadsEnabled = () -> false; @Autowired - void configureArtifacts(ManagedAgentProperties properties, + void configureArtifacts(ManagedAgentSettings properties, ManagedArtifactReader reader) { artifactReadsEnabled = () -> properties.getArtifacts().isEnabled() && reader.supported(); } @@ -179,12 +177,14 @@ public class ManagedAgentService { public ManagedAgentService(AgentStateStore store, RequestDigests digests, HarnessCoordinator coordinator, - HarnessConnector harness, ManagedWorkspaceRegistry workspaces) { + HarnessConnector harness, ManagedWorkspaceRegistry workspaces, HarnessRules harnessRules) { + this.harnessRules = harnessRules; this.store = store; this.workspaces = workspaces; this.digests = digests; this.coordinator = coordinator; this.harness = harness; + this.inputPolicy = new ManagedUserMessagePolicy(store, digests, harness, workspaces); } public CommandAdmission createSession(String tenantId, @@ -200,7 +200,7 @@ public class ManagedAgentService { String title, Map metadata, List blocks) { validateIdempotencyKey(idempotencyKey); - List> input = input(blocks, false); + List> input = inputPolicy.input(blocks, false); String effectiveTitle = metadataTitle(title, metadata); Map semantic = new LinkedHashMap<>(); semantic.put("agentId", agentId); @@ -223,7 +223,7 @@ public class ManagedAgentService { requireHarness(); } String payloadDigest = input.isEmpty() ? null - : SubmitHarnessTurn.computePayloadDigest(input); + : harnessRules.payloadDigest(input); Admission admission; try { admission = store.insertSessionCommand(tenantId, actorId, CREATE, @@ -286,7 +286,7 @@ public class ManagedAgentService { requireModelActor(actorId); var summary = getWebShellSession(tenantId, actorId, sessionId); if (summary.workspace() == null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "unsupported_feature", "Approval selection requires a Workspace Session"); } boolean supported = Boolean.TRUE.equals(summary.capabilities().approvalModeChange()); @@ -315,9 +315,9 @@ public class ManagedAgentService { requireModelActor(actorId); Map> rules; try { - rules = HostedPermissionRules.copyOf(requestedRules); + rules = harnessRules.permissionRules(requestedRules); } catch (IllegalArgumentException invalid) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_permission_rules", invalid.getMessage()); + throw new ManagedAgentException(400, "invalid_permission_rules", invalid.getMessage()); } validateIdempotencyKey(key); Map semantic = new LinkedHashMap<>(Map.of("sessionId", sessionId, @@ -337,7 +337,7 @@ public class ManagedAgentService { private static void requireModelActor(String actorId) { if (actorId == null || actorId.isEmpty()) { - throw new ManagedAgentException(HttpStatus.UNAUTHORIZED, "actor_required", "A trusted actor is required"); + throw new ManagedAgentException(401, "actor_required", "A trusted actor is required"); } } @@ -380,7 +380,7 @@ public class ManagedAgentService { String agentRevision, String title, Map metadata, List blocks, WorkspaceSelection selection, String approvalMode, Boolean planMode, List> skills, String modelId, - List extensionPins) { + List extensionPins) { return createWorkspaceSession(tenantId, actorId, idempotencyKey, agentId, agentRevision, title, metadata, blocks, selection, approvalMode, planMode, skills, modelId, extensionPins, null); } @@ -390,15 +390,15 @@ public class ManagedAgentService { String agentRevision, String title, Map metadata, List blocks, WorkspaceSelection selection, String approvalMode, Boolean planMode, List> skills, String modelId, - List extensionPins, + List extensionPins, SessionWorktreeService.Request worktree) { validateIdempotencyKey(idempotencyKey); var additions = AgentDefinitionExtensions.sessionSkills(skills); if (actorId == null || actorId.isEmpty()) { - throw new ManagedAgentException(HttpStatus.UNAUTHORIZED, + throw new ManagedAgentException(401, "actor_required", "A trusted actor is required."); } - List> input = input(blocks, false); + List> input = inputPolicy.input(blocks, false); String effectiveTitle = metadataTitle(title, metadata); Map semantic = new LinkedHashMap<>(); semantic.put("agentId", agentId); @@ -431,7 +431,7 @@ public class ManagedAgentService { } String requestDigest = digests.digest(semantic); String payloadDigest = input.isEmpty() ? null - : SubmitHarnessTurn.computePayloadDigest(input); + : harnessRules.payloadDigest(input); // Replay before the availability gate, like the legacy paths: a // recorded success must answer even while Workspace files are off. Optional recorded = store.findWorkspaceCreateReplay( @@ -441,18 +441,18 @@ public class ManagedAgentService { return response(recorded.get()); } if (modelId != null && (!"qwen-code".equals(agentId) || !harness.supportsModelSelection())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", + throw new ManagedAgentException(409, "unsupported_feature", "Initial model selection is unavailable for this Agent."); } if (!additions.isEmpty() && !harness.supportsSessionSkills()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", + throw new ManagedAgentException(409, "unsupported_feature", "The Hosted Harness does not support Session Skills."); } if (extensionPins != null && !harness.supportsSessionExtensions()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "The Hosted Harness does not support extension packages."); + throw new ManagedAgentException(409, "unsupported_feature", "The Hosted Harness does not support extension packages."); } if (!input.isEmpty() && !harness.isWorkspaceFilesAvailable()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "workspace_unavailable", "Hosted Workspace execution is not available."); } @@ -461,7 +461,7 @@ public class ManagedAgentService { : java.util.stream.Stream.of(WorkspaceToolProfiles.SHELL, WorkspaceToolProfiles.MCP) .filter(harness::supportsLifecycle).collect(java.util.stream.Collectors.toSet()); if (worktree != null && sessionWorktrees == null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "worktree_unavailable", "Session worktrees are unavailable."); + throw new ManagedAgentException(409, "worktree_unavailable", "Session worktrees are unavailable."); } SessionWorktreeStore.Plan worktreePlan = worktree == null ? null : sessionWorktrees.preflight(tenantId, actorId, selection, worktree); @@ -519,7 +519,7 @@ public class ManagedAgentService { List blocks = List.of(new InputBlock("input_text", description.isBlank() ? prompt : "[" + description + "]\n\n" + prompt)); - List> input = input(blocks, false); + List> input = inputPolicy.input(blocks, false); String title = description.isBlank() ? "child agent" : StringUtils.truncate(description, 256); return insertChildSession(tenantId, parentSessionId, childRunId, @@ -528,7 +528,7 @@ public class ManagedAgentService { public CommandAdmission createPublicChildSession(String tenantId, String parentSessionId, String childRunId, String description, String prompt, com.fasterxml.jackson.databind.JsonNode snapshot) { - List> input = input(List.of(new InputBlock("input_text", + List> input = inputPolicy.input(List.of(new InputBlock("input_text", description.isBlank() ? prompt : "[" + description + "]\n\n" + prompt)), false); return insertChildSession(tenantId, parentSessionId, childRunId, description.isBlank() ? "child agent" : StringUtils.truncate(description, 256), input, false, snapshot); @@ -579,13 +579,13 @@ public class ManagedAgentService { SessionRecord parent = store.requireSession(tenantId, parentSessionId); if (parent.workspace() == null || !"ACTIVE".equals(parent.status()) || CsiFilesRetirementProfile.PROFILE.equals(parent.toolProfile())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "child_parent_unavailable", "The parent Session cannot admit a child."); } String creationKey = childCreationKey(parentSessionId, childRunId); if (!normalizedInput.isEmpty() && !harness.isWorkspaceFilesAvailable()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "workspace_unavailable", "Hosted Workspace execution is not available."); } @@ -602,7 +602,7 @@ public class ManagedAgentService { childCwd = store.findChildWorkspaceCwd(tenantId, parentSessionId, childRunId); if (childCwd == null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "child_workspace_not_ready", "The child run's Workspace is not ready to bind a child Session."); } @@ -623,17 +623,17 @@ public class ManagedAgentService { admission = snapshot != null ? store.insertPublicChildSessionCommand(tenantId, parentSessionId, creationKey, requestDigest, title, normalizedInput, - SubmitHarnessTurn.computePayloadDigest(normalizedInput), lineage, snapshot) + harnessRules.payloadDigest(normalizedInput), lineage, snapshot) : childCwd == null ? store.insertChildSessionCommand(tenantId, parentSessionId, creationKey, requestDigest, title, normalizedInput, - SubmitHarnessTurn.computePayloadDigest(normalizedInput), + harnessRules.payloadDigest(normalizedInput), lineage) : store.insertChildSessionCommand(tenantId, parentSessionId, creationKey, requestDigest, title, normalizedInput, - SubmitHarnessTurn.computePayloadDigest(normalizedInput), + harnessRules.payloadDigest(normalizedInput), lineage, childCwd); } catch (DuplicateKeyException error) { admission = store.replayChildSessionCommand(tenantId, @@ -658,7 +658,7 @@ public class ManagedAgentService { String idempotencyKey, String sessionId, List blocks) { validateIdempotencyKey(idempotencyKey); - List> input = input(blocks, true); + List> input = inputPolicy.input(blocks, true); String requestDigest = digests.digest(Map.of( "sessionId", sessionId, "input", input)); SessionRecord session = store.requireSession(tenantId, sessionId); @@ -668,7 +668,7 @@ public class ManagedAgentService { // would meet below the replay, so a reused key's 409 never leaks to // one, and a below-OPERATOR reader never reaches a recorded key — // the command family's replay is not actor-scoped (#13619). - requireSubmitterRole(session, actorId); + inputPolicy.requireSubmitterRole(session, actorId); // Replay before the visibility and availability gates, so a // recorded submit still answers after a delete and through a // Harness outage. See createSession. The gates above pass every @@ -692,11 +692,11 @@ public class ManagedAgentService { // certifies new work only, so a recorded outcome always answers — // a re-registration, a DRAINING row or a creator demotion must not // retire it. - requireVisibleSession(session); - requireSubmitterFacts(session, actorId); + inputPolicy.requireVisibleSession(session); + inputPolicy.requireSubmitterFacts(session, actorId); requireReadyWorktree(session); requireHarness(); - String payloadDigest = SubmitHarnessTurn.computePayloadDigest(input); + String payloadDigest = harnessRules.payloadDigest(input); Admission admission; try { admission = store.insertTurnCommand(tenantId, SUBMIT, @@ -735,7 +735,7 @@ public class ManagedAgentService { dispatch(tenantId, replay, session.workspace() != null); return body; } - requireVisibleSession(session); + inputPolicy.requireVisibleSession(session); requireCancellerShape(session, actorId); Admission admission; try { @@ -831,7 +831,7 @@ public class ManagedAgentService { // from a fresh key's. SessionRecord session = store.requireSession(tenantId, sessionId); - requireSubmitterRole(session, actorId); + inputPolicy.requireSubmitterRole(session, actorId); if (!recordedByCaller(recorded, actorId)) { requireReplayCreator(session, actorId); } @@ -854,9 +854,9 @@ public class ManagedAgentService { // The fresh-key half answers a tombstone 404, as every other // Session read does; the recorded rename above is the only outcome // that survives the delete. - SessionRecord current = requireVisibleSession(tenantId, sessionId); - requireSubmitterRole(current, actorId); - requireSubmitterFacts(current, actorId); + SessionRecord current = inputPolicy.requireVisibleSession(tenantId, sessionId); + inputPolicy.requireSubmitterRole(current, actorId); + inputPolicy.requireSubmitterFacts(current, actorId); requireReadyWorktree(current); String effectiveTitle = validRenameTitle(title); requireHarness(); @@ -871,7 +871,7 @@ public class ManagedAgentService { String owner = UUID.randomUUID().toString(); var candidate = new StoreModels.RenameDelivery(tenantId, sessionId, idempotencyKey, effectiveTitle, command.renameRevision(), 0); - var delivery = store.claimRename(candidate, owner, renameDispatch.getLeaseDuration()).orElse(null); + var delivery = store.claimRename(candidate, owner, renameLeaseDuration.get()).orElse(null); if (delivery == null) { throw dependencyUnavailable("hosted_harness_unavailable", "The Session title is already being delivered."); @@ -894,10 +894,10 @@ public class ManagedAgentService { LOG.warn("Failed to retire rename tenant={} session={}", tenantId, sessionId, cleanupError); } - if (error instanceof DaemonHttpException refusal - && refusal.getStatusCode() == HttpStatus.CONFLICT.value() + if (error instanceof HarnessHttpFailure refusal + && refusal.getStatusCode() == 409 && "session_mutation_superseded".equals(refusal.getErrorCode())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "session_mutation_superseded", "A later Session title attempt has been admitted."); } @@ -906,10 +906,10 @@ public class ManagedAgentService { } if (error instanceof RuntimeBrokerException refusal && !refusal.isRetryable()) { - HttpStatus status = HttpStatus.resolve( + org.springframework.http.HttpStatus status = org.springframework.http.HttpStatus.resolve( refusal.getStatusCode()); throw new ManagedAgentException( - status == null ? HttpStatus.CONFLICT : status, + status == null ? 409 : status.value(), refusal.getCode(), refusal.getMessage()); } // Chain and log the root cause: a plain 503 leaves on-call @@ -918,7 +918,7 @@ public class ManagedAgentService { // operation rather than a subsystem the fault may not be in. LOG.warn("Managed Agent rename failed tenant={} session={}", tenantId, sessionId, error); - throw new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, + throw new ManagedAgentException(503, "hosted_harness_unavailable", "The Hosted Harness could not persist the Session title.", error); @@ -969,7 +969,7 @@ public class ManagedAgentService { return store.unarchiveWorkspaceSession(tenantId, sessionId, actorId, scopedKey, lifecycleDigest(sessionId, UNARCHIVE)); } - requireLegacyWorkspace(tenantId, actorId, sessionId); + inputPolicy.requireLegacyWorkspace(tenantId, actorId, sessionId); String requestDigest = lifecycleDigest(sessionId, UNARCHIVE); SessionMutationCommand command = store.beginSessionMutation(tenantId, UNARCHIVE, idempotencyKey, requestDigest, sessionId, @@ -1064,7 +1064,7 @@ public class ManagedAgentService { private static String archiveState(String requested) { String state = requested == null ? "all" : requested; if (!List.of("all", "unarchived", "archived").contains(state)) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_archive_state", "Unknown archive state."); } return state; @@ -1072,7 +1072,7 @@ public class ManagedAgentService { private static String titleQuery(String requested) { if (requested != null && requested.codePointCount(0, requested.length()) > 512) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_title_query", "Title query must not exceed 512 characters."); } if (requested == null) { @@ -1111,13 +1111,13 @@ public class ManagedAgentService { String sessionId, String turnId) { // The path schema counts characters, not UTF-16 units. if (turnId.codePointCount(0, turnId.length()) > TURN_ID_MAX_LENGTH) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", + throw new ManagedAgentException(400, "invalid_request", "The Turn id is invalid."); } requireReadableSession(tenantId, actorId, sessionId); return store.findTurnSummary(tenantId, sessionId, turnId) .map(ManagedAgentService::publicTurn) - .orElseThrow(() -> new ManagedAgentException(HttpStatus.NOT_FOUND, + .orElseThrow(() -> new ManagedAgentException(404, "turn_not_found", "The Turn was not found.")); } @@ -1146,7 +1146,7 @@ public class ManagedAgentService { public PublicItemList listPublicItems(String tenantId, String actorId, String sessionId, String cursor, long afterSequence, int requestedLimit) { if (afterSequence < 0) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_event_cursor", "Item sequence must be non-negative."); + throw new ManagedAgentException(400, "invalid_event_cursor", "Item sequence must be non-negative."); } requireReadableSession(tenantId, actorId, sessionId); var page = store.findMessagePage(tenantId, sessionId, cursor, limit(requestedLimit), false, afterSequence); @@ -1196,7 +1196,7 @@ public class ManagedAgentService { private static void requireEventCursor(long afterSequence) { if (afterSequence < 0) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_event_cursor", "Event sequence must be non-negative."); } @@ -1345,7 +1345,7 @@ public class ManagedAgentService { // Cwd admission has no Turn execution-profile restriction. One role // grant read covers the workspaces of both shapes; the executable // facts read covers both id sets, retaining each capability's gates. - Set shaped = sessions.stream().filter(session -> maySubmitShape(session, worktrees.get(session.sessionId()))) + Set shaped = sessions.stream().filter(session -> inputPolicy.maySubmitShape(session, worktrees.get(session.sessionId()))) .map(SessionRecord::sessionId) .collect(java.util.stream.Collectors.toSet()); Set contextShaped = sessions.stream().filter(session -> mayChangeContextShape(session, worktrees.get(session.sessionId()))) @@ -1383,7 +1383,7 @@ public class ManagedAgentService { retention(session, closed), session.workspace() == null || lifecycleOwners.contains(session.sessionId()), lifecycleOperations.get(session.sessionId()), - maySubmitWorkspaceTurn(session, grants, executable, worktrees.get(session.sessionId())), + inputPolicy.maySubmitWorkspaceTurn(session, grants, executable, worktrees.get(session.sessionId())), mayChangeContext(session, grants, executable, worktrees.get(session.sessionId())), modelContexts.get(session.sessionId()), modelSelections.get(session.sessionId()), modelSelectionSupported, renameSupported, @@ -1647,7 +1647,7 @@ public class ManagedAgentService { } private static boolean ready(SessionWorktreeStore.Descriptor worktree) { - return worktree == null || "ready".equals(worktree.state()); + return ManagedUserMessagePolicy.ready(worktree); } String lifecycleDigest(String sessionId, String operation) { @@ -1655,42 +1655,10 @@ public class ManagedAgentService { "sessionId", sessionId, "operation", operation)); } - // Later Turns of a Workspace-bound Session run under the creator's - // Workspace grants (WorkspaceExecutionStore.authorize), so new-work - // admission certifies two conjuncts: the caller holds OPERATOR - // (requireSubmitterRole), and the Session's creator-keyed execution - // facts still hold (requireSubmitterFacts). The command family's - // replay is NOT actor-scoped (#13619), so the role gate runs before - // the replay: below-OPERATOR never reaches a recorded key. The facts - // gate follows the replay: it certifies new work only, so a recorded - // completed outcome always answers — a Workspace re-registration, a - // DRAINING row or a creator demotion must not retire it. Cancelling - // has its own, narrower rule (requireCancellerRole/Shape). - private void requireSubmitterRole(SessionRecord session, - String actorId) { - if (session.workspace() != null - && !workspaces.accessOf(session.tenantId(), actorId, - session.workspace().getWorkspaceId()) - .atLeast(WorkspaceAccess.OPERATOR)) { - requireLegacyWorkspace(session, actorId); - } - } - - private void requireSubmitterFacts(SessionRecord session, - String actorId) { - if (session.publicChildAgentsVersion() == 1 && store.findChildLineage(session.tenantId(), session.sessionId()) != null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "Public child Sessions accept only their delegated input"); - } - if (session.workspace() != null - && !maySubmitWorkspaceTurn(session, actorId)) { - requireLegacyWorkspace(session, actorId); - } - } - private void requireReadyWorktree(SessionRecord session) { var worktree = store.findSessionWorktree(session.tenantId(), session.sessionId()); if (!ready(worktree)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, worktree.errorCode() == null ? "worktree_" + worktree.state() : worktree.errorCode(), "The Session worktree is not ready."); } @@ -1718,10 +1686,10 @@ public class ManagedAgentService { // meets the reads' 404 there, never the live-Session 409 that // would name the row. if ("DELETED".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, + throw new ManagedAgentException(404, "session_not_found", "The Session was not found."); } - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "workspace_unavailable", "Hosted Workspace execution is not available."); } @@ -1739,14 +1707,14 @@ public class ManagedAgentService { && !workspaces.accessOf(session.tenantId(), actorId, session.workspace().getWorkspaceId()) .atLeast(WorkspaceAccess.OPERATOR)) { - requireLegacyWorkspace(session, actorId); + inputPolicy.requireLegacyWorkspace(session, actorId); } } private void requireCancellerShape(SessionRecord session, String actorId) { - if (session.workspace() != null && !maySubmitShape(session, null)) { - requireLegacyWorkspace(session, actorId); + if (session.workspace() != null && !inputPolicy.maySubmitShape(session, null)) { + inputPolicy.requireLegacyWorkspace(session, actorId); } } @@ -1779,52 +1747,11 @@ public class ManagedAgentService { && !workspaces.accessOf(session.tenantId(), actorId, session.workspace().getWorkspaceId()) .atLeast(WorkspaceAccess.OPERATOR)) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, "task_forbidden", + throw new ManagedAgentException(403, "task_forbidden", "Only a Workspace operator may cancel the task."); } } - private boolean maySubmitWorkspaceTurn(SessionRecord session, - String actorId) { - if (!maySubmitShape(session, null) || actorId == null - || actorId.isEmpty()) { - return false; - } - // findReadable throws on an actor id the registry key cannot - // encode; that caller is simply not a submitter. - ManagedWorkspaceRegistry.WorkspaceSummary summary; - try { - summary = workspaces.findReadable(session.tenantId(), actorId, - session.workspace().getWorkspaceId()); - } catch (IllegalArgumentException error) { - return false; - } - // The caller's role and registry state, and on top of them the - // creator-keyed facts the execution authority re-verifies: the - // Registry still backs the binding exactly and stays ACTIVE, and - // the create-command actor keeps OPERATOR or above — after a - // demotion or re-registration admission refuses first instead of - // accepting a Turn that can only fail. - return summary != null && summary.canCreateSession() - && store.hasExecutionRegistryFacts(session.tenantId(), - session.sessionId()); - } - - // The page rule also requires readiness; its permission facts use the - // batch reads the assembler already made — the caller's role from the - // grant batch, the creator-keyed facts of each submit-shaped Session - // from the facts batch. - private boolean maySubmitWorkspaceTurn(SessionRecord session, - Map grants, - java.util.Set executable, SessionWorktreeStore.Descriptor worktree) { - if (!maySubmitShape(session, worktree)) { - return false; - } - var grant = grants.get(session.workspace().getWorkspaceId()); - return grant != null && grant.canCreateSession() - && executable.contains(session.sessionId()); - } - // Model, approval and plan changes share the caller and execution // authority checks for changing an existing Session context. private boolean mayChangeContext(SessionRecord session, @@ -1838,23 +1765,6 @@ public class ManagedAgentService { && executable.contains(session.sessionId()); } - private boolean maySubmitShape(SessionRecord session, SessionWorktreeStore.Descriptor worktree) { - if (session.workspace() == null - || !harness.isWorkspaceFilesAvailable()) { - return false; - } - // The authority execution cites (WorkspaceExecutionStore - // .authorizePassiveAttachment) fixes these at creation: a Session - // that fails them can never execute, so admission must not certify - // it. Empty bound creation skips that validation by design. - return "ACTIVE".equals(session.status()) && session.deletedAt() == null - && ready(worktree) - && ("qwen-code".equals(session.agentId()) - || session.agentDefinitionDigest() != null) - && WorkspaceExecutionProfile.CONTEXT_CONFIG_REF.equals( - session.workspace().getContextConfigRef()); - } - private boolean mayCompressShape(SessionRecord session, SessionWorktreeStore.Descriptor worktree) { return session.workspace() != null && "ACTIVE".equals(session.status()) && session.deletedAt() == null && store.workspaceFilesEnabled() @@ -1879,48 +1789,12 @@ public class ManagedAgentService { void requireLegacyWorkspace(String tenantId, String actorId, String sessionId) { - requireLegacyWorkspace(store.requireSession(tenantId, sessionId), - actorId); - } - - // The submitter family's bound-refusal split: an actor without a read - // grant gets the invisible 404, a readable actor below OPERATOR the - // 403 of the sibling lifecycle refusal, and an OPERATOR blocked by the - // shape gates or the creator-keyed facts the family's domain 409. - private void requireLegacyWorkspace(SessionRecord session, - String actorId) { - if (session.workspace() != null) { - // A tombstone is invisible to every actor shape, as the reads - // answer: the fence stands above the role split, whose 403/409 - // codes are live-Session answers. An unbound tombstone needs no - // fence here — its 404 stands below the replay in - // beginSessionMutation, and a recorded lifecycle outcome must - // keep answering above it. - if ("DELETED".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, - "session_not_found", "The Session was not found."); - } - String workspaceId = session.workspace().getWorkspaceId(); - WorkspaceAccess access = workspaces.accessOf(session.tenantId(), - actorId, workspaceId); - if (!access.canRead()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, - "session_not_found", "The Session was not found."); - } - if (access.atLeast(WorkspaceAccess.OPERATOR)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, - "workspace_unavailable", - "Hosted Workspace execution is not available."); - } - throw new ManagedAgentException(HttpStatus.FORBIDDEN, - "session_operation_forbidden", - "Only a Workspace operator may use the Session."); - } + inputPolicy.requireLegacyWorkspace(tenantId, actorId, sessionId); } public SessionRecord requireReadableSession(String tenantId, String actorId, String sessionId) { - SessionRecord session = requireVisibleSession(tenantId, sessionId); + SessionRecord session = inputPolicy.requireVisibleSession(tenantId, sessionId); requireReadGrant(session, actorId); return session; } @@ -1928,7 +1802,7 @@ public class ManagedAgentService { void requireReadGrant(SessionRecord session, String actorId) { if (session.workspace() != null && !workspaces.canRead(session.tenantId(), actorId, session.workspace().getWorkspaceId())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, + throw new ManagedAgentException(404, "session_not_found", "The Session was not found."); } } @@ -1944,23 +1818,9 @@ public class ManagedAgentService { return replayableEvents(session, afterSequence, STREAM_PAGE); } - private SessionRecord requireVisibleSession(String tenantId, - String sessionId) { - return requireVisibleSession(store.requireSession(tenantId, - sessionId)); - } - - private SessionRecord requireVisibleSession(SessionRecord session) { - if ("DELETED".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, - "session_not_found", "The Session was not found."); - } - return session; - } - private static ManagedAgentException dependencyUnavailable(String code, String message) { - return new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, code, + return new ManagedAgentException(503, code, message); } @@ -1998,7 +1858,7 @@ public class ManagedAgentService { // and the DuplicateKeyException arm then answer the recorded admission // with no gate at all, so a replayed answer re-runs the check on the // command row. The read stays a plain probe — never a locking one, so - // ManagedAgentStore's replay-probe rule still holds. + // AgentStateStore's replay-probe rule still holds. private void requireReplayedAdmission(String tenantId, String operation, String idempotencyKey, Admission admission, SessionRecord session, String actorId) { @@ -2012,7 +1872,7 @@ public class ManagedAgentService { // The probe half, standing alone for the mutation routes whose replay // arms carry no Admission: rename's in-store COMPLETED read-back has // only the command row to interrogate. The read stays a plain probe — - // never a locking one, so ManagedAgentStore's replay-probe rule still + // never a locking one, so AgentStateStore's replay-probe rule still // holds. private void requireReplayActor(String tenantId, String operation, String idempotencyKey, SessionRecord session, String actorId) { @@ -2032,90 +1892,26 @@ public class ManagedAgentService { private void requireHarness() { if (!harness.isAvailable()) { - throw new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, + throw new ManagedAgentException(503, "hosted_harness_disabled", "Hosted Harness is not configured."); } } - public boolean supportsUserMessageQueue() { return harness.supportsUserMessageQueue(); } + public boolean supportsUserMessageQueue() { + return inputPolicy.supportsUserMessageQueue(); + } public SessionRecord authorizeUserMessageActor(String tenantId, String actorId, String sessionId) { - if (actorId == null || actorId.isBlank()) { - throw new ManagedAgentException(HttpStatus.UNAUTHORIZED, "actor_required", "An authenticated actor is required."); - } - SessionRecord session = store.requireSession(tenantId, sessionId); - requireSubmitterRole(session, actorId); - return session; + return inputPolicy.authorizeUserMessageActor(tenantId, actorId, sessionId); } public SessionRecord authorizeUserMessages(String tenantId, String actorId, String sessionId) { - SessionRecord session = authorizeUserMessageActor(tenantId, actorId, sessionId); - requireVisibleSession(session); - requireSubmitterFacts(session, actorId); - if (!"ACTIVE".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "session_not_active", "The Session does not accept messages."); - } - return session; + return inputPolicy.authorizeUserMessages(tenantId, actorId, sessionId); } public List> userMessageInput(List blocks) { - List> normalized = input(blocks, true); - if (normalized.stream().anyMatch(block -> "image".equals(block.get("type")))) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_input", "Images require an idle Turn."); - } - return normalized; - } - - private List> input(List blocks, - boolean required) { - if (blocks == null || blocks.isEmpty()) { - if (required) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, - "input_required", "At least one input is required."); - } - return List.of(); - } - List> result = new ArrayList<>(); - long totalChars = 0; - for (InputBlock block : blocks) { - if (block != null && !INPUT_TYPES.contains(block.type())) { - result.add(ManagedInputAttachments.normalize(block)); - continue; - } - if (block == null || block.text() == null || block.text().isEmpty() - || block.mimeType() != null || block.name() != null || block.data() != null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, - "unsupported_input", - "Expected non-empty text, a text file or an image."); - } - // Code points, matching the published schema's maxLength; a - // String.length() count would disagree on astral-plane text. - int blockChars = block.text().codePointCount(0, - block.text().length()); - if (blockChars > MAX_BLOCK_INPUT_CHARS) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, - "invalid_input", - "Input block exceeds the " + MAX_BLOCK_INPUT_CHARS - + " character per-block limit."); - } - totalChars += blockChars; - if (totalChars > MAX_AGGREGATE_INPUT_CHARS) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, - "invalid_input", - "Input exceeds the " + MAX_AGGREGATE_INPUT_CHARS - + " character aggregate limit."); - } - result.add(Map.of("type", "text", "text", - replaceLoneSurrogates(block.text()))); - } - var normalized = normalizeInput(result); - if (normalized.stream().anyMatch(block -> !"text".equals(block.get("type"))) - && digests.byteLength(normalized) > ManagedInputAttachments.MAX_INPUT_BYTES) { - throw new ManagedAgentException(HttpStatus.PAYLOAD_TOO_LARGE, "input_too_large", - "An input containing attachments must fit within 8 MiB of encoded JSON."); - } - return normalized; + return inputPolicy.userMessageInput(blocks); } /** The input the digest and the column both describe: an unpaired @@ -2126,60 +1922,15 @@ public class ManagedAgentService { @SuppressWarnings("unchecked") private static List> normalizeInput( List> input) { - List> result = new ArrayList<>(input.size()); - for (Map block : input) { - result.add((Map) normalizeValue(block)); - } - return List.copyOf(result); + return ManagedUserMessagePolicy.normalizeInput(input); } private static Object normalizeValue(Object value) { - if (value instanceof String text) { - return replaceLoneSurrogates(text); - } - if (value instanceof Map map) { - Map copy = new LinkedHashMap<>(map.size()); - for (Map.Entry entry : map.entrySet()) { - copy.put(replaceLoneSurrogates((String) entry.getKey()), - normalizeValue(entry.getValue())); - } - return copy; - } - if (value instanceof List list) { - List copy = new ArrayList<>(list.size()); - for (Object item : list) { - copy.add(normalizeValue(item)); - } - return Collections.unmodifiableList(copy); - } - return value; + return ManagedUserMessagePolicy.normalizeValue(value); } static String replaceLoneSurrogates(String text) { - StringBuilder out = null; - int written = 0; - for (int i = 0; i < text.length(); i++) { - char current = text.charAt(i); - if (Character.isHighSurrogate(current)) { - if (i + 1 < text.length() - && Character.isLowSurrogate(text.charAt(i + 1))) { - i++; // a well-formed pair passes through unchanged - continue; - } - } else if (!Character.isLowSurrogate(current)) { - continue; - } - if (out == null) { - out = new StringBuilder(text.length()); - } - out.append(text, written, i).append('�'); - written = i + 1; - } - if (out == null) { - return text; - } - out.append(text, written, text.length()); - return out.toString(); + return ManagedUserMessagePolicy.replaceLoneSurrogates(text); } private static String metadataTitle(String explicitTitle, @@ -2188,7 +1939,7 @@ public class ManagedAgentService { return validTitle(explicitTitle); } if (metadata.size() > 16) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_metadata", "Metadata accepts at most 16 keys."); } for (Map.Entry entry : metadata.entrySet()) { @@ -2196,11 +1947,11 @@ public class ManagedAgentService { || entry.getKey().length() > 64 || (!"title".equals(entry.getKey()) && ((String) entry.getValue()).length() > 512)) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_metadata", "Metadata limits were exceeded."); } if (!"title".equals(entry.getKey())) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "unsupported_feature", "Phase 1 persists metadata.title only."); } @@ -2228,12 +1979,12 @@ public class ManagedAgentService { return null; } if (title.length() > 256) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_title", + throw new ManagedAgentException(400, "invalid_title", "Title must not exceed 256 characters."); } if (title.chars().anyMatch(character -> character <= 31 || character == 127)) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_title", + throw new ManagedAgentException(400, "invalid_title", "Title must not contain control characters."); } return unicodeBlank(title) ? null : title; @@ -2241,7 +1992,7 @@ public class ManagedAgentService { private static String validRenameTitle(String title) { if (title == null || unicodeBlank(title)) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_title", + throw new ManagedAgentException(400, "invalid_title", "Title must contain 1-256 characters."); } return validTitle(title); @@ -2249,7 +2000,7 @@ public class ManagedAgentService { public static void validateIdempotencyKey(String key) { if (key == null || !IDEMPOTENCY_KEY.matcher(key).matches()) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_idempotency_key", "Idempotency-Key must contain 1-128 visible characters."); } @@ -2257,7 +2008,7 @@ public class ManagedAgentService { private static int limit(int requested) { if (requested <= 0 || requested > 100) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_limit", + throw new ManagedAgentException(400, "invalid_limit", "Limit must be between 1 and 100."); } return requested; @@ -2265,7 +2016,7 @@ public class ManagedAgentService { private static int eventLimit(int requested) { if (requested <= 0 || requested > 1000) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_limit", + throw new ManagedAgentException(400, "invalid_limit", "Limit must be between 1 and 1000."); } return requested; @@ -2304,7 +2055,7 @@ public class ManagedAgentService { decoded.substring(0, separator)), decoded.substring(separator + 1)); } catch (IllegalArgumentException error) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_cursor", "Session cursor is invalid."); } } @@ -2339,7 +2090,7 @@ public class ManagedAgentService { } catch (NumberFormatException error) { // A creation time beyond a long is not a cursor this server wrote. } - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_cursor", + throw new ManagedAgentException(400, "invalid_cursor", "Turn cursor is invalid."); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamService.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamService.java index a885b0d5674..6a7a47865cc 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamService.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.session.ReplayCursorExpired; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicEvent; import com.alibaba.qwen.code.managedagent.model.ApiModels.SessionResyncRequired; @@ -11,8 +11,8 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.ReplayWindow; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.session.SessionEventHub.Delivery; import com.alibaba.qwen.code.managedagent.session.SessionEventHub.Subscription; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; @@ -37,7 +37,7 @@ public class ManagedEventStreamService { public ManagedEventStreamService(ManagedAgentService agentService, SessionEventHub eventHub, @Qualifier("managedAgentExecutor") ExecutorService executor, - ManagedAgentProperties properties) { + ManagedAgentSettings properties) { this.agentService = agentService; this.eventHub = eventHub; this.executor = executor; @@ -323,7 +323,7 @@ public class ManagedEventStreamService { agentService.requireReadGrant(session, actorId); return true; } catch (ManagedAgentException error) { - if (error.getStatus() != HttpStatus.NOT_FOUND) { + if (error.getStatus() != 404) { throw error; } complete(emitter, closed); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachments.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachments.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachments.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachments.java index 4baf957479e..9e2a86cf8ae 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachments.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachments.java @@ -1,9 +1,8 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import org.springframework.http.HttpStatus; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -78,7 +77,7 @@ final class ManagedInputAttachments { } private static ManagedAgentException invalid() { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_input", + return new ManagedAgentException(400, "unsupported_input", "Attachments require a UTF-8 text file or a valid PNG, JPEG, GIF or WebP image; URLs and host paths are unsupported."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskCancelService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskCancelService.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskCancelService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskCancelService.java index 8567731bcc1..9b442bfcb9b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskCancelService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskCancelService.java @@ -1,13 +1,13 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicCommandOperation; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellCommandOperation; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; -import org.springframework.http.HttpStatus; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore; import org.springframework.stereotype.Service; import java.util.Map; @@ -70,7 +70,7 @@ public class ManagedTaskCancelService { SessionRecord session = sessions.requireReadableSession(tenantId, actorId, sessionId); if (records.findTask(tenantId, sessionId, taskId).isEmpty()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "task_not_found", + throw new ManagedAgentException(404, "task_not_found", "The task was not found."); } sessions.requireTaskCanceller(session, actorId); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskService.java similarity index 85% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskService.java index aaef3297674..3fce717aca8 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedTaskService.java @@ -1,5 +1,7 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.TaskProjection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicList; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicTask; @@ -8,16 +10,13 @@ import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellPage; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellTask; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellTaskEvent; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellTaskPage; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection.TaskProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore.TaskPage; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore.TaskRow; -import com.alibaba.qwen.code.managedagent.persistence.ManagedTaskEventStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedTaskEventStore.CursorPositions; -import com.alibaba.qwen.code.managedagent.persistence.ManagedTaskEventStore.EventPage; -import com.alibaba.qwen.code.managedagent.persistence.ManagedTaskEventStore.TaskEvent; -import org.springframework.http.HttpStatus; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.TaskPage; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.TaskRow; +import com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.CursorPositions; +import com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.EventPage; +import com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.TaskEvent; import org.springframework.stereotype.Service; import java.nio.charset.StandardCharsets; @@ -120,7 +119,7 @@ public class ManagedTaskService { private TaskPage pageOf(String tenantId, String sessionId, String cursor, int limit) { if (limit < 1 || limit > 100) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_limit", + throw new ManagedAgentException(400, "invalid_limit", "Limit must be between 1 and 100."); } if (cursor == null || cursor.isEmpty()) { @@ -135,14 +134,14 @@ public class ManagedTaskService { } var matcher = CURSOR.matcher(decoded); if (!matcher.matches()) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_cursor", + throw new ManagedAgentException(400, "invalid_cursor", "Task cursor is invalid."); } try { return records.listTasks(tenantId, sessionId, Long.parseLong(matcher.group(1)), matcher.group(2), limit); } catch (NumberFormatException error) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_cursor", + throw new ManagedAgentException(400, "invalid_cursor", "Task cursor is invalid."); } } @@ -152,7 +151,7 @@ public class ManagedTaskService { boolean active = "ACTIVE".equals(sessions.requireReadableSession( tenantId, actorId, sessionId).status()); return new Viewed<>(records.findTask(tenantId, sessionId, taskId) - .orElseThrow(() -> new ManagedAgentException(HttpStatus.NOT_FOUND, + .orElseThrow(() -> new ManagedAgentException(404, "task_not_found", "The task was not found.")), active); } @@ -172,7 +171,7 @@ public class ManagedTaskService { String sessionId, String taskId, String after, int limit) { task(tenantId, actorId, sessionId, taskId); if (limit < 1 || limit > 100) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_limit", + throw new ManagedAgentException(400, "invalid_limit", "Limit must be between 1 and 100."); } long position; @@ -180,7 +179,7 @@ public class ManagedTaskService { position = events.positions(tenantId, sessionId, taskId) .expiredThrough(); } else { - position = ManagedTaskEventStore.decodeCursor(tenantId, sessionId, taskId, after); + position = com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.decodeCursor(tenantId, sessionId, taskId, after); } EventPage page = events.read(tenantId, sessionId, taskId, position, limit); @@ -194,17 +193,17 @@ public class ManagedTaskService { long floor = events.positions(tenantId, sessionId, taskId) .expiredThrough(); if (after != null && !after.isEmpty() && position < floor) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "cursor_expired", + throw new ManagedAgentException(409, "cursor_expired", "The task event cursor is below the task's durable" + " retention floor."); } String nextCursor; if (page.events().isEmpty()) { - nextCursor = ManagedTaskEventStore.encodeCursor(tenantId, sessionId, taskId, + nextCursor = com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.encodeCursor(tenantId, sessionId, taskId, Math.max(position, floor)); } else { TaskEvent last = page.events().get(page.events().size() - 1); - nextCursor = ManagedTaskEventStore.encodeCursor(tenantId, sessionId, taskId, + nextCursor = com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.encodeCursor(tenantId, sessionId, taskId, last.sequence()); } return new ResolvedPage(page, nextCursor); @@ -263,7 +262,7 @@ public class ManagedTaskService { private static String outputCursor(String tenantId, String sessionId, String taskId, CursorPositions positions) { return positions.lastSequence() == 0 ? null - : ManagedTaskEventStore.encodeCursor(tenantId, sessionId, taskId, + : com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.encodeCursor(tenantId, sessionId, taskId, positions.lastSequence()); } @@ -271,7 +270,7 @@ public class ManagedTaskService { String taskId, TaskEvent event) { return new PublicTaskEvent(event.schemaVersion(), event.projectionVersion(), taskId, sessionId, event.type(), - ManagedTaskEventStore.encodeCursor(tenantId, sessionId, taskId, event.sequence()), + com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.encodeCursor(tenantId, sessionId, taskId, event.sequence()), event.occurredAt(), event.state(), event.runtimeState(), event.text(), event.truncated(), event.artifactId()); } @@ -280,7 +279,7 @@ public class ManagedTaskService { String taskId, TaskEvent event) { return new WebShellTaskEvent(event.schemaVersion(), event.projectionVersion(), taskId, sessionId, event.type(), - ManagedTaskEventStore.encodeCursor(tenantId, sessionId, taskId, event.sequence()), + com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.encodeCursor(tenantId, sessionId, taskId, event.sequence()), event.occurredAt(), event.state(), event.runtimeState(), event.text(), event.truncated(), event.artifactId()); } diff --git a/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedUserMessagePolicy.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedUserMessagePolicy.java new file mode 100644 index 00000000000..73fc40da650 --- /dev/null +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ManagedUserMessagePolicy.java @@ -0,0 +1,330 @@ +package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.session.UserMessagePolicy; +import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; +import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; +import com.alibaba.qwen.code.managedagent.model.RequestDigests; +import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; +import com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry; +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore; +import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceAccess; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +@Service("managedUserMessagePolicy") +public class ManagedUserMessagePolicy implements UserMessagePolicy { + private final AgentStateStore store; + private final RequestDigests digests; + private final HarnessConnector harness; + private final ManagedWorkspaceRegistry workspaces; + + public ManagedUserMessagePolicy(AgentStateStore store, RequestDigests digests, + HarnessConnector harness, ManagedWorkspaceRegistry workspaces) { + this.store = store; + this.digests = digests; + this.harness = harness; + this.workspaces = workspaces; + } + + private static final Set INPUT_TYPES = Set.of("input_text", + "text"); + + private static final int MAX_AGGREGATE_INPUT_CHARS = 4 * 1000 * 1000; + + private static final int MAX_BLOCK_INPUT_CHARS = 1000 * 1000; + + static boolean ready(SessionWorktreeStore.Descriptor worktree) { + return worktree == null || "ready".equals(worktree.state()); + } + + // Later Turns of a Workspace-bound Session run under the creator's + // Workspace grants (WorkspaceExecutionStore.authorize), so new-work + // admission certifies two conjuncts: the caller holds OPERATOR + // (requireSubmitterRole), and the Session's creator-keyed execution + // facts still hold (requireSubmitterFacts). The command family's + // replay is NOT actor-scoped (#13619), so the role gate runs before + // the replay: below-OPERATOR never reaches a recorded key. The facts + // gate follows the replay: it certifies new work only, so a recorded + // completed outcome always answers — a Workspace re-registration, a + // DRAINING row or a creator demotion must not retire it. Cancelling + // has its own, narrower rule (requireCancellerRole/Shape). + void requireSubmitterRole(SessionRecord session, + String actorId) { + if (session.workspace() != null + && !workspaces.accessOf(session.tenantId(), actorId, + session.workspace().getWorkspaceId()) + .atLeast(WorkspaceAccess.OPERATOR)) { + requireLegacyWorkspace(session, actorId); + } + } + + void requireSubmitterFacts(SessionRecord session, + String actorId) { + if (session.publicChildAgentsVersion() == 1 && store.findChildLineage(session.tenantId(), session.sessionId()) != null) { + throw new ManagedAgentException(409, "unsupported_feature", "Public child Sessions accept only their delegated input"); + } + if (session.workspace() != null + && !maySubmitWorkspaceTurn(session, actorId)) { + requireLegacyWorkspace(session, actorId); + } + } + + boolean maySubmitWorkspaceTurn(SessionRecord session, + String actorId) { + if (!maySubmitShape(session, null) || actorId == null + || actorId.isEmpty()) { + return false; + } + // findReadable throws on an actor id the registry key cannot + // encode; that caller is simply not a submitter. + ManagedWorkspaceRegistry.WorkspaceSummary summary; + try { + summary = workspaces.findReadable(session.tenantId(), actorId, + session.workspace().getWorkspaceId()); + } catch (IllegalArgumentException error) { + return false; + } + // The caller's role and registry state, and on top of them the + // creator-keyed facts the execution authority re-verifies: the + // Registry still backs the binding exactly and stays ACTIVE, and + // the create-command actor keeps OPERATOR or above — after a + // demotion or re-registration admission refuses first instead of + // accepting a Turn that can only fail. + return summary != null && summary.canCreateSession() + && store.hasExecutionRegistryFacts(session.tenantId(), + session.sessionId()); + } + + // The page rule also requires readiness; its permission facts use the + // batch reads the assembler already made — the caller's role from the + // grant batch, the creator-keyed facts of each submit-shaped Session + // from the facts batch. + boolean maySubmitWorkspaceTurn(SessionRecord session, + Map grants, + java.util.Set executable, SessionWorktreeStore.Descriptor worktree) { + if (!maySubmitShape(session, worktree)) { + return false; + } + var grant = grants.get(session.workspace().getWorkspaceId()); + return grant != null && grant.canCreateSession() + && executable.contains(session.sessionId()); + } + + boolean maySubmitShape(SessionRecord session, SessionWorktreeStore.Descriptor worktree) { + if (session.workspace() == null + || !harness.isWorkspaceFilesAvailable()) { + return false; + } + // The authority execution cites (WorkspaceExecutionStore + // .authorizePassiveAttachment) fixes these at creation: a Session + // that fails them can never execute, so admission must not certify + // it. Empty bound creation skips that validation by design. + return "ACTIVE".equals(session.status()) && session.deletedAt() == null + && ready(worktree) + && ("qwen-code".equals(session.agentId()) + || session.agentDefinitionDigest() != null) + && WorkspaceExecutionProfile.CONTEXT_CONFIG_REF.equals( + session.workspace().getContextConfigRef()); + } + + void requireLegacyWorkspace(String tenantId, String actorId, + String sessionId) { + requireLegacyWorkspace(store.requireSession(tenantId, sessionId), + actorId); + } + + // The submitter family's bound-refusal split: an actor without a read + // grant gets the invisible 404, a readable actor below OPERATOR the + // 403 of the sibling lifecycle refusal, and an OPERATOR blocked by the + // shape gates or the creator-keyed facts the family's domain 409. + void requireLegacyWorkspace(SessionRecord session, + String actorId) { + if (session.workspace() != null) { + // A tombstone is invisible to every actor shape, as the reads + // answer: the fence stands above the role split, whose 403/409 + // codes are live-Session answers. An unbound tombstone needs no + // fence here — its 404 stands below the replay in + // beginSessionMutation, and a recorded lifecycle outcome must + // keep answering above it. + if ("DELETED".equals(session.status())) { + throw new ManagedAgentException(404, + "session_not_found", "The Session was not found."); + } + String workspaceId = session.workspace().getWorkspaceId(); + WorkspaceAccess access = workspaces.accessOf(session.tenantId(), + actorId, workspaceId); + if (!access.canRead()) { + throw new ManagedAgentException(404, + "session_not_found", "The Session was not found."); + } + if (access.atLeast(WorkspaceAccess.OPERATOR)) { + throw new ManagedAgentException(409, + "workspace_unavailable", + "Hosted Workspace execution is not available."); + } + throw new ManagedAgentException(403, + "session_operation_forbidden", + "Only a Workspace operator may use the Session."); + } + } + + SessionRecord requireVisibleSession(String tenantId, + String sessionId) { + return requireVisibleSession(store.requireSession(tenantId, + sessionId)); + } + + SessionRecord requireVisibleSession(SessionRecord session) { + if ("DELETED".equals(session.status())) { + throw new ManagedAgentException(404, + "session_not_found", "The Session was not found."); + } + return session; + } + + public boolean supportsUserMessageQueue() { return harness.supportsUserMessageQueue(); } + + public SessionRecord authorizeUserMessageActor(String tenantId, String actorId, String sessionId) { + if (actorId == null || actorId.isBlank()) { + throw new ManagedAgentException(401, "actor_required", "An authenticated actor is required."); + } + SessionRecord session = store.requireSession(tenantId, sessionId); + requireSubmitterRole(session, actorId); + return session; + } + + public SessionRecord authorizeUserMessages(String tenantId, String actorId, String sessionId) { + SessionRecord session = authorizeUserMessageActor(tenantId, actorId, sessionId); + requireVisibleSession(session); + requireSubmitterFacts(session, actorId); + if (!"ACTIVE".equals(session.status())) { + throw new ManagedAgentException(409, "session_not_active", "The Session does not accept messages."); + } + return session; + } + + public List> userMessageInput(List blocks) { + List> normalized = input(blocks, true); + if (normalized.stream().anyMatch(block -> "image".equals(block.get("type")))) { + throw new ManagedAgentException(400, "unsupported_input", "Images require an idle Turn."); + } + return normalized; + } + + List> input(List blocks, + boolean required) { + if (blocks == null || blocks.isEmpty()) { + if (required) { + throw new ManagedAgentException(400, + "input_required", "At least one input is required."); + } + return List.of(); + } + List> result = new ArrayList<>(); + long totalChars = 0; + for (InputBlock block : blocks) { + if (block != null && !INPUT_TYPES.contains(block.type())) { + result.add(ManagedInputAttachments.normalize(block)); + continue; + } + if (block == null || block.text() == null || block.text().isEmpty() + || block.mimeType() != null || block.name() != null || block.data() != null) { + throw new ManagedAgentException(400, + "unsupported_input", + "Expected non-empty text, a text file or an image."); + } + // Code points, matching the published schema's maxLength; a + // String.length() count would disagree on astral-plane text. + int blockChars = block.text().codePointCount(0, + block.text().length()); + if (blockChars > MAX_BLOCK_INPUT_CHARS) { + throw new ManagedAgentException(400, + "invalid_input", + "Input block exceeds the " + MAX_BLOCK_INPUT_CHARS + + " character per-block limit."); + } + totalChars += blockChars; + if (totalChars > MAX_AGGREGATE_INPUT_CHARS) { + throw new ManagedAgentException(400, + "invalid_input", + "Input exceeds the " + MAX_AGGREGATE_INPUT_CHARS + + " character aggregate limit."); + } + result.add(Map.of("type", "text", "text", + replaceLoneSurrogates(block.text()))); + } + var normalized = normalizeInput(result); + if (normalized.stream().anyMatch(block -> !"text".equals(block.get("type"))) + && digests.byteLength(normalized) > ManagedInputAttachments.MAX_INPUT_BYTES) { + throw new ManagedAgentException(413, "input_too_large", + "An input containing attachments must fit within 8 MiB of encoded JSON."); + } + return normalized; + } + + @SuppressWarnings("unchecked") + static List> normalizeInput( + List> input) { + List> result = new ArrayList<>(input.size()); + for (Map block : input) { + result.add((Map) normalizeValue(block)); + } + return List.copyOf(result); + } + + static Object normalizeValue(Object value) { + if (value instanceof String text) { + return replaceLoneSurrogates(text); + } + if (value instanceof Map map) { + Map copy = new LinkedHashMap<>(map.size()); + for (Map.Entry entry : map.entrySet()) { + copy.put(replaceLoneSurrogates((String) entry.getKey()), + normalizeValue(entry.getValue())); + } + return copy; + } + if (value instanceof List list) { + List copy = new ArrayList<>(list.size()); + for (Object item : list) { + copy.add(normalizeValue(item)); + } + return Collections.unmodifiableList(copy); + } + return value; + } + + static String replaceLoneSurrogates(String text) { + StringBuilder out = null; + int written = 0; + for (int i = 0; i < text.length(); i++) { + char current = text.charAt(i); + if (Character.isHighSurrogate(current)) { + if (i + 1 < text.length() + && Character.isLowSurrogate(text.charAt(i + 1))) { + i++; // a well-formed pair passes through unchanged + continue; + } + } else if (!Character.isLowSurrogate(current)) { + continue; + } + if (out == null) { + out = new StringBuilder(text.length()); + } + out.append(text, written, i).append('�'); + written = i + 1; + } + if (out == null) { + return text; + } + out.append(text, written, text.length()); + return out.toString(); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/OperationReconciler.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/OperationReconciler.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/OperationReconciler.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/OperationReconciler.java index 7c997ff4d8d..5452be98331 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/OperationReconciler.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/OperationReconciler.java @@ -1,5 +1,7 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.session.AcceptanceEvidence; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.AcceptanceVerdict; import com.alibaba.qwen.code.managedagent.model.StoreModels.ParkedOperation; import org.slf4j.Logger; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptions.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptions.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptions.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptions.java index b60343ebbb2..a259feaa89d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptions.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptions.java @@ -1,9 +1,9 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.AttachedSession; -import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore; -import org.apache.rocketmq.client.apis.consumer.LitePushConsumer; +import com.alibaba.qwen.code.managedagent.contracts.event.EventSubscriptions; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.AttachedSession; +import com.alibaba.qwen.code.managedagent.repository.TransportConsumerStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,7 +41,7 @@ public class OwnerSubscriptions { private final HarnessConnector harness; private final TransportConsumerStore store; - private volatile LitePushConsumer consumer; + private volatile EventSubscriptions consumer; public OwnerSubscriptions(HarnessConnector harness, TransportConsumerStore store) { @@ -50,13 +50,13 @@ public class OwnerSubscriptions { } /** Binds the owner consumer, which is built after its listener. */ - public void bind(LitePushConsumer consumer) { + public void bind(EventSubscriptions consumer) { this.consumer = consumer; } /** Subscribes at once to a Session this node just activated. */ public void subscribe(String sessionId) { - LitePushConsumer current = consumer; + EventSubscriptions current = consumer; if (current == null) { return; } @@ -77,7 +77,7 @@ public class OwnerSubscriptions { * held set changes nothing. */ public void reconcile() { - LitePushConsumer current = consumer; + EventSubscriptions current = consumer; if (current == null) { return; } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/RenameEvidence.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/RenameEvidence.java similarity index 85% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/RenameEvidence.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/RenameEvidence.java index 63c1b435d26..2b725b0e0cc 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/RenameEvidence.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/RenameEvidence.java @@ -1,8 +1,10 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.session.AcceptanceEvidence; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.AcceptanceVerdict; import com.alibaba.qwen.code.managedagent.model.StoreModels.ParkedOperation; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract; import org.springframework.stereotype.Component; import java.util.Optional; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancer.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancer.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancer.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancer.java index f4fdab2dd62..a2f3b739b85 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancer.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancer.java @@ -1,7 +1,8 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.ReplayFloorTarget; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; @@ -18,10 +19,10 @@ public class ReplayFloorAdvancer { ReplayFloorAdvancer.class); private static final int TARGET_LIMIT = 64; private final AgentStateStore store; - private final ManagedAgentProperties properties; + private final ManagedAgentSettings properties; public ReplayFloorAdvancer(AgentStateStore store, - ManagedAgentProperties properties) { + ManagedAgentSettings properties) { this.store = store; this.properties = properties; } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionEventHub.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionEventHub.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionEventHub.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionEventHub.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinator.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinator.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinator.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinator.java index 1d07cc54928..515251841ff 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinator.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinator.java @@ -1,25 +1,25 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.daemon.DaemonProtocolException; -import com.alibaba.qwen.code.daemon.HostedHarnessCapabilityMismatchException; -import com.alibaba.qwen.code.daemon.HostedHarnessGenerationException; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.CwdChangeOutcome; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessCapabilityFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessGenerationFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessProtocolFailure; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationTarget; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore.CwdChangeOutcome; +import com.alibaba.qwen.code.managedagent.repository.ChildResultRelayStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.managedagent.workspace.ChildLifecycleAdmissions; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerOperations; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; @@ -104,10 +104,10 @@ public class SessionLifecycleCoordinator { private static final Set SELF_CLEARING_DAEMON_REFUSALS = Set.of("hosted_lifecycle_recovery_required", "hosted_turn_active"); private final AgentStateStore store; - private com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore lifecycle; + private com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore lifecycle; @org.springframework.beans.factory.annotation.Autowired(required = false) - public void setWorkspaceLifecycleStore(com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore lifecycle) { + public void setWorkspaceLifecycleStore(com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore lifecycle) { this.lifecycle = lifecycle; } @@ -131,8 +131,8 @@ public class SessionLifecycleCoordinator { private final ObjectMapper objectMapper; private final ChildLifecycleAdmissions childCloses; private final ManagedAgentService sessions; - private final com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore childWorkspaces; - private final ObjectProvider brokerProviders; + private final com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore childWorkspaces; + private final ObjectProvider brokerProviders; private final ExecutorService executor; private final Clock clock; private final Duration leaseDuration; @@ -162,9 +162,9 @@ public class SessionLifecycleCoordinator { ObjectMapper objectMapper, ChildLifecycleAdmissions childCloses, ManagedAgentService sessions, - ObjectProvider brokerProviders, + ObjectProvider brokerProviders, ExecutorService executor, - Clock clock, ManagedAgentProperties properties) { + Clock clock, ManagedAgentSettings properties) { this(store, sessionStore, harness, runtimeWarmer, childScopes, objectMapper, childCloses, sessions, brokerProviders, executor, clock, properties, null); @@ -177,10 +177,10 @@ public class SessionLifecycleCoordinator { @Qualifier("managedAgentObjectMapper") ObjectMapper objectMapper, ChildLifecycleAdmissions childCloses, ManagedAgentService sessions, - ObjectProvider brokerProviders, + ObjectProvider brokerProviders, @Qualifier("managedAgentExecutor") ExecutorService executor, - @Qualifier("managedAgentClock") Clock clock, ManagedAgentProperties properties, - com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore childWorkspaces) { + @Qualifier("managedAgentClock") Clock clock, ManagedAgentSettings properties, + com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore childWorkspaces) { this.childWorkspaces = childWorkspaces; this.store = store; this.sessionStore = sessionStore; @@ -298,9 +298,9 @@ public class SessionLifecycleCoordinator { String blocked = null; String daemonWaitCode = null; String failureCode = "session_lifecycle_delivery_failed"; - if (cause instanceof HostedHarnessCapabilityMismatchException mismatch) { + if (cause instanceof HarnessCapabilityFailure mismatch) { failureCode = mismatch.getCode(); - } else if (cause instanceof DaemonProtocolException) { + } else if (cause instanceof HarnessProtocolFailure) { failureCode = "hosted_harness_protocol_error"; } else if (cause instanceof com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException brokerError) { failureCode = brokerError.getCode(); @@ -317,15 +317,15 @@ public class SessionLifecycleCoordinator { && (apiError.getCode().startsWith("workspace_lifecycle") || apiError.getCode().startsWith("workspace_close"))) { blocked = apiError.getCode(); } - if ((cause instanceof DaemonHttpException - || cause instanceof com.alibaba.qwen.code.daemon.MutationOutcomeUnknownException) + if ((cause instanceof HarnessHttpFailure + || cause instanceof com.alibaba.qwen.code.managedagent.harness.contracts.HarnessMutationUnknownFailure) && operationIsLifecycle(claimed)) { String refusal = daemonRefusalCode(cause); if (refusal != null && SELF_CLEARING_DAEMON_REFUSALS.contains(refusal)) { daemonWaitCode = refusal; } else { - blocked = cause instanceof DaemonHttpException httpError + blocked = cause instanceof HarnessHttpFailure httpError && httpError.getErrorCode() != null && (httpError.getErrorCode().startsWith("workspace_lifecycle") || httpError.getErrorCode().startsWith("workspace_close") || "runtime_admission_closed".equals(httpError.getErrorCode())) @@ -336,7 +336,7 @@ public class SessionLifecycleCoordinator { && HarnessCoordinator.isPoolWait(wait)) { blocked = null; daemonWaitCode = wait instanceof com.alibaba.qwen.code.managedagent.exception.ManagedAgentException apiError - ? apiError.getCode() : ((DaemonHttpException) wait).getErrorCode(); + ? apiError.getCode() : ((HarnessHttpFailure) wait).getErrorCode(); } if (blocked != null) { failureCode = blocked; @@ -376,7 +376,7 @@ public class SessionLifecycleCoordinator { boolean writerLive = !cwdChange && writerStillLive(claimed); boolean exemptWait = writerLive && retryable(cause); boolean staleBoot = - cause instanceof HostedHarnessGenerationException; + cause instanceof HarnessGenerationFailure; // The budget bounds every settle outcome that could have made // progress, blocked or not, and the terminal record keeps the // cause instead of certifying a completion that never settled. @@ -578,12 +578,12 @@ public class SessionLifecycleCoordinator { // on a fresh writerStillLive(claimed) check regardless. private static boolean retryable(Throwable cause) { if (cause instanceof com.alibaba.qwen.code.managedagent.exception.ManagedAgentException apiError - && apiError.getStatus().is4xxClientError()) { + && (apiError.getStatus() >= 400 && apiError.getStatus() < 500)) { return "workspace_lifecycle_writer_active".equals(apiError.getCode()) || "managed_session_writer_active".equals(apiError.getCode()); } - if (cause instanceof HostedHarnessCapabilityMismatchException - || cause instanceof DaemonProtocolException) { + if (cause instanceof HarnessCapabilityFailure + || cause instanceof HarnessProtocolFailure) { return false; } return !(cause instanceof RuntimeBrokerException brokerError) @@ -622,7 +622,7 @@ public class SessionLifecycleCoordinator { } private static boolean operationIsLifecycle(OperationRecord operation) { - return com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles.lifecycleProtocol(operation.lifecycleProtocolVersion()); + return com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles.lifecycleProtocol(operation.lifecycleProtocolVersion()); } // The SDK wraps a daemon refusal in MutationOutcomeUnknownException once @@ -631,7 +631,7 @@ public class SessionLifecycleCoordinator { private static String daemonRefusalCode(Throwable failure) { for (Throwable cause = failure; cause != null; cause = cause.getCause()) { - if (cause instanceof DaemonHttpException httpError) { + if (cause instanceof HarnessHttpFailure httpError) { return httpError.getErrorCode(); } } @@ -648,7 +648,7 @@ public class SessionLifecycleCoordinator { var session = store.requireSession(tenantId, sessionId); try { if (session.workspace() == null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } runtimeWarmer.verifyWorkspaceCwdTarget(session.workspace(), operation.targetCwdRelative()); @@ -713,7 +713,7 @@ public class SessionLifecycleCoordinator { private boolean settle(OperationRecord operation, java.util.concurrent.atomic.AtomicBoolean harnessSettled) { if (operation.lifecycleProtocolVersion() < 0 || operation.lifecycleProtocolVersion() > 2) { - throw com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore.blocked("workspace_lifecycle_protocol_unavailable"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_protocol_unavailable"); } boolean harnessConfirmed = false; var session = store.requireSession(operation.tenantId(), operation.sessionId()); @@ -727,22 +727,22 @@ public class SessionLifecycleCoordinator { } if (bound && operationIsLifecycle(operation)) { if (lifecycle == null || !RuntimeWarmer.supportsClose(runtimeWarmer, session.workspace())) { - throw com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore.blocked("workspace_close_identity_unverified"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_close_identity_unverified"); } lifecycle.requireSettledExecutions(operation); JsonNode effects = lifecycle.recoverEffects(operation); if (effects == null) { if (!harness.supportsLifecycle(session.toolProfile())) { - throw com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore.blocked("workspace_lifecycle_protocol_unavailable"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_protocol_unavailable"); } try { effects = harness.settleLifecycle(operation); - } catch (com.alibaba.qwen.code.daemon.HostedHarnessSettleAnsweredException answered) { + } catch (com.alibaba.qwen.code.managedagent.harness.contracts.HarnessSettleAnsweredFailure answered) { // The Harness answered the settle but Java could not // process the receipt — the latch still sets: a later // failure of this attempt is not "the settle never // happened" (review round 11, R7-2). A pre-send - // DaemonProtocolException (e.g. the protocol-1 refusal) + // HarnessProtocolFailure (e.g. the protocol-1 refusal) // never carries the marker and stays unlatched. harnessSettled.set(true); throw answered; @@ -770,8 +770,8 @@ public class SessionLifecycleCoordinator { String profile = session.toolProfile(); try { harness.prepareLifecycleResources(operation); - if (com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles.SHELL.equals(profile)) { - var authority = com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore.authority(operation); + if (com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles.SHELL.equals(profile)) { + var authority = com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation); if (store.hasMcpServers(session)) { runtimeWarmer.prepareWorkspaceResources(operation.tenantId(), operation.sessionId(), authority) .toCompletableFuture().join(); @@ -782,28 +782,28 @@ public class SessionLifecycleCoordinator { } harness.settleLifecycleResources(operation); runtimeWarmer.drainWorkspaceResources(operation.tenantId(), operation.sessionId(), - com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore.authority(operation)) + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation)) .toCompletableFuture().join(); } catch (RuntimeException unsettled) { - if (!(unsettled instanceof com.alibaba.qwen.code.daemon.DaemonException) - || unsettled instanceof com.alibaba.qwen.code.daemon.HostedHarnessGenerationException) { + if (!(unsettled instanceof com.alibaba.qwen.code.managedagent.harness.contracts.HarnessFailure) + || unsettled instanceof com.alibaba.qwen.code.managedagent.harness.contracts.HarnessGenerationFailure) { throw unsettled; } - if (unsettled instanceof com.alibaba.qwen.code.daemon.DaemonHttpException http + if (unsettled instanceof com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure http && (http.getErrorCode() != null && (http.getErrorCode().startsWith("workspace_lifecycle_claim") || http.getErrorCode().startsWith("workspace_lifecycle_authorization") || http.getErrorCode().startsWith("workspace_close") - && !(com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles.MCP.equals(profile) + && !(com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles.MCP.equals(profile) && "workspace_close_identity_unverified".equals(http.getErrorCode())) || "runtime_admission_closed".equals(http.getErrorCode())))) { throw unsettled; } - if (com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles.MCP.equals(profile)) { + if (com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles.MCP.equals(profile)) { runtimeWarmer.recoverStoppedWorkspaceResources(operation.tenantId(), operation.sessionId(), - com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore.authority(operation)) + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation)) .toCompletableFuture().join(); } else { - throw com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore.blocked("workspace_lifecycle_resources_unsettled"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_resources_unsettled"); } } } @@ -813,7 +813,7 @@ public class SessionLifecycleCoordinator { } harness.detachLifecycle(operation); if (sessionStore.hasLiveWriter(operation.tenantId(), operation.sessionId())) { - throw com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore.blocked("workspace_lifecycle_writer_active"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_writer_active"); } runtimeWarmer.closeWorkspace(operation.tenantId(), operation.sessionId()).toCompletableFuture().join(); return true; @@ -883,7 +883,7 @@ public class SessionLifecycleCoordinator { return true; } childWorkspaces.requestFinish(tenantId, sessionId, childRunId, - com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.DISCARD, + com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.DISCARD, clock.millis()); return true; } catch (com.alibaba.qwen.code.managedagent.exception.ManagedAgentException refused) { @@ -994,7 +994,7 @@ public class SessionLifecycleCoordinator { // with none of the three the never-started pairing is // the honest one. The SDK's refusal and transport- // ambiguous throws are plain RuntimeExceptions - // (DaemonHttpException, + // (HarnessHttpFailure, // MutationOutcomeUnknownException): a journal-side // failure owes its revision, but must never skip the // child's own physical close behind it. @@ -1094,7 +1094,7 @@ public class SessionLifecycleCoordinator { try { harness.runMessageOperation(tenantId, childSessionId, stop); - } catch (DaemonHttpException error) { + } catch (HarnessHttpFailure error) { // A child waiting on its recovery or // closing takes no stop yet: the retry // asks again, as on the relay's arm. @@ -1300,7 +1300,7 @@ public class SessionLifecycleCoordinator { * the relay answers READY-only. */ private RuntimeBindingRecord findBinding(String tenantId, String sessionId) { - RuntimeBrokerService broker = brokerProviders == null ? null + RuntimeBrokerOperations broker = brokerProviders == null ? null : brokerProviders.getIfAvailable(); return broker == null ? null : broker.findLatestBindingByHarnessSessionAnyState(tenantId, diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleService.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleService.java index a7d77248439..a735bc0eb45 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleService.java @@ -1,5 +1,8 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicCommandOperation; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicCwdOperation; @@ -10,10 +13,7 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.util.Locale; @@ -183,13 +183,13 @@ public class SessionLifecycleService { private OperationRecord operation(String tenantId, String actorId, String sessionId, String operationId) { if (operationId.length() > 64) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", + throw new ManagedAgentException(400, "invalid_request", "The operation id is invalid."); } SessionRecord session = store.requireSession(tenantId, sessionId); sessions.requireReadGrant(session, actorId); return store.findOperation(tenantId, sessionId, operationId) - .orElseThrow(() -> new ManagedAgentException(HttpStatus.NOT_FOUND, + .orElseThrow(() -> new ManagedAgentException(404, "operation_not_found", "The operation was not found.")); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelay.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelay.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelay.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelay.java index c8a701c98ad..b3e5c71b055 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelay.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelay.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords; -import com.alibaba.qwen.code.managedagent.persistence.SessionMessageRelayStore; -import com.alibaba.qwen.code.managedagent.persistence.SessionMessageRelayStore.MessageRow; -import com.alibaba.qwen.code.managedagent.persistence.SessionMessageRelayStore.PendingMessage; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.repository.ChildResultRelayStore; +import com.alibaba.qwen.code.managedagent.repository.SessionMessageRelayStore; +import com.alibaba.qwen.code.managedagent.repository.SessionMessageRelayStore.MessageRow; +import com.alibaba.qwen.code.managedagent.repository.SessionMessageRelayStore.PendingMessage; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; @@ -378,7 +378,7 @@ public class SessionMessageRelay { body.required("contentDigest").asText()); try { harness.runMessageOperation(row.tenantId(), target, receive); - } catch (DaemonHttpException error) { + } catch (HarnessHttpFailure error) { if ("session_message_not_ready".equals(error.getErrorCode())) { // A parent takes its child's message only once that // child's run attached: held, never a failure. @@ -444,7 +444,7 @@ public class SessionMessageRelay { consume.put("kind", "consume"); try { harness.runMessageOperation(row.tenantId(), target, consume); - } catch (DaemonHttpException error) { + } catch (HarnessHttpFailure error) { if ("session_message_not_ready".equals(error.getErrorCode())) { store.scheduleRetry(row, owner, now + AWAIT_MS, now + LEASE_MS, now); @@ -580,7 +580,7 @@ public class SessionMessageRelay { try { settled = harness.runMessageOperation(row.tenantId(), target, execute); - } catch (DaemonHttpException error) { + } catch (HarnessHttpFailure error) { if ("session_command_refused".equals(error.getErrorCode())) { // The lead cannot accept the command at all: rejected, and // never re-executed. diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryService.java similarity index 84% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryService.java index 8fd65060443..0ee73bcc532 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryService.java @@ -1,17 +1,17 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.RecoveryStatus; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.RecoveryStatus; import com.alibaba.qwen.code.managedagent.model.ApiModels.SessionRecovery; import com.alibaba.qwen.code.managedagent.model.ApiModels.SessionRecoveryActions; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnSummary; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; -import org.springframework.jdbc.core.JdbcTemplate; +import com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore; +import com.alibaba.qwen.code.managedagent.repository.SessionRecoveryRepository; +import com.alibaba.qwen.code.managedagent.repository.SessionRecoveryRepository.HeadRecovery; import org.springframework.stereotype.Service; import java.util.List; @@ -27,16 +27,16 @@ public class SessionRecoveryService { private final AgentStateStore store; private final HarnessCoordinator coordinator; private final HarnessConnector harness; - private final JdbcTemplate jdbc; + private final SessionRecoveryRepository recovery; private final ManagedSessionStore journals; public SessionRecoveryService(ManagedAgentService sessions, AgentStateStore store, - HarnessCoordinator coordinator, HarnessConnector harness, @Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, ManagedSessionStore journals) { + HarnessCoordinator coordinator, HarnessConnector harness, SessionRecoveryRepository recovery, ManagedSessionStore journals) { this.sessions = sessions; this.store = store; this.coordinator = coordinator; this.harness = harness; - this.jdbc = jdbc; + this.recovery = recovery; this.journals = journals; } @@ -56,7 +56,7 @@ public class SessionRecoveryService { sessions.requireRecoveryOperator(session, actorId); TurnRecord turn = latest(session); if (turn == null || !turn.turnId().equals(turnId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "recovery_target_changed", "The Session's recovery Turn has changed."); } SessionRecovery recovery = targetRecovery(session, actorId, turnId); @@ -64,7 +64,7 @@ public class SessionRecoveryService { return recovery; } if (!recovery.actions().recheck()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "recovery_recheck_unavailable", "This recovery has no existing work to recheck."); } boolean rechecked; @@ -85,7 +85,7 @@ public class SessionRecoveryService { } recovery = targetRecovery(session, actorId, turnId); if (!rechecked && !"ready".equals(recovery.state())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "recovery_recheck_unavailable", "The original recovery work is not available."); } return recovery; @@ -94,7 +94,7 @@ public class SessionRecoveryService { private SessionRecovery targetRecovery(SessionRecord session, String actorId, String turnId) { SessionRecovery recovery = query(session.tenantId(), actorId, session.sessionId()); if (!turnId.equals(recovery.turnId())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "recovery_target_changed", "The Session's recovery Turn has changed."); } return recovery; @@ -105,12 +105,7 @@ public class SessionRecoveryService { boolean operator = operator(session, actorId); boolean active = turn != null && ACTIVE.contains(turn.status()); boolean cancel = operator && active && !"CANCELLING".equals(turn.status()); - List heads = jdbc.query("SELECT recovery_status, recovery_detail_code" - + " FROM ma_session_head WHERE tenant_id = ? AND session_id = ?" - + " AND head_kind = 'MANAGED_SESSION'", - (row, index) -> new HeadRecovery(row.getString("recovery_status"), - row.getString("recovery_detail_code")), - session.tenantId(), session.sessionId()); + List heads = recovery.heads(session.tenantId(), session.sessionId()); if (!"ACTIVE".equals(session.status())) { return view("operator_required", "session_not_active", turn, false, false, false); } @@ -123,10 +118,7 @@ public class SessionRecoveryService { turn, false, false, cancel); } boolean initial = turn == null || "CANCELLED".equals(turn.status()) && !turn.submissionAttempted() - && turn.harnessEventEpoch() == null && jdbc.queryForObject( - "SELECT COUNT(*) FROM ma_turn WHERE tenant_id = ? AND session_id = ?" - + " AND (status <> 'CANCELLED' OR submission_attempted = TRUE OR harness_event_epoch IS NOT NULL)", - Long.class, session.tenantId(), session.sessionId()) == 0; + && turn.harnessEventEpoch() == null && recovery.nonInitialTurns(session.tenantId(), session.sessionId()) == 0; if (initial && (heads.isEmpty() || journals.hasPristineInitialJournal(session.tenantId(), session.sessionId(), session.workspace() == null ? null : session.workspace().getWorkspaceId()))) { return view("ready", "ready", turn, true, false, false); @@ -180,7 +172,7 @@ public class SessionRecoveryService { List.of(session.sessionId())).get(session.sessionId()); return latest == null ? null : store.findTurn(session.tenantId(), session.sessionId(), latest.turnId()).orElseThrow(() -> - new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, + new ManagedAgentException(503, "recovery_evidence_unavailable", "The recovery evidence is unavailable.")); } @@ -212,7 +204,4 @@ public class SessionRecoveryService { nextTurnAllowed, new SessionRecoveryActions(recheck, cancel, !nextTurnAllowed), recheck && turn != null && ACTIVE.contains(turn.status()) ? turn.retryAfter() : null); } - - private record HeadRecovery(String state, String reasonCode) { - } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRenameCoordinator.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRenameCoordinator.java similarity index 92% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRenameCoordinator.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRenameCoordinator.java index a59ecb17d4e..3c845b33127 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRenameCoordinator.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/SessionRenameCoordinator.java @@ -1,8 +1,9 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.RenameDelivery; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; @@ -23,12 +24,12 @@ public class SessionRenameCoordinator { private final HarnessConnector harness; private final ExecutorService executor; private final Clock clock; - private final ManagedAgentProperties.Dispatch settings; + private final ManagedAgentSettings.Dispatch settings; private final String owner = UUID.randomUUID().toString(); private final Set> active = ConcurrentHashMap.newKeySet(); public SessionRenameCoordinator(AgentStateStore store, HarnessConnector harness, - @Qualifier("managedAgentExecutor") ExecutorService executor, @Qualifier("managedAgentClock") Clock clock, ManagedAgentProperties properties) { + @Qualifier("managedAgentExecutor") ExecutorService executor, @Qualifier("managedAgentClock") Clock clock, ManagedAgentSettings properties) { this.store = store; this.harness = harness; this.executor = executor; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinator.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinator.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinator.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinator.java index 252a0fc97c6..8a039614341 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinator.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinator.java @@ -1,18 +1,20 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.session.AcceptanceEvidence; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.TaskCancelOutcome; import com.alibaba.qwen.code.managedagent.model.StoreModels.AcceptanceVerdict; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationTarget; import com.alibaba.qwen.code.managedagent.model.StoreModels.ParkedOperation; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore.TaskTarget; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore.TaskCancelOutcome; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.TaskTarget; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerOperations; import jakarta.annotation.PreDestroy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -110,10 +112,10 @@ public class TaskCancelCoordinator implements AcceptanceEvidence { private final AgentStateStore store; private final ManagedExtensionRecordStore records; private final HarnessConnector harness; - private final RuntimeBrokerService broker; + private final RuntimeBrokerOperations broker; private final ExecutorService executor; private final Clock clock; - private final ManagedAgentProperties.Dispatch dispatch; + private final ManagedAgentSettings.Dispatch dispatch; private final Duration attemptDeadline; private final String owner = UUID.randomUUID().toString(); private final Set active = ConcurrentHashMap.newKeySet(); @@ -132,9 +134,9 @@ public class TaskCancelCoordinator implements AcceptanceEvidence { @Autowired public TaskCancelCoordinator(AgentStateStore store, ManagedExtensionRecordStore records, HarnessConnector harness, - ObjectProvider broker, + ObjectProvider broker, @Qualifier("managedAgentExecutor") ExecutorService executor, @Qualifier("managedAgentClock") Clock clock, - ManagedAgentProperties properties) { + ManagedAgentSettings properties) { this(store, records, harness, broker.getIfAvailable(), executor, clock, properties, deadlineFor(properties.getHarness().getRequestTimeout())); @@ -156,8 +158,8 @@ public class TaskCancelCoordinator implements AcceptanceEvidence { TaskCancelCoordinator(AgentStateStore store, ManagedExtensionRecordStore records, HarnessConnector harness, - RuntimeBrokerService broker, ExecutorService executor, - Clock clock, ManagedAgentProperties properties, + RuntimeBrokerOperations broker, ExecutorService executor, + Clock clock, ManagedAgentSettings properties, Duration attemptDeadline) { this.store = store; this.records = records; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/TurnInputEvidence.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/TurnInputEvidence.java similarity index 90% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/TurnInputEvidence.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/TurnInputEvidence.java index cf38e7dcdd8..4a35afe1b7d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/TurnInputEvidence.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/TurnInputEvidence.java @@ -1,5 +1,7 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.session.AcceptanceEvidence; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.AcceptanceVerdict; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/UserMessageDispatcher.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/UserMessageDispatcher.java similarity index 82% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/UserMessageDispatcher.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/UserMessageDispatcher.java index f29904a13b5..dacf9e8e135 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/UserMessageDispatcher.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/UserMessageDispatcher.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.session; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedUserMessageStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedUserMessageStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; @@ -11,15 +11,19 @@ import org.springframework.stereotype.Component; public class UserMessageDispatcher { private static final Logger LOG = LoggerFactory.getLogger(UserMessageDispatcher.class); private final ManagedUserMessageStore messages; + private final ManagedAgentService sessions; - public UserMessageDispatcher(ManagedUserMessageStore messages) { this.messages = messages; } + public UserMessageDispatcher(ManagedUserMessageStore messages, ManagedAgentService sessions) { + this.messages = messages; + this.sessions = sessions; + } @Scheduled(scheduler = "managedAgentScheduler", fixedDelay = 1000) public void dispatch() { messages.retireClosed(); for (var session : messages.readySessions()) { try { - messages.startNext(session.get("tenant"), session.get("session")); + messages.startNext(session.get("tenant"), session.get("session"), sessions::submitTurn); } catch (ManagedAgentException unavailable) { LOG.debug("User message dispatch deferred for Session {}", session.get("session"), unavailable); } catch (RuntimeException failure) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/WakeRecovery.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/WakeRecovery.java similarity index 86% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/WakeRecovery.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/WakeRecovery.java index 50caa5a8c9d..06e970136e6 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/WakeRecovery.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/session/WakeRecovery.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords; -import com.alibaba.qwen.code.managedagent.persistence.WakeRecoveryStore; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.repository.ChildResultRelayStore; +import com.alibaba.qwen.code.managedagent.repository.WakeRecoveryStore; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,7 +23,7 @@ public class WakeRecovery { private final String defaultWorkspace; public WakeRecovery(WakeRecoveryStore store, HarnessConnector harness, - ChildResultRelayStore records, ManagedAgentProperties properties) { + ChildResultRelayStore records, ManagedAgentSettings properties) { this.store = store; this.harness = harness; this.records = records; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildLifecycleAdmissions.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildLifecycleAdmissions.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildLifecycleAdmissions.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildLifecycleAdmissions.java index 6bca16c247a..44e3f6ac911 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildLifecycleAdmissions.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildLifecycleAdmissions.java @@ -1,9 +1,9 @@ package com.alibaba.qwen.code.managedagent.workspace; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import org.springframework.stereotype.Component; import java.util.Map; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelay.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelay.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelay.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelay.java index efdbe17d78d..31a7a3d9a05 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelay.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelay.java @@ -1,18 +1,19 @@ package com.alibaba.qwen.code.managedagent.workspace; -import com.alibaba.qwen.code.daemon.DaemonHttpException; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorktreeGit; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; import com.alibaba.qwen.code.managedagent.model.ApiModels.CommandAdmission; -import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore; -import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore.PendingChild; -import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore.RelayRow; -import com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.repository.ChildResultRelayStore; +import com.alibaba.qwen.code.managedagent.repository.ChildResultRelayStore.PendingChild; +import com.alibaba.qwen.code.managedagent.repository.ChildResultRelayStore.RelayRow; +import com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerOperations; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -81,7 +82,7 @@ public class ChildResultRelay { private final ChildResultRelayStore relayStore; private final ManagedAgentService sessions; - private final RuntimeBrokerService broker; + private final RuntimeBrokerOperations broker; private final HarnessConnector harness; private final ObjectMapper mapper; private final ChildLifecycleAdmissions childCloses; @@ -92,7 +93,7 @@ public class ChildResultRelay { @org.springframework.beans.factory.annotation.Autowired public ChildResultRelay(ChildResultRelayStore relayStore, ManagedAgentService sessions, - org.springframework.beans.factory.ObjectProvider broker, + org.springframework.beans.factory.ObjectProvider broker, HarnessConnector harness, @Qualifier("managedAgentObjectMapper") ObjectMapper mapper, ChildLifecycleAdmissions childCloses, ChildWorkspaceService childWorkspaces) { @@ -102,7 +103,7 @@ public class ChildResultRelay { ChildResultRelay(ChildResultRelayStore relayStore, ManagedAgentService sessions, - org.springframework.beans.factory.ObjectProvider broker, + org.springframework.beans.factory.ObjectProvider broker, HarnessConnector harness, ObjectMapper mapper, ChildLifecycleAdmissions childCloses, ChildWorkspaceService childWorkspaces, @@ -375,7 +376,7 @@ public class ChildResultRelay { // inputs stopped before the Turn cancel's load can start one. try { harness.runMessageOperation(row.tenantId(), child, stop); - } catch (DaemonHttpException error) { + } catch (HarnessHttpFailure error) { // A child that waits on its recovery or is closing takes // no stop yet: the heartbeat asks again, spending no // attempt, and the bound above ends the wait. @@ -972,7 +973,7 @@ public class ChildResultRelay { try { settleFrom(row, pending, status, turnId, completedAt, journalTurn, messageCount, "COMPLETED".equals(turn.status()), now); - } catch (DaemonHttpException error) { + } catch (HarnessHttpFailure error) { if (!"child_messages_pending".equals(error.getErrorCode())) { throw error; } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceService.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceService.java index 984fd0c13e6..6805895e760 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceService.java @@ -1,12 +1,15 @@ package com.alibaba.qwen.code.managedagent.workspace; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorkspaceException; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorkspaceProvider; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorktreeGit; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore; -import com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.Row; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore; +import com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.Row; +import com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; @@ -16,7 +19,6 @@ import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -35,21 +37,21 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.Supplier; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.APPLIED; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.APPLYING; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.BLOCKED; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.CONFLICTED; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.DISCARD; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.DISCARDED; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.DISCARDING; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.DISCARD_PINNED; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.FAILED; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.MERGE; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.MERGED; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.MERGING; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.PREPARING; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.READY; -import static com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.columns; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.APPLIED; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.APPLYING; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.BLOCKED; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.CONFLICTED; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.DISCARD; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.DISCARDED; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.DISCARDING; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.DISCARD_PINNED; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.FAILED; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.MERGE; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.MERGED; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.MERGING; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.PREPARING; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.READY; +import static com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.columns; /** * The child Workspace of the isolation slice (#13753 I1): prepares a Git @@ -125,12 +127,12 @@ public class ChildWorkspaceService { */ public Row prepare(String tenantId, String parentSessionId, String childRunId) { if (warmer.childWorkspaces() == null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_workspace_unsupported", + throw new ManagedAgentException(409, "child_workspace_unsupported", "This host cannot create a child Workspace."); } SessionRecord parent = sessions.requireSession(tenantId, parentSessionId); if (parent.workspace() == null || !"ACTIVE".equals(parent.status())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_parent_unavailable", + throw new ManagedAgentException(409, "child_parent_unavailable", "The parent Session cannot admit a child."); } return drive(store.admit(tenantId, parentSessionId, childRunId, parent.workspace(), @@ -147,12 +149,12 @@ public class ChildWorkspaceService { public Row request(String tenantId, String parentSessionId, String childRunId, String workspaceMode) { if (warmer.childWorkspaces() == null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_workspace_unsupported", + throw new ManagedAgentException(409, "child_workspace_unsupported", "This host cannot create a child Workspace."); } SessionRecord parent = sessions.requireSession(tenantId, parentSessionId); if (parent.workspace() == null || !"ACTIVE".equals(parent.status())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_parent_unavailable", + throw new ManagedAgentException(409, "child_parent_unavailable", "The parent Session cannot admit a child."); } return store.admit(tenantId, parentSessionId, childRunId, parent.workspace(), workspaceMode, @@ -520,7 +522,7 @@ public class ChildWorkspaceService { String execution = leases.holdForMaintenance(storage, claimed.childWorkspaceId(), claimed.claimGeneration()); boolean stopped = true; try { - return ChildWorktreeGit.whileLive(live::get, () -> work.apply(root)); + return provider.git().runWhileLive(live::get, () -> work.apply(root)); } catch (ChildWorkspaceException error) { stopped = !ChildWorkspaceException.STOP_UNVERIFIED.equals(error.code()); throw error; @@ -536,7 +538,7 @@ public class ChildWorkspaceService { private void advance(Row claimed, String toState, java.util.Map columns) { if (!store.transition(claimed, owner, toState, columns, clock.get())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_workspace_claim_lost", + throw new ManagedAgentException(409, "child_workspace_claim_lost", "The child Workspace moved on under another claim."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeService.java similarity index 81% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeService.java index bebc2eff5d8..dcfc7c66108 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeService.java @@ -1,14 +1,18 @@ package com.alibaba.qwen.code.managedagent.workspace; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorkspaceException; +import com.alibaba.qwen.code.managedagent.contracts.workspace.SessionWorktreeOperations; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore; -import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore.Descriptor; -import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore.Plan; -import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore.Row; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore; +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Descriptor; +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Plan; +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Row; +import com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore; import com.alibaba.qwen.code.managedagent.session.HarnessCoordinator; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.fasterxml.jackson.databind.JsonNode; import jakarta.annotation.PreDestroy; import org.springframework.scheduling.annotation.Scheduled; @@ -27,8 +31,8 @@ public class SessionWorktreeService { private static final long LEASE_MS = 120_000; private final SessionWorktreeStore store; private final WorkspaceExecutionStore execution; - private final ManagedAgentProperties properties; - private final ManagedAgentStore sessions; + private final ManagedAgentSettings properties; + private final AgentStateStore sessions; private final HarnessCoordinator coordinator; private final String owner = "session-worktree-" + UUID.randomUUID(); private final ScheduledExecutorService renewals = Executors.newSingleThreadScheduledExecutor(task -> { @@ -36,10 +40,18 @@ public class SessionWorktreeService { thread.setDaemon(true); return thread; }); - private SessionWorktreeGit git; + private SessionWorktreeOperations git; + private SessionWorktreeOperations.Factory gitFactory; + private WorktreeFiles worktreeFiles; + + @org.springframework.beans.factory.annotation.Autowired + public void configureWorktreeFiles(WorktreeFiles files, SessionWorktreeOperations.Factory factory) { + worktreeFiles = files; + gitFactory = factory; + } public SessionWorktreeService(SessionWorktreeStore store, WorkspaceExecutionStore execution, - ManagedAgentProperties properties, ManagedAgentStore sessions, HarnessCoordinator coordinator) { + ManagedAgentSettings properties, AgentStateStore sessions, HarnessCoordinator coordinator) { this.store = store; this.execution = execution; this.properties = properties; @@ -59,7 +71,7 @@ public class SessionWorktreeService { } if (!value.isObject() || value.size() > 1 || value.size() == 1 && !value.has(key) || value.has(key) && (!value.get(key).isTextual() || value.get(key).asText().isBlank())) { - throw SessionWorktreeGit.error("invalid_worktree", "Worktree accepts only an optional local base ref."); + throw SessionWorktreeOperations.error("invalid_worktree", "Worktree accepts only an optional local base ref."); } return new Request(value.has(key) ? value.get(key).asText() : null); } @@ -91,22 +103,22 @@ public class SessionWorktreeService { public Plan preflight(String tenant, String actor, WorkspaceSelection selection, Request request) { if (!enabled()) { - throw SessionWorktreeGit.error("worktree_unsupported", "Session worktrees require a supported local Runtime."); + throw SessionWorktreeOperations.error("worktree_unsupported", "Session worktrees require a supported local Runtime."); } var source = store.source(tenant, actor, selection); try { var binding = source.resolved().binding(); var located = git().source(source.root(), binding.getCwdRelative(), request.baseRef()); String id = UUID.randomUUID().toString().replace("-", ""); - String target = SessionWorktreeGit.CONTAINER + "/" + id; + String target = SessionWorktreeOperations.CONTAINER + "/" + id; String cwd = ".".equals(located.offset()) ? target : target + "/" + located.offset(); return new Plan(tenant, binding.getWorkspaceId(), binding.getWorkspaceGeneration(), binding.getStorageId(), binding.getCwdRelative(), binding.getContextConfigRef(), binding.getContextRevision(), source.root().toString(), - SessionWorktreeGit.identity(source.root()), located.repositoryRelative(), located.offset(), located.commonDirectory(), + worktreeFiles.identity(source.root()), located.repositoryRelative(), located.offset(), located.commonDirectory(), located.commonIdentity(), request.baseRef(), located.commit(), id, target, cwd, "worktree-managed-" + id, source.storageIdentity()); } catch (ChildWorkspaceException exception) { - throw SessionWorktreeGit.error("worktree_layout_unsupported", exception.getMessage()); + throw SessionWorktreeOperations.error("worktree_layout_unsupported", exception.getMessage()); } } @@ -169,7 +181,7 @@ public class SessionWorktreeService { if (preparing && !store.active(row) && row.identityJson() == null) { if (java.nio.file.Files.exists(java.nio.file.Path.of(row.plan().root()).resolve(row.plan().worktreeRoot()), java.nio.file.LinkOption.NOFOLLOW_LINKS)) { - store.complete(row, owner, "READY", SessionWorktreeGit.verify(row.plan(), null), null); + store.complete(row, owner, "READY", worktreeFiles.verify(row.plan(), null), null); } else { store.complete(row, owner, "FAILED", null, "worktree_session_closed"); } @@ -180,18 +192,18 @@ public class SessionWorktreeService { store.complete(row, owner, "READY", null, storedBlocker); return; } - var binding = SessionWorktreeStore.storageBinding(row.plan()); + var binding = com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.storageBinding(row.plan()); String step = execution.holdForMaintenance(binding, row.operationId(), row.generation()); String identity = null; boolean stopped = true; try { - identity = ChildWorktreeGit.whileLive(live::get, () -> { + identity = git().runWhileLive(live::get, () -> { if (preparing) { return git().prepare(row.plan()); } String blocker = store.cleanupBlocker(row); if (blocker != null) { - throw SessionWorktreeGit.error(blocker, "The Session worktree is still in use or no longer authorized."); + throw SessionWorktreeOperations.error(blocker, "The Session worktree is still in use or no longer authorized."); } git().cleanup(row.plan(), row.identityJson()); return null; @@ -232,10 +244,10 @@ public class SessionWorktreeService { } } - private synchronized SessionWorktreeGit git() { + private synchronized SessionWorktreeOperations git() { if (git == null) { var broker = properties.getRuntimeBroker(); - git = new SessionWorktreeGit(new ChildWorktreeGit(broker.getChildWorkspaceGit(), broker.getChildWorkspaceGitTimeout())); + git = gitFactory.create(broker.getChildWorkspaceGit(), broker.getChildWorkspaceGitTimeout()); } return git; } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceService.java similarity index 79% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceService.java index 41919d6f4a4..4cdca808686 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceService.java @@ -1,29 +1,27 @@ package com.alibaba.qwen.code.managedagent.workspace; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.config.SshRuntimeSettings; +import com.alibaba.qwen.code.managedagent.contracts.session.AcceptanceEvidence; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.AcceptanceVerdict; import com.alibaba.qwen.code.managedagent.model.StoreModels.ParkedOperation; -import com.alibaba.qwen.code.managedagent.persistence.SshWorkspaceStore; -import com.alibaba.qwen.code.managedagent.persistence.SshWorkspaceStore.Intent; -import com.alibaba.qwen.code.managedagent.persistence.SshWorkspaceStore.Operation; -import com.alibaba.qwen.code.managedagent.persistence.SshWorkspaceStore.Parked; -import com.alibaba.qwen.code.managedagent.session.AcceptanceEvidence; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore; +import com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.Intent; +import com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.Operation; +import com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.Parked; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; +import com.alibaba.qwen.code.managedagent.settings.SshSettings; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.provisioning.SshConnectionManager; -import com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeNode; -import com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeStorage; +import com.alibaba.qwen.code.runtimebroker.spi.SshConnections; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeNode; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeStorage; import jakarta.annotation.PreDestroy; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.support.TransactionSynchronizationManager; -import java.net.URI; import java.time.Clock; import java.util.LinkedHashMap; import java.util.List; @@ -43,34 +41,29 @@ import java.util.concurrent.TimeUnit; * whether its marker landed, and it is read, never written again. */ @Service -public class SshWorkspaceService implements AutoCloseable, AcceptanceEvidence { - private final ManagedAgentProperties properties; +public class SshWorkspaceService implements AutoCloseable, AcceptanceEvidence, com.alibaba.qwen.code.managedagent.contracts.workspace.SshWorkspaceFiles { + private final ManagedAgentSettings properties; private final SshWorkspaceStore store; private final Clock clock; - private final Map nodes; - private final SshConnectionManager connections; + private final Map nodes; + private final SshConnections connections; private final Set running = ConcurrentHashMap.newKeySet(); private final ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(32), Thread.ofPlatform().daemon().name("ssh-workspace-", 0).factory()); - @org.springframework.beans.factory.annotation.Autowired - public SshWorkspaceService(ManagedAgentProperties properties, SshWorkspaceStore store, @Qualifier("managedAgentClock") Clock clock) { - this(properties, store, properties.getRuntimeBroker().getSsh().getNodes().isEmpty() ? null - : new SshConnectionManager(URI.create(properties.getToolPublication().getServiceBaseUrl())), clock); - } - - SshWorkspaceService(ManagedAgentProperties properties, SshWorkspaceStore store, - SshConnectionManager connections) { + SshWorkspaceService(ManagedAgentSettings properties, SshWorkspaceStore store, + SshConnections connections) { this(properties, store, connections, Clock.systemUTC()); } - SshWorkspaceService(ManagedAgentProperties properties, SshWorkspaceStore store, - SshConnectionManager connections, Clock clock) { + @org.springframework.beans.factory.annotation.Autowired + public SshWorkspaceService(ManagedAgentSettings properties, SshWorkspaceStore store, + @org.springframework.lang.Nullable SshConnections connections, @Qualifier("managedAgentClock") Clock clock) { this.properties = properties; this.store = store; this.connections = connections; this.clock = clock; - Map catalog = new LinkedHashMap<>(); + Map catalog = new LinkedHashMap<>(); for (var node : properties.getRuntimeBroker().getSsh().getNodes()) { if (catalog.putIfAbsent(node.nodeId(), node) != null) { throw new IllegalArgumentException("SSH node IDs must be unique"); @@ -97,8 +90,8 @@ public class SshWorkspaceService implements AutoCloseable, AcceptanceEvidence { public Directory check(String tenant, String actor, String nodeId, String path) { var node = admit(tenant, actor, nodeId); Map result = probe(node, path, "."); - return new Directory(nodeId, SshWorkspaceStore.string(SshWorkspaceStore.object(result, "identity"), "root"), - SshWorkspaceStore.string(result, "platform")); + return new Directory(nodeId, com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.object(result, "identity"), "root"), + com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(result, "platform")); } public Operation create(String tenant, String actor, String node, String path, String name, String key) { @@ -121,7 +114,7 @@ public class SshWorkspaceService implements AutoCloseable, AcceptanceEvidence { return !nodes.isEmpty(); } - public SshConnectionManager connections() { + public SshConnections connections() { if (connections == null) { throw unavailable(); } @@ -183,9 +176,6 @@ public class SshWorkspaceService implements AutoCloseable, AcceptanceEvidence { return result; } - public record FileTarget(SshRuntimeNode node, Map request, String qwenVersion) { - } - public FileTarget freezeFiles(ContextBinding binding) { verifyBinding(binding); var storage = runtimeStorage(binding); @@ -219,18 +209,18 @@ public class SshWorkspaceService implements AutoCloseable, AcceptanceEvidence { return result; } - private SshRuntimeSettings.Node admit(String tenant, String actor, String id) { + private SshSettings.Node admit(String tenant, String actor, String id) { if (!properties.getRuntimeBroker().getSsh().isEnabled()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "ssh_runtime_disabled", "SSH Workspace registration is disabled."); + throw new ManagedAgentException(404, "ssh_runtime_disabled", "SSH Workspace registration is disabled."); } - SshRuntimeSettings.Node node = nodes.get(id); + SshSettings.Node node = nodes.get(id); if (node == null || !node.admits(tenant, actor)) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, "runtime_node_forbidden", "Runtime node is unavailable."); + throw new ManagedAgentException(403, "runtime_node_forbidden", "Runtime node is unavailable."); } return node; } - private SshRuntimeSettings.Node configuredNode(String id) { + private SshSettings.Node configuredNode(String id) { var node = nodes.get(id); if (node == null) { throw unavailable(); @@ -238,7 +228,7 @@ public class SshWorkspaceService implements AutoCloseable, AcceptanceEvidence { return node; } - private Map probe(SshRuntimeSettings.Node node, String root, String cwd) { + private Map probe(SshSettings.Node node, String root, String cwd) { if (TransactionSynchronizationManager.isActualTransactionActive()) { throw new IllegalStateException("SSH probe cannot run in a database transaction"); } @@ -248,10 +238,10 @@ public class SshWorkspaceService implements AutoCloseable, AcceptanceEvidence { request.put("allowedRoots", node.allowedRoots()); var result = connections().bootstrap(node.runtimeNode(), request); if (!node.qwenVersion().equals(result.get("qwenVersion"))) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "ssh_runtime_version_mismatch", "Remote qwen-code version does not match its pinned version."); + throw new ManagedAgentException(409, "ssh_runtime_version_mismatch", "Remote qwen-code version does not match its pinned version."); } - if (SshWorkspaceStore.string(SshWorkspaceStore.object(result, "identity"), "root").length() > 512) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", "Canonical Workspace path exceeds its limit."); + if (com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.object(result, "identity"), "root").length() > 512) { + throw new ManagedAgentException(400, "invalid_request", "Canonical Workspace path exceeds its limit."); } return result; } @@ -285,7 +275,7 @@ public class SshWorkspaceService implements AutoCloseable, AcceptanceEvidence { throw unavailable(); } var result = probe(node, intent.path(), "."); - Map identity = SshWorkspaceStore.object(result, "identity"); + Map identity = com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.object(result, "identity"); Map marker = new LinkedHashMap<>(identity); marker.put("version", 2); marker.put("tenantId", tenant); @@ -306,7 +296,7 @@ public class SshWorkspaceService implements AutoCloseable, AcceptanceEvidence { if (!identity.equals(confirmed.get("identity")) || !marker.equals(confirmed.get("marker"))) { throw unavailable(); } - var finalProbe = probe(node, SshWorkspaceStore.string(identity, "root"), "."); + var finalProbe = probe(node, com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(identity, "root"), "."); if (!identity.equals(finalProbe.get("identity")) || !marker.equals(finalProbe.get("marker"))) { throw unavailable(); } @@ -349,13 +339,13 @@ public class SshWorkspaceService implements AutoCloseable, AcceptanceEvidence { return AcceptanceVerdict.notCommitted(parked.failureCode()); } Intent intent = parked.intent(); - SshRuntimeSettings.Node node = nodes.get(intent.nodeId()); + SshSettings.Node node = nodes.get(intent.nodeId()); if (node == null || !node.admits(intent.tenantId(), intent.actorId())) { return AcceptanceVerdict.unknown(); } - Map identity = SshWorkspaceStore.object(intent.registration(), "identity"); - Map marker = SshWorkspaceStore.object(intent.registration(), "marker"); - Map probed = probe(node, SshWorkspaceStore.string(identity, "root"), "."); + Map identity = com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.object(intent.registration(), "identity"); + Map marker = com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.object(intent.registration(), "marker"); + Map probed = probe(node, com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(identity, "root"), "."); if (!identity.equals(probed.get("identity"))) { return AcceptanceVerdict.notCommitted("ssh_runtime_identity_mismatch"); } @@ -375,7 +365,7 @@ public class SshWorkspaceService implements AutoCloseable, AcceptanceEvidence { }; } - private static Map request(SshRuntimeSettings.Node node, String operation) { + private static Map request(SshSettings.Node node, String operation) { Map result = new LinkedHashMap<>(); result.put("protocolVersion", 1); result.put("operation", operation); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceDiscoveryService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceDiscoveryService.java similarity index 92% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceDiscoveryService.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceDiscoveryService.java index 0bb76d7f6bb..4c889ea57b1 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceDiscoveryService.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceDiscoveryService.java @@ -1,12 +1,11 @@ package com.alibaba.qwen.code.managedagent.workspace; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry.WorkspaceSummary; +import com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry; +import com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.WorkspaceSummary; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.nio.ByteBuffer; @@ -76,14 +75,14 @@ public class WorkspaceDiscoveryService { WorkspaceSummary found = registry.findReadable(tenantId, actorId, workspaceId); if (found == null) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, + throw new ManagedAgentException(404, "workspace_not_found", "Workspace not found."); } return found; } private static ManagedAgentException invalidCursor() { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, + return new ManagedAgentException(400, "invalid_workspace_cursor", "Workspace cursor or limit is invalid."); } diff --git a/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ManagedRuntimeCoordinator.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ManagedRuntimeCoordinator.java new file mode 100644 index 00000000000..9dd0871d7a7 --- /dev/null +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ManagedRuntimeCoordinator.java @@ -0,0 +1,317 @@ +package com.alibaba.qwen.code.managedagent.workspace.runtime; + +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorkspaceProvider; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentWorkspaceCatalog; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; +import com.alibaba.qwen.code.managedagent.workspace.SshWorkspaceService; +import com.alibaba.qwen.code.runtimebroker.HarnessSessionResolver; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerOperations; +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.WorkspacePhysicalTransport; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +public final class ManagedRuntimeCoordinator implements RuntimeWarmer { + private final RuntimeBrokerOperations service; + private final AgentStateStore store; + private final WorkspaceRuntimeResolver workspaces; + private final ChildWorkspaceProvider childWorkspaces; + private final ResidentWorkspaceCatalog residents; + private final boolean resident; + private final SshWorkspaceService ssh; + private final RuntimeProvisioner localProvisioner; + private final RuntimeBindingRepository extensionBindings; + private final WorkspacePhysicalTransport extensionTransport; + private final java.util.Set retired = java.util.concurrent.ConcurrentHashMap.newKeySet(); + private static final java.util.Set LIFECYCLE_FENCED = java.util.Set.of("CLOSING", "CLOSED", "ARCHIVING", "ARCHIVED", "DELETING", "DELETED"); + public ManagedRuntimeCoordinator(RuntimeBrokerOperations service, AgentStateStore store, WorkspaceRuntimeResolver workspaces, + ChildWorkspaceProvider childWorkspaces, ResidentWorkspaceCatalog residents, boolean resident, + SshWorkspaceService ssh, RuntimeProvisioner localProvisioner, RuntimeBindingRepository extensionBindings, + WorkspacePhysicalTransport extensionTransport) { + this.service = service; + this.store = store; + this.workspaces = workspaces; + this.childWorkspaces = childWorkspaces; + this.residents = residents; + this.resident = resident; + this.ssh = ssh; + this.localProvisioner = localProvisioner; + this.extensionBindings = extensionBindings; + this.extensionTransport = extensionTransport; + } + public static HarnessSessionResolver resolver(AgentStateStore store, WorkspaceRuntimeResolver workspaces, + ManagedAgentSettings properties, String workspaceId, String workspaceCwd) { + var broker = properties.getRuntimeBroker(); + return new HarnessSessionResolver() { + @Override + public CompletionStage resolve(String sessionId) { + return resolveScope(sessionId, false, null); + } + + @Override + public CompletionStage resolve(String sessionId, + RuntimeLifecycleAuthority authority) { + return resolveScope(sessionId, false, authority); + } + + @Override + public CompletionStage resolveAdmission( + String sessionId) { + return resolveScope(sessionId, true, null); + } + + @Override + public CompletionStage resolveAdmission( + String sessionId, RuntimeLifecycleAuthority authority) { + return resolveScope(sessionId, true, authority); + } + + @Override + public CompletionStage resolveTenant(String sessionId) { + return store.findSessionById(sessionId) + .map(session -> CompletableFuture.completedFuture( + session.tenantId())) + .orElseGet(() -> CompletableFuture.failedFuture( + new IllegalArgumentException( + "Session is not owned by this" + + " service"))); + } + + private CompletionStage resolveScope( + String sessionId, boolean admission, + RuntimeLifecycleAuthority authority) { + SessionRecord session = store.findSessionById(sessionId) + .orElse(null); + if (session == null) { + CompletableFuture failed = + new CompletableFuture<>(); + failed.completeExceptionally(new IllegalArgumentException( + "Session is not owned by this service")); + return failed; + } + if (session.workspace() != null) { + if (workspaces != null) { + return CompletableFuture.completedFuture(workspaces.resolve(sessionId, authority).scope()); + } + return CompletableFuture.failedFuture( + new RuntimeBrokerException(409, + "workspace_unavailable", + "Hosted Workspace execution is not available.", + false)); + } + // The durable row is the admission fence for closing/closed, + // archived and deleted Sessions: unlike the in-process + // retired set it survives restarts and never accumulates in + // memory. Only the admission resolve fences: release and the + // unknown-outcome reconcile run after a close and must still + // resolve the scope to settle the binding. + if (admission && LIFECYCLE_FENCED.contains(session.status())) { + return CompletableFuture.failedFuture( + new RuntimeBrokerException(409, + "runtime_broker_session_closed", + "Harness Session is closed.", false)); + } + return CompletableFuture.completedFuture(new RuntimeScope( + session.tenantId(), workspaceId, + broker.getWorkspaceGeneration(), + workspaceCwd, + properties.getHarness().getCapabilityDigest(), + broker.getIsolationClass())); + } + }; + } + + @Override + public boolean isEnabled() { + return true; + } + + public void configureExtensions(com.alibaba.qwen.code.managedagent.contracts.extensions.ExtensionPackages packages) { + extensionTransport.configureExtensionPreparation((binding, sessionId, operation) -> { + String tenant = binding.getRequest().getScope().getTenantId(); + @SuppressWarnings("unchecked") + var selected = (List>) operation.get("extensions"); + CompletionStage>> delivery = + CompletableFuture.completedFuture(new java.util.LinkedHashMap<>()); + java.util.Map manifests = new java.util.LinkedHashMap<>(); + for (var pin : selected) { + delivery = delivery.thenCompose(settings -> { + var parsed = new com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Pin( + (String) pin.get("extensionId"), ((Number) pin.get("packageRevision")).longValue(), + (String) pin.get("packageDigest"), pin.get("settingsRevision") == null ? null + : ((Number) pin.get("settingsRevision")).longValue()); + try { + var resolved = packages.resolvePin(tenant, binding.getRequest().getScope().getWorkspaceId(), parsed); + var manifest = resolved.descriptor().manifest(); + java.util.Map metadata = new java.util.LinkedHashMap<>(java.util.Map.of( + "schemaVersion", manifest.schemaVersion(), "compilerVersion", manifest.compilerVersion(), + "name", manifest.name(), "version", manifest.version(), + "target", java.util.Map.of("platform", manifest.target().platform(), "arch", manifest.target().arch()), + "components", manifest.components())); + if (manifest.sourceType() != null) { + metadata.put("sourceType", manifest.sourceType()); + metadata.put("sourceRevision", manifest.sourceRevision()); + } + manifests.put(parsed.extensionId(), metadata); + return installExtensionPackage(tenant, sessionId, binding, parsed.packageDigest(), resolved.archive()) + .thenApply(ignored -> { + settings.put(parsed.extensionId(), resolved.settings()); + return settings; + }); + } catch (IOException error) { + return CompletableFuture.failedFuture(error); + } + }); + } + return delivery.thenApply(settings -> { + java.util.Map prepared = new java.util.LinkedHashMap<>(operation); + prepared.put("settings", settings); + prepared.put("packageManifests", manifests); + prepared.put("toolProfile", store.requireSession(tenant, sessionId).toolProfile()); + return prepared; + }); + }); + } + + private CompletionStage> installExtensionPackage(String tenantId, + String sessionId, com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord expected, String digest, Path archive) { + SessionRecord session = store.requireSession(tenantId, sessionId); + var binding = extensionBindings.findById(expected.getBindingId()); + if (binding == null || binding.getGeneration() != expected.getGeneration() + || !binding.getRequest().equals(expected.getRequest()) + || !java.util.Objects.equals(binding.getProvisionSeed(), expected.getProvisionSeed()) + || !sameExtensionLease(binding.getLease(), expected.getLease()) + || binding.getState() != com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord.State.READY + || binding.isDrainRequested() || binding.getLease() == null || session.workspace() == null + || !tenantId.equals(binding.getRequest().getScope().getTenantId()) + || !session.workspace().getWorkspaceId().equals(binding.getRequest().getScope().getWorkspaceId()) + || !"session".equals(binding.getRequest().getScope().getIsolationClass()) + || !sessionId.equals(binding.getRequest().getIsolationKey())) { + return CompletableFuture.failedFuture(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()); + } + return extensionTransport.installExtensionPackage(binding, digest, archive).thenApply(result -> { + var current = extensionBindings.findById(expected.getBindingId()); + if (current == null || current.getGeneration() != binding.getGeneration() + || !current.getRequest().equals(binding.getRequest()) + || !java.util.Objects.equals(current.getProvisionSeed(), binding.getProvisionSeed()) + || current.getState() != com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord.State.READY + || current.isDrainRequested() || !sameExtensionLease(current.getLease(), binding.getLease())) { + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); + } + return result; + }); + } + + private static boolean sameExtensionLease(RuntimeLease left, RuntimeLease right) { + return left != null && right != null && left.getRuntimeInstanceId().equals(right.getRuntimeInstanceId()) + && left.getLeaseId().equals(right.getLeaseId()) && left.getEpoch() == right.getEpoch() + && left.getEndpoint().equals(right.getEndpoint()) && left.getToken().equals(right.getToken()); + } + + @Override + public CompletionStage warm(String sessionId) { + if (retired.contains(sessionId)) { + CompletableFuture failed = new CompletableFuture<>(); + failed.completeExceptionally(new RuntimeBrokerException(409, + "runtime_broker_session_closed", + "Harness Session is closed.", false)); + return failed; + } + return service.warm(sessionId).thenApply(ignored -> null); + } + + @Override + public CompletionStage drain(String sessionId) { + // drain() runs while the row still reads CLOSING/DELETING; the + // admission resolve fences every lifecycle status durably, so only + // a vanished row needs the in-process entry. + if (store.findSessionById(sessionId).isEmpty()) { + retired.add(sessionId); + } + return CompletableFuture.completedFuture(null); + } + + @Override + public boolean supportsWorkspaceClose() { + return service.supportsDrainedStop() || resident; + } + + @Override + public boolean supportsWorkspaceContextChange(ContextBinding binding) { + return residents.find(binding) == null; + } + + @Override + public boolean supportsWorkspaceClose(com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding binding) { + if (resident && residents.find(binding) != null) { + return true; + } + if (binding != null && ssh != null && ssh.runtimeStorage(binding) != null) { + ssh.node(ssh.runtimeStorage(binding).nodeId()); + return true; + } + return localProvisioner.supportsDrainedStop(); + } + + @Override + public void requestWorkspaceClose(String tenantId, String sessionId) { + service.requestHarnessDrain(tenantId, sessionId); + } + + @Override + public CompletionStage closeWorkspace(String tenantId, String sessionId) { + var session = store.findSessionById(sessionId).orElse(null); + if (resident) { + if (session == null || !tenantId.equals(session.tenantId()) || residents.find(session.workspace()) == null) { + return CompletableFuture.failedFuture(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()); + } + return service.closeResidentHarnessSession(residents.request(session.workspace()), sessionId); + } + return service.drainHarnessSession(tenantId, sessionId); + } + + @Override + public CompletionStage drainWorkspaceResources(String tenantId, String sessionId, + com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority) { + return service.drainHarnessResources(tenantId, sessionId, authority, false); + } + + @Override + public CompletionStage prepareWorkspaceResources(String tenantId, String sessionId, + com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority) { + return service.prepareHarnessResources(tenantId, sessionId, authority); + } + + @Override + public CompletionStage recoverStoppedWorkspaceResources(String tenantId, String sessionId, + com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority) { + return service.drainHarnessResources(tenantId, sessionId, authority, true); + } + + @Override + public ChildWorkspaceProvider childWorkspaces() { + return childWorkspaces; + } + + @Override + public void verifyWorkspaceCwdTarget(ContextBinding binding, + String targetCwdRelative) { + if (workspaces == null) { + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); + } + workspaces.verifyInstallable(binding, targetCwdRelative); + } +} diff --git a/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentWorkspaceRetirementService.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentWorkspaceRetirementService.java new file mode 100644 index 00000000000..51378b1ce67 --- /dev/null +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentWorkspaceRetirementService.java @@ -0,0 +1,213 @@ +package com.alibaba.qwen.code.managedagent.workspace.runtime; + +import com.alibaba.qwen.code.managedagent.repository.ResidentRetirementRepository; +import com.alibaba.qwen.code.managedagent.repository.ResidentRetirementRepository.*; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker.ResidentWorkspace; +import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; +import com.alibaba.qwen.code.runtimebroker.spi.KubernetesMaintenanceClient; + +import java.time.Instant; +import java.util.List; +import java.util.Map; + +import static com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity.digest; +import static com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity.map; +public final class ResidentWorkspaceRetirementService { + private final ResidentRetirementRepository repository; + public ResidentWorkspaceRetirementService(ResidentRetirementRepository repository) { this.repository = repository; } + + public Status deletePod( + Request request, String resourceVersion, KubernetesMaintenanceClient api) { + require(resourceVersion != null && resourceVersion.matches("[\\x21-\\x7e]{1,256}")); + var selected = repository.selected(request); + var original = repository.original(request, selected); + var placement = map(original.getResourceHandle().getValue().get("placement")); + String workKey = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key((String) placement.get("podUid"), resourceVersion); + if (repository.hasDeletionAttempt(request, workKey)) { + return repository.inspect(request); + } + api.verifyProtection(selected.registration(), selected.protection()).toCompletableFuture().join(); + String podName = (String) placement.get("podName"); + var pod = api.get("pods", selected.registration().namespace(), podName).toCompletableFuture().join(); + var saved = repository.loadState(request); + var baseline = saved.get("reclamationBaseline") == null ? null : repository.baseline(saved); + verifyPod(original, pod, resourceVersion, baseline == null ? List.of() : List.of(baseline.finalizer())); + var node = api.getCluster("nodes", (String) placement.get("nodeName")).toCompletableFuture().join(); + require(node != null && placement.get("nodeUid").equals(map(node.get("metadata")).get("uid")) + && map(node.get("status")).get("conditions") instanceof List conditions + && conditions.stream().anyMatch(value -> "Ready".equals(map(value).get("type")) + && "True".equals(map(value).get("status")))); + byte[] body = WorkspaceCsiRuntimeIdentity.bytes(Map.of("apiVersion", "v1", "kind", "DeleteOptions", + "gracePeriodSeconds", 30, "propagationPolicy", "Background", "preconditions", + Map.of("uid", placement.get("podUid"), "resourceVersion", resourceVersion))); + Attempt attempt = repository.beginDeletion(request, selected, workKey, resourceVersion, body); + if (attempt == null) { + return repository.inspect(request); + } + KubernetesMaintenanceClient.DeleteResponse reply = null; + try { + reply = api.deletePod(selected.registration().namespace(), podName, attempt.body()).toCompletableFuture().join(); + } catch (java.util.concurrent.CompletionException failure) { + // The durable UNKNOWN attempt must survive a lost reply without resending. + } + var response = reply; + repository.completeDeletion(request, selected, attempt, response); + return repository.inspect(request); + } + + public Status prepareReclamation( + Request request, String resourceVersion, KubernetesMaintenanceClient api) { + require(resourceVersion != null && resourceVersion.matches("[\\x21-\\x7e]{1,256}")); + var prior = repository.inspect(request); + if (!"DRAINING".equals(prior.state())) { + require(!"NOT_STARTED".equals(prior.state())); + return prior; + } + var selected = repository.selected(request); + var original = repository.original(request, selected); + var saved = repository.loadState(request); + require(repository.planVersion(request, saved) == 2); + verifyProtection(selected, api); + var placement = map(original.getResourceHandle().getValue().get("placement")); + var pod = api.get("pods", selected.registration().namespace(), (String) placement.get("podName")).toCompletableFuture().join(); + require(pod != null); + String finalizer = finalizer(request, original); + var retained = saved.get("reclamationBaseline") == null ? null : repository.baseline(saved); + if (retained != null && List.of(finalizer).equals(map(pod.get("metadata")).get("finalizers"))) { + verifyPod(original, pod, resourceVersion, List.of(finalizer)); + return repository.inspect(request); + } + verifyPod(original, pod, resourceVersion, List.of()); + var observed = api.reclamationBaseline(original, selected, finalizer, Instant.now()); + repository.recordBaseline(request, selected, observed, finalizer); + patchFinalizer(request, selected, original, pod, "ADD", api); + return repository.inspect(request); + } + + public Status reclaim(Request request, KubernetesMaintenanceClient api) { + var prior = repository.inspect(request); + if ("RELEASED".equals(prior.state())) { + return prior; + } + var selected = repository.selected(request); + var original = repository.original(request, selected); + var saved = repository.loadState(request); + require(repository.planVersion(request, saved) == 2); + var baseline = repository.baseline(saved); + require(baseline.finalizer().equals(finalizer(request, original))); + var placement = map(original.getResourceHandle().getValue().get("placement")); + String namespace = selected.registration().namespace(); + String name = (String) placement.get("podName"); + if ("DRAINING".equals(saved.get("state"))) { + require(repository.hasDeletionAttempts(request)); + verifyProtection(selected, api); + var pod = api.get("pods", namespace, name).toCompletableFuture().join(); + var observed = api.reclamationReceipt(original, selected, baseline, pod, Instant.now()); + repository.recordCleanup(request, selected, baseline, observed, original); + } + saved = repository.loadState(request); + var proof = repository.receipt(saved, original); + var pod = api.get("pods", namespace, name).toCompletableFuture().join(); + if (pod != null) { + var metadata = map(pod.get("metadata")); + require(placement.get("podUid").equals(metadata.get("uid")) + && placement.get("podName").equals(metadata.get("name")) + && placement.get("namespace").equals(metadata.get("namespace")) + && metadata.get("deletionTimestamp") instanceof String + && digest(pod.get("spec")).equals(original.getResourceHandle().getValue().get("podSpecDigest"))); + Instant.parse((String) metadata.get("deletionTimestamp")); + if (List.of(baseline.finalizer()).equals(metadata.get("finalizers"))) { + patchFinalizer(request, selected, original, pod, "REMOVE", api); + } else { + require(empty(metadata.get("finalizers"))); + } + if (api.get("pods", namespace, name).toCompletableFuture().join() != null) { + return repository.inspect(request); + } + } + return repository.release(request, selected, proof); + } + + private void patchFinalizer( + Request request, + ResidentWorkspace selected, + RuntimeBindingRecord original, + Map pod, + String action, + KubernetesMaintenanceClient api) { + var metadata = map(pod.get("metadata")); + String resourceVersion = (String) metadata.get("resourceVersion"); + require(resourceVersion != null && resourceVersion.matches("[\\x21-\\x7e]{1,256}")); + String finalizer = finalizer(request, original); + var before = metadata.get("finalizers"); + require("ADD".equals(action) ? empty(before) : List.of(finalizer).equals(before)); + var patch = new java.util.ArrayList>(); + patch.add(Map.of("op", "test", "path", "/metadata/uid", "value", metadata.get("uid"))); + patch.add(Map.of("op", "test", "path", "/metadata/resourceVersion", "value", resourceVersion)); + if (before != null) { + patch.add(Map.of("op", "test", "path", "/metadata/finalizers", "value", before)); + } + patch.add(Map.of("op", "add", "path", "/metadata/finalizers", "value", "ADD".equals(action) ? List.of(finalizer) : List.of())); + byte[] body = WorkspaceCsiRuntimeIdentity.bytes(patch); + var attempt = repository.beginFinalizer(request, selected, original, finalizer, action, metadata, resourceVersion, body); + KubernetesMaintenanceClient.DeleteResponse response = null; + try { + response = api.patchPodFinalizers(selected.registration().namespace(), + (String) map(original.getResourceHandle().getValue().get("placement")).get("podName"), body).toCompletableFuture().join(); + } catch (java.util.concurrent.CompletionException failure) { + // Independent platform readback can recover the effect; this attempt stays UNKNOWN. + } + var reply = response; + repository.completeFinalizer(request, selected, attempt, reply); + } + + private static void verifyProtection( + ResidentWorkspace selected, KubernetesMaintenanceClient api) { + api.verifyProtection(selected.registration(), selected.protection()).toCompletableFuture().join(); + } + + private static void verifyPod( + RuntimeBindingRecord original, + Map pod, + String resourceVersion, + List finalizers) { + require(pod != null && "v1".equals(pod.get("apiVersion")) && "Pod".equals(pod.get("kind"))); + var placement = map(original.getResourceHandle().getValue().get("placement")); + var metadata = map(pod.get("metadata")); + require(placement.get("podName").equals(metadata.get("name")) && placement.get("namespace").equals(metadata.get("namespace")) + && placement.get("podUid").equals(metadata.get("uid")) && resourceVersion.equals(metadata.get("resourceVersion")) + && metadata.get("deletionTimestamp") == null && metadata.get("deletionGracePeriodSeconds") == null + && empty(metadata.get("ownerReferences")) + && (finalizers.isEmpty() ? empty(metadata.get("finalizers")) : finalizers.equals(metadata.get("finalizers"))) + && digest(pod.get("spec")).equals(original.getResourceHandle().getValue().get("podSpecDigest"))); + var status = map(pod.get("status")); + require("Running".equals(status.get("phase")) && placement.get("podIp").equals(status.get("podIP")) + && status.get("containerStatuses") instanceof List containers && containers.size() == 1); + var container = map(((List) status.get("containerStatuses")).getFirst()); + require(placement.get("containerName").equals(container.get("name")) + && placement.get("containerId").equals(container.get("containerID")) && placement.get("imageId").equals(container.get("imageID")) + && Long.valueOf(0).equals(BrokerValues.exactLong(container.get("restartCount"))) + && Boolean.TRUE.equals(container.get("ready")) && map(container.get("state")).containsKey("running")); + } + + private static String finalizer(Request request, RuntimeBindingRecord original) { + return "qwen.ai/retirement-" + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(request.tenantId(), request.retirementId(), original.getBindingId()).substring(0, 40); + } + + private static boolean empty(Object value) { + return value == null || value instanceof List list && list.isEmpty(); + } + + private static void require(boolean valid) { + if (!valid) { + throw unavailable(); + } + } + + private static RuntimeBrokerException unavailable() { + return new RuntimeBrokerException(409, "resident_retirement_unavailable", "Original idle Workspace maintenance is unavailable.", false); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinator.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinator.java similarity index 79% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinator.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinator.java index 58a28fe8f59..743f50dc335 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinator.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinator.java @@ -1,8 +1,7 @@ package com.alibaba.qwen.code.managedagent.workspace.runtime; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerOperations; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,26 +9,26 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; /** Bounded maintenance of saved local bindings, independent of product Session authorization. */ -final class RuntimeRecoveryCoordinator implements AutoCloseable { +public final class RuntimeRecoveryCoordinator implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(RuntimeRecoveryCoordinator.class); private static final int BATCH_SIZE = 8; - private final RuntimeBrokerService service; + private final RuntimeBrokerOperations service; private final RuntimeBindingRepository bindings; private final AtomicBoolean running = new AtomicBoolean(); private volatile boolean closed; private String cursor; - RuntimeRecoveryCoordinator(RuntimeBrokerService service, RuntimeBindingRepository bindings) { + public RuntimeRecoveryCoordinator(RuntimeBrokerOperations service, RuntimeBindingRepository bindings) { this.service = service; this.bindings = bindings; } - void scan() { + public void scan() { if (closed || !running.compareAndSet(false, true)) { return; } try { - var candidates = bindings.findRecoveryCandidates(LocalProcessRuntimeProvisioner.KIND, cursor, BATCH_SIZE); + var candidates = bindings.findRecoveryCandidates(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, cursor, BATCH_SIZE); cursor = candidates.size() < BATCH_SIZE ? null : candidates.getLast().getBindingId(); CompletableFuture[] operations = candidates.stream().map(binding -> { try { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJob.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJob.java similarity index 75% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJob.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJob.java index 920a4aa9462..4fe4936adf9 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJob.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJob.java @@ -1,18 +1,18 @@ package com.alibaba.qwen.code.managedagent.workspace.runtime; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeRetention; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; +import com.alibaba.qwen.code.runtimebroker.RuntimeRetention; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; public final class RuntimeRetentionJob { private static final Logger LOG = LoggerFactory.getLogger(RuntimeRetentionJob.class); - private final JdbcRuntimeRetention retention; - private final ManagedAgentProperties.RuntimeBroker.Retention settings; - private JdbcRuntimeRetention.Cursor cursor; + private final RuntimeRetention retention; + private final ManagedAgentSettings.RuntimeBroker.Retention settings; + private RuntimeRetention.Cursor cursor; - public RuntimeRetentionJob(JdbcRuntimeRetention retention, ManagedAgentProperties properties) { + public RuntimeRetentionJob(RuntimeRetention retention, ManagedAgentSettings properties) { this.retention = retention; this.settings = properties.getRuntimeBroker().getRetention(); } @@ -33,7 +33,7 @@ public final class RuntimeRetentionJob { } } - public synchronized JdbcRuntimeRetention.BatchResult runOnce() { + public synchronized RuntimeRetention.BatchResult runOnce() { var result = retention.sweep(settings.getMaxAge(), settings.getBatchSize(), cursor); cursor = result.cursor(); return result; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeProvisioner.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeProvisioner.java similarity index 92% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeProvisioner.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeProvisioner.java index b3422a8b223..8211df79c91 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeProvisioner.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeProvisioner.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.workspace.runtime; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; +import com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeObservation; @@ -14,12 +14,12 @@ import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -final class WorkspaceRuntimeProvisioner implements RuntimeProvisioner { +public final class WorkspaceRuntimeProvisioner implements RuntimeProvisioner { private final RuntimeProvisioner delegate; private final WorkspaceRuntimeResolver resolver; private final WorkspaceExecutionStore executionStore; - WorkspaceRuntimeProvisioner(RuntimeProvisioner delegate, WorkspaceRuntimeResolver resolver, + public WorkspaceRuntimeProvisioner(RuntimeProvisioner delegate, WorkspaceRuntimeResolver resolver, WorkspaceExecutionStore executionStore) { this.delegate = delegate; this.resolver = resolver; @@ -38,7 +38,7 @@ final class WorkspaceRuntimeProvisioner implements RuntimeProvisioner { } var resolved = resolver.resolve(harnessSessionId, scope.getLifecycleAuthority()); if (!resolved.scope().equals(scope)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } var selected = delegate.createRequest(scope, isolationKey); return new RuntimeProvisionRequest(scope, isolationKey, selected.getProvisionerKind(), resolved.binding().getStorageId()); @@ -86,7 +86,7 @@ final class WorkspaceRuntimeProvisioner implements RuntimeProvisioner { var resolved = resolver.resolve(request.getIsolationKey(), request.getScope().getLifecycleAuthority()); if (!resolved.scope().equals(request.getScope()) || !resolved.binding().getStorageId().equals(request.getStorageId())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return; } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolver.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolver.java similarity index 77% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolver.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolver.java index baae5926188..5658bfe72fd 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolver.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolver.java @@ -1,15 +1,14 @@ package com.alibaba.qwen.code.managedagent.workspace.runtime; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.managedagent.workspace.SshWorkspaceService; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceRelativePath; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; import java.io.IOException; import java.nio.file.Files; @@ -20,22 +19,22 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; -final class WorkspaceRuntimeResolver { +public final class WorkspaceRuntimeResolver { private final AgentStateStore sessions; private final WorkspaceExecutionStore authority; private final SshWorkspaceService ssh; - private final com.alibaba.qwen.code.managedagent.persistence.ResidentWorkspaces residents; + private final com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentWorkspaceCatalog residents; private final Map mounts = new LinkedHashMap<>(); private final String boundSessionIsolationClass; - private final ManagedAgentProperties.ManagedExtensions managedExtensions; + private final ManagedAgentSettings.ManagedExtensions managedExtensions; - WorkspaceRuntimeResolver(AgentStateStore sessions, WorkspaceExecutionStore authority, - ManagedAgentProperties properties) { + public WorkspaceRuntimeResolver(AgentStateStore sessions, WorkspaceExecutionStore authority, + ManagedAgentSettings properties) { this(sessions, authority, properties, null); } - WorkspaceRuntimeResolver(AgentStateStore sessions, WorkspaceExecutionStore authority, - ManagedAgentProperties properties, SshWorkspaceService ssh) { + public WorkspaceRuntimeResolver(AgentStateStore sessions, WorkspaceExecutionStore authority, + ManagedAgentSettings properties, SshWorkspaceService ssh) { this.ssh = ssh; this.managedExtensions = properties.getManagedExtensions(); if (managedExtensions.isEnabled() && !properties.getExtensions().isEnabled()) { @@ -45,7 +44,7 @@ final class WorkspaceRuntimeResolver { this.authority = authority; var broker = properties.getRuntimeBroker(); this.boundSessionIsolationClass = broker.getBoundSessionIsolationClass(); - this.residents = new com.alibaba.qwen.code.managedagent.persistence.ResidentWorkspaces(broker.getResidentWorkspaces()); + this.residents = new com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentWorkspaceCatalog(broker.getResidentWorkspaces()); if (WorkspaceExecutionProfile.CAPABILITY_DIGEST.equals( properties.getHarness().getCapabilityDigest()) || com.alibaba.qwen.code.runtimebroker.ResidentWorkspaceProfile.CAPABILITY_DIGEST.equals( @@ -53,7 +52,7 @@ final class WorkspaceRuntimeResolver { throw new IllegalStateException("Workspace execution profile is reserved"); } if (!broker.getWorkspaceMounts().isEmpty() - && (!LocalProcessRuntimeProvisioner.KIND.equals(broker.getProvisioner()) + && (!com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(broker.getProvisioner()) || !"session".equals(boundSessionIsolationClass) && !"workspace".equals(boundSessionIsolationClass))) { throw new IllegalStateException("Workspace execution requires a local-process Broker" @@ -83,21 +82,21 @@ final class WorkspaceRuntimeResolver { } } - Resolved resolve(String sessionId) { + public Resolved resolve(String sessionId) { return resolve(sessionId, null); } - Resolved resolve(String sessionId, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority lifecycle) { + public Resolved resolve(String sessionId, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority lifecycle) { return resolve(sessionId, lifecycle, false); } - Resolved resolveHook(String sessionId, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority lifecycle) { + public Resolved resolveHook(String sessionId, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority lifecycle) { return resolve(sessionId, lifecycle, true); } private Resolved resolve(String sessionId, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority lifecycle, boolean hook) { SessionRecord session = sessions.findSessionById(sessionId) - .orElseThrow(WorkspaceExecutionStore::unavailable); + .orElseThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore::unavailable); if (lifecycle == null && hook && "CLOSING".equals(session.status())) { authority.authorizeLegacyClose(session); } else if (lifecycle == null) { @@ -108,7 +107,7 @@ final class WorkspaceRuntimeResolver { ContextBinding binding = session.workspace(); if (ssh != null && ssh.runtimeStorage(binding) != null) { if (hook && !managedExtensions.isEnabled()) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } ssh.verifyBinding(binding); return new Resolved(binding, new RuntimeScope(session.tenantId(), binding.getWorkspaceId(), @@ -117,12 +116,12 @@ final class WorkspaceRuntimeResolver { } var resident = residents.find(binding); if (resident != null) { - return new Resolved(binding, com.alibaba.qwen.code.managedagent.persistence.ResidentWorkspaces.scope(resident) + return new Resolved(binding, com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentWorkspaceCatalog.scope(resident) .withLifecycleAuthority(lifecycle)); } Mount mount = mount(binding); if (mount == null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } verifyMountIntact(mount); return new Resolved(binding, new RuntimeScope(session.tenantId(), binding.getWorkspaceId(), @@ -154,7 +153,7 @@ final class WorkspaceRuntimeResolver { * change *away from* a destroyed directory reachable, the escape this * feature exists for. */ - void verifyInstallable(ContextBinding binding, String targetCwdRelative) { + public void verifyInstallable(ContextBinding binding, String targetCwdRelative) { if (ssh != null && ssh.runtimeStorage(binding) != null) { ssh.verify(new ContextBinding(binding.getTenantId(), binding.getWorkspaceId(), binding.getWorkspaceGeneration(), binding.getStorageId(), WorkspaceRelativePath.normalize(targetCwdRelative), @@ -163,7 +162,7 @@ final class WorkspaceRuntimeResolver { } Mount mount = mount(binding); if (mount == null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } verifyMountIntactForProbe(mount); requireDirectoryForProbe(mount.root().toString(), targetCwdRelative); @@ -178,7 +177,7 @@ final class WorkspaceRuntimeResolver { binding.getContextRevision())); } catch (com.alibaba.qwen.code.runtimebroker.managedworkspace .WorkspaceException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -187,10 +186,10 @@ final class WorkspaceRuntimeResolver { * administrator mount of the binding's storage, held to the same * continuity check as an acquisition. */ - java.nio.file.Path storageRoot(ContextBinding binding) { + public java.nio.file.Path storageRoot(ContextBinding binding) { Mount mount = mount(binding); if (mount == null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } verifyMountIntact(mount); return mount.root(); @@ -201,21 +200,21 @@ final class WorkspaceRuntimeResolver { * the mount for the storage must still resolve to the request's root and * stay intact. A shared binding has no owning Session to resolve by. */ - void requireMountCurrent(RuntimeScope scope, String storageId) { + public void requireMountCurrent(RuntimeScope scope, String storageId) { Mount mount = mounts.get(new Storage(scope.getTenantId(), storageId)); if (mount == null) { Path root = authority.createdStorageRoot(scope.getTenantId(), storageId); if (root == null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } try { mount = new Mount(root, Files.readAttributes(root, BasicFileAttributes.class).fileKey()); } catch (IOException error) { - throw WorkspaceExecutionStore.unavailable(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(error); } } if (!mount.root().toString().equals(scope.getCanonicalCwd())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } verifyMountIntact(mount); } @@ -232,7 +231,7 @@ final class WorkspaceRuntimeResolver { try { return new Mount(root, Files.readAttributes(root, BasicFileAttributes.class).fileKey()); } catch (IOException error) { - throw WorkspaceExecutionStore.unavailable(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(error); } } @@ -244,10 +243,10 @@ final class WorkspaceRuntimeResolver { if (!mount.root().equals(mount.root().toRealPath()) || !Objects.equals(mount.fileKey(), Files.readAttributes( mount.root(), BasicFileAttributes.class).fileKey())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } catch (IOException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -256,14 +255,14 @@ final class WorkspaceRuntimeResolver { if (!mount.root().equals(mount.root().toRealPath()) || !Objects.equals(mount.fileKey(), Files.readAttributes( mount.root(), BasicFileAttributes.class).fileKey())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } // The mount root itself being gone is a permanent configuration // fact, not a momentary blip — only a genuine I/O hiccup retries. } catch (java.nio.file.NoSuchFileException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } catch (IOException error) { - throw WorkspaceExecutionStore.unavailableTransient(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailableTransient(error); } } @@ -274,7 +273,7 @@ final class WorkspaceRuntimeResolver { // retry; the acquire path fails the Turn immediately with the // accurate code instead (an ENOTDIR-shaped cwd must not surface as // hosted_harness_unavailable or park the binding RECOVERY_BLOCKED). - static void requireDirectory(String root, String cwdRelative) { + public static void requireDirectory(String root, String cwdRelative) { try { Path base = Path.of(root); Path directory = base.resolve(cwdRelative).normalize(); @@ -283,13 +282,13 @@ final class WorkspaceRuntimeResolver { || !directory.toRealPath().equals(directory) || !Files.isReadable(directory) || !Files.isExecutable(directory)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } // The missing-directory shape (NoSuchFileException) and every // other I/O anomaly share the terminal verdict. } catch (IOException | IllegalArgumentException | SecurityException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -302,7 +301,7 @@ final class WorkspaceRuntimeResolver { // instead: a regular file or a dangling/looping symlink in the chain // is structural, whatever is left (ESTALE/EIO, ENAMETOOLONG) is // momentary and retries through the budget. - static void requireDirectoryForProbe(String root, String cwdRelative) { + public static void requireDirectoryForProbe(String root, String cwdRelative) { Path base = Path.of(root); Path directory = base.resolve(cwdRelative).normalize(); try { @@ -311,7 +310,7 @@ final class WorkspaceRuntimeResolver { BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS).isDirectory() || !directory.toRealPath().equals(directory)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } directory.getFileSystem().provider().checkAccess(directory, java.nio.file.AccessMode.READ, @@ -319,11 +318,11 @@ final class WorkspaceRuntimeResolver { } catch (java.nio.file.NoSuchFileException | java.nio.file.AccessDeniedException | SecurityException | IllegalArgumentException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } catch (IOException error) { throw hasStructuralAncestor(base, directory) - ? WorkspaceExecutionStore.unavailable() - : WorkspaceExecutionStore.unavailableTransient(error); + ? com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable() + : com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailableTransient(error); } } @@ -344,12 +343,12 @@ final class WorkspaceRuntimeResolver { return false; } - ContextBinding savedBinding(String sessionId) { + public ContextBinding savedBinding(String sessionId) { return sessions.findSessionById(sessionId).map(SessionRecord::workspace) - .orElseThrow(WorkspaceExecutionStore::unavailable); + .orElseThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore::unavailable); } - record Resolved(ContextBinding binding, RuntimeScope scope) { + public record Resolved(ContextBinding binding, RuntimeScope scope) { } private record Storage(String tenantId, String storageId) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransport.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransport.java similarity index 89% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransport.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransport.java index 720e3e07b81..16baf10f026 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransport.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransport.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent.workspace.runtime; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; +import com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.HarnessOwnership; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiFilesRetirementGuard; import com.alibaba.qwen.code.runtimebroker.ManagedExtensionExecutionProfile; import com.alibaba.qwen.code.runtimebroker.ResidentWorkspaceProfile; import com.alibaba.qwen.code.runtimebroker.RuntimeAttestation; @@ -18,10 +18,8 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.WorkspacePhysicalTransport; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiProtocol; import com.alibaba.qwen.code.runtimebroker.transport.ManagedExtensionCommandProtocol; import com.alibaba.qwen.code.runtimebroker.transport.ManagedExtensionProtocol; @@ -38,8 +36,8 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -final class WorkspaceRuntimeTransport implements RuntimeTransport { - private final HttpRuntimeTransport delegate; +public final class WorkspaceRuntimeTransport implements RuntimeTransport { + private final WorkspacePhysicalTransport delegate; private final WorkspaceRuntimeResolver resolver; private final java.util.function.Function savedBindings; private final WorkspaceExecutionStore ownership; @@ -51,7 +49,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { // drops the debt and leaves it to the worker's own lifetime. private final java.util.Set detachOwed = java.util.concurrent.ConcurrentHashMap.newKeySet(); - WorkspaceRuntimeTransport(HttpRuntimeTransport delegate, WorkspaceRuntimeResolver resolver, + public WorkspaceRuntimeTransport(WorkspacePhysicalTransport delegate, WorkspaceRuntimeResolver resolver, WorkspaceExecutionStore ownership, RuntimeBindingRepository bindings, RuntimeSessionRepository sessions) { this.delegate = delegate; @@ -62,7 +60,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { this.sessions = sessions; } - WorkspaceRuntimeTransport(HttpRuntimeTransport delegate, WorkspaceExecutionStore ownership, + public WorkspaceRuntimeTransport(WorkspacePhysicalTransport delegate, WorkspaceExecutionStore ownership, RuntimeBindingRepository bindings, RuntimeSessionRepository sessions, java.util.function.Function savedBindings) { this.delegate = delegate; @@ -88,11 +86,11 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { || current.getState() != RuntimeBindingRecord.State.READY || current.isDrainRequested() || current.getLease() == null || !sameLease(current.getLease(), runtime.getLease()) - || !LocalProcessRuntimeProvisioner.KIND.equals(current.getRequest().getProvisionerKind()) - && !(SshRuntimeProvisioner.KIND.equals(current.getRequest().getProvisionerKind()) + || !com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(current.getRequest().getProvisionerKind()) + && !(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS.equals(current.getRequest().getProvisionerKind()) && ManagedExtensionExecutionProfile.admits(current.getRequest().getScope().getCapabilityDigest())) || !WorkspaceExecutionProfile.admitsCapabilityDigest(current.getRequest().getScope().getCapabilityDigest())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return delegate.mcpDefinitions(current); } @@ -115,7 +113,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { || !Long.toString(saved.getWorkspaceGeneration()).equals(current.getRequest().getScope().getWorkspaceGeneration()) || !saved.getStorageId().equals(current.getRequest().getStorageId()) || !harnessSessionId.equals(current.getRequest().getIsolationKey())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return delegate.extensionControl(current, harnessSessionId, saved, operation); } @@ -128,9 +126,9 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { Context context = context(lease, session, true); // Reject a missing directory before a new claim can strand storage ownership. if (!ResidentWorkspaceProfile.selects(session.getScope())) { - if (LocalProcessRuntimeProvisioner.KIND.equals(context.runtime().getRequest().getProvisionerKind())) { + if (com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(context.runtime().getRequest().getProvisionerKind())) { WorkspaceRuntimeResolver.requireDirectory(session.getScope().getCanonicalCwd(), context.binding().getCwdRelative()); - } else if (SshRuntimeProvisioner.KIND.equals(context.runtime().getRequest().getProvisionerKind()) && resolver != null) { + } else if (com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS.equals(context.runtime().getRequest().getProvisionerKind()) && resolver != null) { try { resolver.verifyInstallable(context.binding(), context.binding().getCwdRelative()); } catch (RuntimeBrokerException error) { @@ -139,10 +137,10 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { // Hosted tool turn recognizes instead of the ssh_runtime_* // verdict an unrecognized code would park into Session // recovery. The original verdict stays as the cause. - throw WorkspaceExecutionStore.unavailable(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(error); } } else { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } ownership.claim(context.binding(), context.session()); @@ -196,7 +194,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { } if (v3(reference)) { if (captureContext == null || !"run_shell_command".equals(reference.get("toolName"))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Map capture = Map.of("tenantId", session.getScope().getTenantId(), "sessionId", session.getHarnessSessionId(), "turnId", reference.get("promptId"), @@ -205,7 +203,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { "capturePolicy", "complete_required"); return delegate.executeV3(lease, session, wireReference(reference), capture).thenApply(answer -> { if (!"settled".equals(answer.get("state")) || !(answer.get("result") instanceof Map)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } @SuppressWarnings("unchecked") Map result = (Map) answer.get("result"); @@ -275,7 +273,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { Map boot, Map expectedPod, Map request, Map expectedCaptureIdentity) { if (!managed(session)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Context context = context(lease, session, false, false, true); var runtime = context.runtime(); @@ -285,7 +283,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { || context.session().getState() != RuntimeSessionRecord.State.READY || !context.session().getSession().getScope().equals(session.getScope()) || !Long.toString(runtime.getGeneration()).equals(expectedCaptureIdentity.get("bindingGeneration"))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } try { @SuppressWarnings("unchecked") @@ -294,14 +292,14 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { ManagedCsiProtocol.validateAcknowledgementRequest(request, originalBoot, expectedPod); ManagedCsiProtocol.validateAcknowledgementIdentity(lease, session, boot, request, expectedCaptureIdentity); } catch (RuntimeException failure) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return delegate.acknowledgeCsi(lease, session, boot, expectedPod, request, expectedCaptureIdentity); } private void requireOwnedWorkspace(RuntimeLease lease, RuntimeSession session) { if (!managed(session)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Context context = context(lease, session, true); ownership.assertHeld(context.binding(), context.session()); @@ -309,7 +307,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { private void requireOriginalRuntime(RuntimeLease lease, RuntimeSession session) { if (!managed(session)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } context(lease, session, false); } @@ -318,7 +316,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { public CompletionStage installPublisher(RuntimeLease lease, RuntimeSession session, Map publisher) { if (!managed(session)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Context context = requireLocalCapture(context(lease, session, true)); ownership.assertHeld(context.binding(), context.session()); @@ -329,7 +327,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { public CompletionStage> acknowledge(RuntimeLease lease, RuntimeSession session, Map reference, Map receipt) { if (!managed(session) || !v3(reference)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } requireLocalCapture(context(lease, session, false)); return delegate.acknowledgeV3(lease, session, wireReference(reference), receipt) @@ -341,7 +339,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { Map reference, long afterSequence) { if (v3(reference)) { if (!managed(session)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } requireLocalCapture(context(lease, session, false)); return delegate.statusV3(lease, session, wireReference(reference), afterSequence) @@ -355,7 +353,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { Map reference) { if (v3(reference)) { if (!managed(session)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } requireLocalCapture(context(lease, session, false)); return delegate.cancelV3(lease, session, wireReference(reference)) @@ -369,9 +367,9 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { } private static Context requireLocalCapture(Context context) { - if (!java.util.Set.of(LocalProcessRuntimeProvisioner.KIND, com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeProvisioner.KIND) + if (!java.util.Set.of(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS) .contains(context.runtime().getRequest().getProvisionerKind())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return context; } @@ -408,14 +406,14 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { boolean extensionControl = ManagedExtensionExecutionProfile.admits(session.getScope().getCapabilityDigest()) && (ManagedMcpProtocol.isOperation(operation) || ManagedHookProtocol.isOperation(operation) || ManagedExtensionCommandProtocol.isOperation(operation)); - if (com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeProvisioner.KIND.equals(selected.runtime().getRequest().getProvisionerKind()) + if (com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS.equals(selected.runtime().getRequest().getProvisionerKind()) && !extensionControl && !java.util.Set.of("history", "raw-file-history", "workspace-context").contains(operation.get("kind"))) { throw new RuntimeBrokerException(501, "ssh_runtime_control_unsupported", "SSH Runtime supports files and foreground Shell only.", false); } } - if (JdbcCsiFilesRetirementGuard.isProfile(session.getScope())) { + if (CsiFilesRetirementProfile.isProfile(session.getScope())) { throw new RuntimeBrokerException(501, "csi_control_not_qualified", "Generic CSI runtime controls are not qualified.", false); } @@ -424,7 +422,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { || ManagedShellProtocol.isOperation(operation) || ManagedMonitorProtocol.isOperation(operation)) { if (!managed(session)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if (ManagedExtensionCommandProtocol.isOperation(operation)) { ManagedExtensionCommandProtocol.validateSession(session, operation); @@ -444,7 +442,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { Context context = context(lease, session, !recovery, ManagedHookProtocol.isOperation(operation)); if (context.runtime().getState() != RuntimeBindingRecord.State.READY && !(recovery && context.runtime().getState() == RuntimeBindingRecord.State.DRAINING)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if (recovery) { if (!ownership.isHeld(context.binding(), context.session())) { @@ -490,7 +488,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { } return delegate.release(lease, session).thenCompose(released -> { if (!Boolean.TRUE.equals(released)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return delegate.activateWorkspace(context.runtime(), context.session(), context.binding(), false); }) @@ -586,7 +584,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { var resolved = hook ? resolver.resolveHook(session.getHarnessSessionId(), session.getScope().getLifecycleAuthority()) : resolver.resolve(session.getHarnessSessionId(), session.getScope().getLifecycleAuthority()); if (!resolved.scope().equals(session.getScope())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } binding = resolved.binding(); } else { @@ -610,7 +608,7 @@ final class WorkspaceRuntimeTransport implements RuntimeTransport { || !Long.toString(binding.getWorkspaceGeneration()).equals( session.getScope().getWorkspaceGeneration()) || !sameLease(lease, runtime.getLease())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return new Context(binding, record, runtime); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ConfiguredWorkspaceRegistry.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/runtimebroker/workspace/ConfiguredWorkspaceRegistry.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ConfiguredWorkspaceRegistry.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/runtimebroker/workspace/ConfiguredWorkspaceRegistry.java index 69743ea9c76..2a45413bca0 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ConfiguredWorkspaceRegistry.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/runtimebroker/workspace/ConfiguredWorkspaceRegistry.java @@ -1,4 +1,8 @@ -package com.alibaba.qwen.code.runtimebroker.managedworkspace; +package com.alibaba.qwen.code.runtimebroker.workspace; + +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceRecord; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceRegistry; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceValues; import java.util.ArrayList; import java.util.Collection; @@ -16,7 +20,7 @@ import java.util.TreeMap; * {@link #requireSuccessorOf(ConfiguredWorkspaceRegistry)}, and swap it in. */ public final class ConfiguredWorkspaceRegistry implements WorkspaceRegistry { - static final int MAXIMUM_PAGE_SIZE = 1000; + static final int MAXIMUM_PAGE_SIZE = WorkspaceRegistry.MAXIMUM_PAGE_SIZE; // Workspace IDs are ASCII, so String order is byte order. private final Map> tenants; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/GrantedWorkspaceAccessPolicy.java b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/runtimebroker/workspace/GrantedWorkspaceAccessPolicy.java similarity index 83% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/GrantedWorkspaceAccessPolicy.java rename to packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/runtimebroker/workspace/GrantedWorkspaceAccessPolicy.java index 598ab184710..b6ae8f45184 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/GrantedWorkspaceAccessPolicy.java +++ b/packages/managed-agent-server/control-plane/src/main/java/com/alibaba/qwen/code/runtimebroker/workspace/GrantedWorkspaceAccessPolicy.java @@ -1,4 +1,10 @@ -package com.alibaba.qwen.code.runtimebroker.managedworkspace; +package com.alibaba.qwen.code.runtimebroker.workspace; + +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceAccess; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceAccessPolicy; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceActor; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceRecord; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceValues; import java.util.HashMap; import java.util.List; diff --git a/packages/managed-agent-server/docs/runtime-broker.md b/packages/managed-agent-server/docs/runtime-broker.md index 48e8837ec16..132483719d5 100644 --- a/packages/managed-agent-server/docs/runtime-broker.md +++ b/packages/managed-agent-server/docs/runtime-broker.md @@ -1,16 +1,8 @@ # Qwen Managed Runtime Broker Core -The `com.alibaba.qwen.code.runtimebroker` package defines the state and orchestration core for a -Managed Agent Runtime Broker. It contains Runtime binding, Runtime Session, -and Tool execution records; repository contracts; thread-safe in-memory -and JDBC implementations; and a framework-neutral service that composes -authoritative scope resolution, Runtime provisioning, and Runtime transport -adapters. +The `contracts` module owns `com.alibaba.qwen.code.runtimebroker` values and interfaces. The `runtime-broker` module owns the framework-neutral `runtimebroker.service.RuntimeBrokerService`. JDBC and memory implementations are in the `persistence` module; providers, HTTP clients and credential encryption are in `adapters`. The `server` module composes these implementations through the contracts, hosts HTTP endpoints, and owns resource lifecycle. -Runtime providers and their deployment protocols are in `runtimebroker.provisioning`, -HTTP adapters and wire protocols in `runtimebroker.transport`, and credential -encryption in `runtimebroker.security`. The execution and repository core stays -in the parent package; the application still builds all of them in one Maven module. +None of the four implementation modules depends on another implementation module. In particular the Broker consumes `NativeCsiExecutionRepository` capability rather than a concrete JDBC class. Memory repositories do not provide private CSI execution capability. Capability checks do not replace transactional admission, same-DataSource identity or generation fencing. The service acquires operation and dispatch leases, renews them while external work is in flight, converges idempotent Tool execution, records cancellation @@ -32,7 +24,7 @@ The embedding service still owns the worker command wiring, recovery-capable provisioners, and deployment configuration. The experimental Kubernetes provider below supports disposable scratch only. The Broker core stays independent of Spring and public Agent resources; the enclosing Managed Agent Server wires its HTTP and application adapters. -Broker sources, classpath resources and tests are built as part of `packages/managed-agent-server`, which requires JDK 21 or later and Maven 3.8.9+ (the SpotBugs plugin floor). There is no separate Broker JAR or test JAR. Install the Java SDK first, as described in the [application README](../README.md). +Broker sources, classpath resources and tests are built as part of `packages/managed-agent-server`, which requires JDK 21 or later and Maven 3.8.9+ (the SpotBugs plugin floor). The Broker has an ordinary reactor JAR; its tests live in `verification`, without a test-JAR dependency. Install the Java SDK first, as described in the [application README](../README.md). From `packages/managed-agent-server`, build and test with: diff --git a/packages/managed-agent-server/persistence/pom.xml b/packages/managed-agent-server/persistence/pom.xml new file mode 100644 index 00000000000..07d0a7c73b2 --- /dev/null +++ b/packages/managed-agent-server/persistence/pom.xml @@ -0,0 +1,92 @@ + + 4.0.0 + + com.alibaba + qwen-managed-agent-parent + 0.1.0-alpha + ../pom.xml + + qwen-managed-agent-persistence + Qwen Managed Agent Persistence + + ${project.basedir}/.. + + + + com.alibaba + qwen-managed-agent-contracts + + + org.springframework + spring-jdbc + + + org.springframework + spring-context + + + org.springframework + spring-tx + + + org.springframework + spring-web + + + jakarta.annotation + jakarta.annotation-api + + + jakarta.servlet + jakarta.servlet-api + + + org.slf4j + slf4j-api + + + com.fasterxml.jackson.core + jackson-databind + + + org.apache.commons + commons-lang3 + + + commons-codec + commons-codec + + + com.alibaba.fastjson2 + fastjson2 + + + com.networknt + json-schema-validator + + + org.flywaydb + flyway-core + true + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + com.github.spotbugs + spotbugs-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStore.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStore.java index 5a1a65e28c1..6aa0ba9ead8 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStore.java @@ -1,16 +1,16 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; -import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -39,15 +39,9 @@ import java.util.Set; * so a scanner whose lease expired under it cannot record or settle a decision. */ @Repository -public class AutomationLedgerStore { - public static final String STATE_LIVE = "live"; - public static final String STATE_RETIRED = "retired"; - public static final String OUTCOME_FIRING = "firing"; - public static final String OUTCOME_FIRED = "fired"; - public static final String OUTCOME_SKIPPED = "skipped"; - public static final String OUTCOME_MISSED = "missed"; +public class AutomationLedgerStore + implements com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore { /** A claim whose Harness answer stayed unobtainable past the bound. */ - public static final String OUTCOME_UNKNOWN = "unknown"; /** The holder's proof: the definition row under this owner and fence. */ private static final String HELD = " AND EXISTS (SELECT 1 FROM" @@ -55,70 +49,16 @@ public class AutomationLedgerStore { + " AND s.schedule_id = ? AND s.lease_owner = ? AND s.fence = ?)"; /** How a gate probe decided a fire, as the Harness answers it. */ - public static final String GATE_DELIVERED = "delivered"; - public static final String GATE_QUIET = "quiet"; - public static final String GATE_FAILED = "failed"; - public static final String GATE_BLOCKED = "blocked"; - public static final String GATE_UNKNOWN = "unknown"; /** * One definition as the scanner reads it. The gate columns mirror the * definition's gate and its recent outcomes * (docs/design/2026-10-22-managed-automation-gate.md). */ - public record ScheduleRow(String tenantId, String scheduleId, - String sessionId, String workspaceId, String actorId, - long recordRevision, long definitionRevision, - String definitionDigest, String goal, String cron, String timezone, - String sessionMode, String overlap, String catchUp, - Long catchUpLimit, boolean enabled, String state, - String blockedReason, long armedAt, Long watermarkSlot, - String leaseOwner, Long leaseUntil, long fence, long createdAt, - long updatedAt, String gate, String gateLastOutcome, - Long gateLastAt, int gateQuietSinceDelivery, int gateFailures, - String gateLastError) { - } /** One occurrence decision. */ - public record OccurrenceRow(String tenantId, String scheduleId, - String occurrenceKey, String runId, String sessionId, Long slot, - String trigger, String outcome, String reason, - long definitionRevision, long fence, int attempts, - long nextRetryAt, String lastError, long createdAt, - long updatedAt) { - /** A fresh decision of one occurrence of the definition as held. */ - public static OccurrenceRow decision(ScheduleRow definition, - String occurrenceKey, Long slot, String trigger, - String outcome, String reason, long fence, long now) { - return new OccurrenceRow(definition.tenantId(), - definition.scheduleId(), occurrenceKey, - automationRunId(definition.scheduleId(), occurrenceKey), - definition.sessionId(), slot, trigger, outcome, reason, - definition.definitionRevision(), fence, 0, 0L, null, now, - now); - } - } /** An occurrence with the task state of its run, when one committed. */ - public record OccurrenceView(OccurrenceRow occurrence, String taskState) { - } - - public record CommandRow(String tenantId, String idempotencyKey, - String actorId, String requestDigest, String scheduleId, - String resultJson) { - } - - public record ScheduleCursor(long createdAt, String scheduleId) { - } - - public record SchedulePage(List rows, boolean hasMore) { - } - - public record OccurrenceCursor(long createdAt, String occurrenceKey) { - } - - public record OccurrencePage(List rows, boolean hasMore) { - } private static final RowMapper SCHEDULE = (result, row) -> new ScheduleRow(result.getString("tenant_id"), @@ -198,14 +138,6 @@ public class AutomationLedgerStore { * managed-automation-operations.ts} makes, so both sides name the same record (design decision * 2). */ - public static String automationRunId(String scheduleId, - String occurrenceKey) { - return "arun_" + sha256(scheduleId + '\0' + occurrenceKey); - } - - public static String automationInputId(String runId) { - return runId + ":input"; - } /** * Each run's mirror task_state by its exact projection PK, in one batch keyed to the pair every @@ -230,7 +162,7 @@ public class AutomationLedgerStore { String recordKey = ManagedExtensionProjection.recordKey( identities.get(index).sessionId(), "automation_run", identities.get(index).runId()); - args.add(ManagedSessionStore.sessionScopeKey(tenantId, + args.add(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, identities.get(index).sessionId())); args.add(recordKey); runByRecordKey.put(recordKey, identities.get(index).runId()); @@ -249,10 +181,6 @@ public class AutomationLedgerStore { return byRunId; } - public static String sha256(String value) { - return DigestUtils.sha256Hex(value.getBytes(StandardCharsets.UTF_8)); - } - // --- definition mirror --- public Optional findSchedule(String tenantId, @@ -340,7 +268,7 @@ public class AutomationLedgerStore { ScheduleRow current = existing.get(); if (!Objects.equals(current.sessionId(), sessionId) || !Objects.equals(current.workspaceId(), workspaceId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, ManagedExtensionRecordStore.ERROR_REJECTED, "A schedule identity belongs to another Session."); } @@ -424,7 +352,7 @@ public class AutomationLedgerStore { } byte[] actorKey; try { - actorKey = ManagedWorkspaceRegistry.actorKey(tenantId, actorId); + actorKey = com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId); } catch (IllegalArgumentException error) { return new SchedulePage(List.of(), false); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelInstanceStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelInstanceStore.java similarity index 89% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelInstanceStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelInstanceStore.java index 2f8ffdb5d7e..238acc2b9c4 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelInstanceStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChannelInstanceStore.java @@ -13,52 +13,19 @@ import java.util.List; import java.util.Optional; /** - * H5b/H5c: the control plane's channel indexes beside the V47 serving - * tables — registered connections, the scope → Session route catalog, - * the dispatcher's claim ledger, and the bounded reads the channel service - * needs over the Session store's extension-record projection. Every row - * here is located or settled by a journal commit; none is a second copy - * of one. + * H5b/H5c: the control plane's channel indexes beside the V47 serving tables — registered + * connections, the scope → Session route catalog, the dispatcher's claim ledger, and the bounded + * reads the channel service needs over the Session store's extension-record projection. Every row + * here is located or settled by a journal commit; none is a second copy of one. */ @Repository -public class ChannelInstanceStore { +public class ChannelInstanceStore + implements com.alibaba.qwen.code.managedagent.repository.ChannelInstanceStore { /** The states a registered connection reports. */ - public static final List INSTANCE_STATES = List.of("connected", - "disconnected"); - - public record ChannelInstance(String tenantId, String channelId, - String platform, String accountId, long accountGeneration, - String state, String actorId, String workspaceId, - String cwdRelative, String policyJson, long createdAt, - long updatedAt) { - } - - public record InstanceCursor(long createdAt, String channelId) { - } - - public record InstancePage(List instances, - boolean hasMore) { - } - - public record ChannelBinding(String tenantId, String channelId, - String routeId, String sessionId, String scopeKind, - String senderId, String chatId, String threadId, - long createdAt) { - } - - public record ChannelClaim(String tenantId, String channelId, - String deliveryId, String sessionId, long claimedAt, long claimGeneration) { - } /** One delivery record row the projection still shows as owed. */ - public record PendingDelivery(String sessionId, String deliveryId, - long revision, String deliveryState, String recordResourceId) { - } /** The projection's mirror of one committed delivery, any state. */ - public record DeliveryRecordState(String deliveryState, - String recordResourceId) { - } private static final RowMapper INSTANCE = (row, index) -> new ChannelInstance(row.getString("tenant_id"), @@ -278,7 +245,7 @@ public class ChannelInstanceStore { + " AND record_id = ?", (row, index) -> new DeliveryRecordState(row.getString(1), row.getString(2)), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), deliveryId).stream().findFirst(); } @@ -300,7 +267,7 @@ public class ChannelInstanceStore { row.getString("record_id"), row.getLong("revision"), row.getString("delivery_state"), row.getString("record_resource_id")), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), limit); } @@ -312,7 +279,7 @@ public class ChannelInstanceStore { + " AND storage_kind = 'MYSQL_INLINE'" + " AND state = 'REFERENCED'", (row, index) -> row.getBytes("inline_bytes"), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, resourceId); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, resourceId); return rows.isEmpty() ? null : new String(rows.getFirst(), StandardCharsets.UTF_8); } @@ -356,16 +323,9 @@ public class ChannelInstanceStore { tenantId, channelId, deliveryId).stream().findFirst(); } - public static final String RECONCILE_OPERATION_PREFIX = "channel-reconcile:"; - - public static String reconciliationOperationId(ChannelClaim claim) { - return RECONCILE_OPERATION_PREFIX + com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( - claim.tenantId(), claim.channelId(), claim.deliveryId(), Long.toString(claim.claimGeneration())); - } - public boolean reserveReconciliation(ChannelClaim claim) { return jdbc.update("UPDATE ma_delivery SET gmt_modified = " + ManagedSchemaTime.SQL_NOW + ", reconcile_operation_id = ? WHERE delivery_key = ? AND delivery_kind = 'CHANNEL' AND tenant_id = ? AND channel_id = ? AND delivery_id = ? AND source_session_id = ? AND claimed_at = ? AND claim_generation = ? AND transport_state IN ('planned','sending','partial')", - reconciliationOperationId(claim), JdbcChannelDeliveryRepository.deliveryKey(claim.tenantId(), claim.channelId(), claim.deliveryId()), + com.alibaba.qwen.code.managedagent.repository.ChannelInstanceStore.reconciliationOperationId(claim), JdbcChannelDeliveryRepository.deliveryKey(claim.tenantId(), claim.channelId(), claim.deliveryId()), claim.tenantId(), claim.channelId(), claim.deliveryId(), claim.sessionId(), claim.claimedAt(), claim.claimGeneration()) == 1; } @@ -379,8 +339,8 @@ public class ChannelInstanceStore { + " AND delivery_kind = 'CHANNEL' FOR UPDATE", CLAIM, operationId); if (claims.size() != 1 || !tenantId.equals(claims.getFirst().tenantId()) || !sessionId.equals(claims.getFirst().sessionId()) || !deliveryId.equals(claims.getFirst().deliveryId()) - || !operationId.equals(reconciliationOperationId(claims.getFirst()))) { - throw new com.alibaba.qwen.code.managedagent.exception.ManagedAgentException(org.springframework.http.HttpStatus.CONFLICT, + || !operationId.equals(com.alibaba.qwen.code.managedagent.repository.ChannelInstanceStore.reconciliationOperationId(claims.getFirst()))) { + throw new com.alibaba.qwen.code.managedagent.exception.ManagedAgentException(409, "channel_claim_stale", "The channel delivery claim changed before reconciliation committed."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildResultRelayStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildResultRelayStore.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildResultRelayStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildResultRelayStore.java index bb8e5038a52..7d35aa8b014 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildResultRelayStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildResultRelayStore.java @@ -1,9 +1,12 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; @@ -23,60 +26,24 @@ import java.util.Map; import java.util.Set; /** - * H4b: the JDBC side of the child result relay — its V54 ledger plus the - * small reads the worker needs (pending child-agent rows over the - * delivery-pending index, launch bodies and envelopes from the Session - * resource store, the child turn line, and the terminal result content). - * The ledger's classifications (`orphaned`, `unknown`) are terminal: they - * never represent consumption and never authorize re-execution, surviving - * worker restarts by construction. + * H4b: the JDBC side of the child result relay — its V54 ledger plus the small reads the worker + * needs (pending child-agent rows over the delivery-pending index, launch bodies and envelopes from + * the Session resource store, the child turn line, and the terminal result content). The ledger's + * classifications (`orphaned`, `unknown`) are terminal: they never represent consumption and never + * authorize re-execution, surviving worker restarts by construction. */ @Repository -public class ChildResultRelayStore { +public class ChildResultRelayStore + implements com.alibaba.qwen.code.managedagent.repository.ChildResultRelayStore { /** One child run the delivery-pending index surfaced. */ - public record PendingChild(String tenantId, String parentSessionId, - String childRunId, long revision, String deliveryState, - String recordResourceId) { - } /** One relay ledger row. */ - public record RelayRow(String tenantId, String parentSessionId, - String childRunId, String creationKey, String childSessionId, - String state, String claimedBy, long claimedUntil, int attempts, - long nextRetryAt, String lastError, long createdAt, - long updatedAt, long claimGeneration, String operationKind) { - public RelayRow(String tenantId, String parentSessionId, String childRunId, String creationKey, - String childSessionId, String state, String claimedBy, long claimedUntil, int attempts, - long nextRetryAt, String lastError, long createdAt, long updatedAt) { - this(tenantId, parentSessionId, childRunId, creationKey, childSessionId, state, claimedBy, - claimedUntil, attempts, nextRetryAt, lastError, createdAt, updatedAt, 0, "CHILD_SESSION_START"); - } - } /** A child Session's Turn line (its latest, or its first), as the * relays read it — * with the G3 dispatch pair on board: a Turn whose admission never * landed (no submission mark, no harness epoch) is a pre-admission * failure, never proof that a Runtime binding ever existed. */ - public record TurnLine(String turnId, String status, Long completedAt, - String errorCode, boolean submissionAttempted, - String harnessEventEpoch) { - /** Whether this Turn proves a dispatch went out — the same pair - * the G3 submission machinery guards with. */ - public boolean dispatched() { - return submissionAttempted || harnessEventEpoch != null; - } - - /** Whether this Turn is the terminal pre-admission failure: an - * undispatched, terminally failed (or cancelled) admission. A - * live enqueued ACCEPTED Turn shares `!dispatched()` with it — - * only the terminal half may ever settle a never-started - * verdict (R24's runnable-Turn guard). */ - public boolean preAdmissionTerminal() { - return ("FAILED".equals(status) || "CANCELLED".equals(status)) - && !dispatched(); - } - } /** * The child Session kinds — the {@code task_kind} values the relay @@ -163,7 +130,7 @@ public class ChildResultRelayStore { + " WHERE session_scope_key = ? AND tenant_id = ? AND session_id = ?" + " AND domain = 'child_run' AND record_id = ?", (result, row) -> result.getString("delivery_state"), - ManagedSessionStore.sessionScopeKey(tenantId, parentSessionId), tenantId, parentSessionId, childRunId); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, parentSessionId), tenantId, parentSessionId, childRunId); return rows.isEmpty() ? null : rows.getFirst(); } @@ -183,7 +150,7 @@ public class ChildResultRelayStore { + " WHERE session_scope_key = ? AND tenant_id = ? AND session_id = ?" + " AND domain = 'child_run' AND record_id = ?", (result, row) -> result.getString("record_resource_id"), - ManagedSessionStore.sessionScopeKey(tenantId, parentSessionId), tenantId, parentSessionId, childRunId); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, parentSessionId), tenantId, parentSessionId, childRunId); if (rows.isEmpty()) { return null; } @@ -205,8 +172,6 @@ public class ChildResultRelayStore { /** The run's stop request and whether its run line has ended, as * the latest committed body states them (H4f). */ - public record StopState(boolean stopRequested, boolean ended) { - } /** The committed stop state of one child run, or null when no record * row exists; an unreadable body owes the caller a bounded retry. */ @@ -220,7 +185,7 @@ public class ChildResultRelayStore { + " AND tenant_id = ? AND session_id = ?" + " AND domain = 'child_run' AND record_id = ?", (result, row) -> result.getString("record_resource_id"), - ManagedSessionStore.sessionScopeKey(tenantId, + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, parentSessionId), ManagedExtensionProjection.recordKey(parentSessionId, "child_run", childRunId), @@ -269,7 +234,7 @@ public class ChildResultRelayStore { } public byte[] readResourceBytes(String tenantId, String sessionId, String resourceId) { - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); return jdbc.query("SELECT inline_bytes FROM ma_resource WHERE resource_key = ? AND session_scope_key = ?" + " AND tenant_id = ? AND session_id = ? AND resource_id = ? AND storage_kind = 'MYSQL_INLINE'" + " AND state = 'REFERENCED' AND deletion_operation_key IS NULL", @@ -294,9 +259,6 @@ public class ChildResultRelayStore { /** The newest settled Turn of a Session's own journal, with the * source of the input that started it. */ - public record SettledTurn(String turnId, String outcome, String source, - long settledAt) { - } /** * What a child Session's journal proves about the turns that decide its @@ -306,17 +268,10 @@ public class ChildResultRelayStore { * while none settled). Other wake inputs — a Monitor's, an * automation's — neither hold nor decide. */ - public record JournalTurns(int pendingMessageInputs, - Set messageInputs, long lastActivityAt, - SettledTurn lastSettled) { - } /** The messages on one parent–child edge, as committed now: * `receivedToChild` names the input of each of the parent's messages * to this run its target already received. */ - public record EdgeMessages(int undelivered, int toChild, - List receivedToChild) { - } private static final String MESSAGES_SQL = "SELECT delivery_state, record_resource_id" @@ -357,7 +312,7 @@ public class ChildResultRelayStore { int toChild = 0; List received = new ArrayList<>(); for (Map row : jdbc.query(MESSAGES_SQL, (schemaRow, schemaIndex) -> ManagedSchemaRows.read(schemaRow), - ManagedSessionStore.sessionScopeKey(tenantId, parentSessionId), tenantId, parentSessionId)) { + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, parentSessionId), tenantId, parentSessionId)) { JsonNode body = body(tenantId, parentSessionId, row); if (!"outbound".equals(body.path("direction").asText()) || !"to_child".equals(body.path("route").asText()) @@ -388,7 +343,7 @@ public class ChildResultRelayStore { return 0; } Integer owed = jdbc.queryForObject(CHILD_OUTBOX_OWED_SQL, - Integer.class, ManagedSessionStore.sessionScopeKey(tenantId, childSessionId), tenantId, childSessionId); + Integer.class, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, childSessionId), tenantId, childSessionId); return owed == null ? 0 : owed; } @@ -414,7 +369,7 @@ public class ChildResultRelayStore { + " ma_session_record" + " WHERE session_scope_key = ? AND tenant_id = ? AND session_id = ?" + " AND domain = 'session_message' LIMIT 1", - (result, row) -> result.getString("record_key"), ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, + (result, row) -> result.getString("record_key"), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId).isEmpty(); } @@ -517,7 +472,7 @@ public class ChildResultRelayStore { + " WHERE session_scope_key = ? AND tenant_id = ? AND session_id = ?" + " AND domain = 'child_run' AND record_id = ?", (result, row) -> result.getString("record_resource_id"), - ManagedSessionStore.sessionScopeKey(tenantId, parentSessionId), tenantId, parentSessionId, childRunId); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, parentSessionId), tenantId, parentSessionId, childRunId); return rows.isEmpty() ? null : readTree(readResource(tenantId, parentSessionId, rows.getFirst())); } @@ -589,8 +544,6 @@ public class ChildResultRelayStore { } /** One non-terminal child run a closing Session still owns. */ - public record LiveScope(String childRunId, String recordResourceId) { - } /** * The non-terminal child runs of one Session, for the close cascade: @@ -613,7 +566,7 @@ public class ChildResultRelayStore { (result, row) -> new LiveScope( result.getString("record_id"), result.getString("record_resource_id")), - ManagedSessionStore.sessionScopeKey(tenantId, parentSessionId), tenantId, parentSessionId); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, parentSessionId), tenantId, parentSessionId); } /** Whether a Session currently holds the acceptance of one child run. */ @@ -624,7 +577,7 @@ public class ChildResultRelayStore { + " WHERE session_scope_key = ? AND tenant_id = ? AND session_id = ?" + " AND domain = 'child_acceptance'" + " AND record_id = ?", - (result, row) -> result.getString("record_key"), ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, + (result, row) -> result.getString("record_key"), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, childRunId).isEmpty(); } @@ -694,7 +647,7 @@ public class ChildResultRelayStore { throw new IllegalStateException("Child message body resource is unavailable or corrupt"); } return new String(bytes, StandardCharsets.UTF_8); - }, ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, turnId); + }, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, turnId); if (messages.isEmpty()) { return null; } @@ -719,7 +672,7 @@ public class ChildResultRelayStore { } private static String scope(String tenant, String parent) { - return ManagedSessionStore.sessionScopeKey(tenant, parent); + return com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, parent); } private static String operationKey(String kind, String tenant, String parent, String run) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStore.java similarity index 85% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStore.java index 3760b4987c8..71ae6d3b6ea 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStore.java @@ -5,16 +5,13 @@ import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionTemplate; -import java.nio.charset.StandardCharsets; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; @@ -24,34 +21,22 @@ import java.util.Map; import java.util.Set; @Repository -public class ChildWorkspaceStore { - public static final String PREPARING = "preparing"; - public static final String READY = "ready"; - public static final String MERGING = "merging"; - public static final String APPLYING = "applying"; +public class ChildWorkspaceStore + implements com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore { /** The merge is in the parent's working tree; only the worktree and the pins remain. */ - public static final String APPLIED = "applied"; - public static final String MERGED = "merged"; - public static final String CONFLICTED = "conflicted"; - public static final String DISCARDING = "discarding"; - public static final String DISCARDED = "discarded"; - public static final String FAILED = "failed"; - public static final String BLOCKED = "blocked"; - public static final String MERGE = "merge"; - public static final String DISCARD = "discard"; + /** * A discard that replaced a merge before the child's result was pinned: * the discard pins it first. Recorded by {@link #requestFinish}, never * asked for. */ - public static final String DISCARD_PINNED = "discard_pinned"; + /** A child Workspace that merges back when its child completes (I2). */ - public static final String WORKTREE = "worktree"; + /** * A read-only child Workspace (#13753 I3): its child reads a frozen copy * and every finish is a discard. */ - public static final String SNAPSHOT = "snapshot"; private static final Set SETTLED_SESSION_STATES = Set.of("CLOSED", "ARCHIVED", "DELETED"); /** The columns a transition may set besides the state; anything else is refused. */ @@ -63,18 +48,6 @@ public class ChildWorkspaceStore { private static final WorkspaceOperations.Kind KIND = WorkspaceOperations.Kind.CHILD_WORKSPACE; /** One child Workspace row. */ - public record Row(String tenantId, String parentSessionId, String childRunId, - String childWorkspaceId, String workspaceId, long workspaceGeneration, String storageId, - String parentCwdRelative, String repositoryRelative, String childCwdRelative, - String baseCommit, String resultCommit, String parentTree, String mergedTree, - String state, String finishRequest, String outcomeCode, List conflictPaths, - String lastError, String claimedBy, Long claimedUntil, long claimGeneration, - int attempts, long nextRetryAt, long createdAt, long updatedAt, String workspaceMode) { - /** Whether the row is a read-only snapshot, whose every finish is a discard. */ - public boolean snapshot() { - return SNAPSHOT.equals(workspaceMode); - } - } private final JdbcTemplate jdbc; private final TransactionTemplate transaction; @@ -88,15 +61,6 @@ public class ChildWorkspaceStore { * The id that names a child Workspace's directory and pins: the first * 32 hex digits of sha256(tenant NUL parent NUL childRunId). */ - public static String childWorkspaceId(String tenantId, String parentSessionId, String childRunId) { - try { - return DigestUtils.sha256Hex( - (tenantId + "\u0000" + parentSessionId + "\u0000" + childRunId) - .getBytes(StandardCharsets.UTF_8)).substring(0, 32); - } catch (IllegalArgumentException error) { - throw new IllegalStateException("SHA-256 is unavailable", error); - } - } /** Admits a worktree, the mode I1 and I2 prepare. */ public Row admit(String tenantId, String parentSessionId, String childRunId, @@ -121,10 +85,10 @@ public class ChildWorkspaceStore { var storages = jdbc.queryForList("SELECT storage_id FROM ma_workspace WHERE tenant_id = ? AND workspace_id = ? FOR UPDATE", String.class, tenantId, parent.getWorkspaceId()); if (storages.size() != 1 || !parent.getStorageId().equals(storages.getFirst())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } WorkspaceStorageKindGuard.requireLocalAlias(jdbc, tenantId, storages.getFirst()); - String childId = childWorkspaceId(tenantId, parentSessionId, childRunId); + String childId = com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.childWorkspaceId(tenantId, parentSessionId, childRunId); jdbc.update("INSERT IGNORE INTO ma_workspace (tenant_id, workspace_id, workspace_generation, storage_id," + " display_name, config_ref, policy_ref, state, workspace_kind, parent_workspace_id, parent_session_id," + " child_run_id, child_workspace_id, parent_cwd_relative, child_state, gmt_create, gmt_modified)" @@ -147,11 +111,11 @@ public class ChildWorkspaceStore { } Row row = find(tenantId, parentSessionId, childRunId); if (row == null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_workspace_conflict", + throw new ManagedAgentException(409, "child_workspace_conflict", "The child Workspace is unavailable."); } if (!workspaceMode.equals(row.workspaceMode())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_workspace_conflict", + throw new ManagedAgentException(409, "child_workspace_conflict", "The child Workspace was admitted for another mode."); } return row; @@ -248,7 +212,7 @@ public class ChildWorkspaceStore { } Row current = find(claimed.tenantId(), claimed.parentSessionId(), claimed.childRunId()); return current != null && finish.equals(current.finishRequest()) - && transition(claimed, owner, toState, columns("last_error", null), now); + && transition(claimed, owner, toState, com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.columns("last_error", null), now); }); } @@ -313,7 +277,7 @@ public class ChildWorkspaceStore { List rows = jdbc.query(SELECT + " WHERE w.tenant_id = ? AND w.parent_session_id = ?" + " AND w.child_run_id = ? FOR UPDATE", this::row, tenantId, parentSessionId, childRunId); if (rows.isEmpty()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "child_workspace_not_found", + throw new ManagedAgentException(404, "child_workspace_not_found", "The child run has no child Workspace."); } Row row = rows.getFirst(); @@ -325,16 +289,16 @@ public class ChildWorkspaceStore { String recorded = asked; if (MERGE.equals(asked)) { if (row.finishRequest() != null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_workspace_conflict", + throw new ManagedAgentException(409, "child_workspace_conflict", "The child Workspace is already being discarded."); } if (!READY.equals(row.state())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_workspace_not_ready", + throw new ManagedAgentException(409, "child_workspace_not_ready", "Only a ready child Workspace can be merged."); } } else { if (MERGED.equals(row.state())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_workspace_conflict", + throw new ManagedAgentException(409, "child_workspace_conflict", "The child Workspace was already merged."); } if (DISCARDED.equals(row.state())) { @@ -343,7 +307,7 @@ public class ChildWorkspaceStore { // A merge not yet started (the row is still ready) yields to // the discard; one that started runs to its end. if (MERGING.equals(row.state()) || APPLYING.equals(row.state()) || APPLIED.equals(row.state())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_workspace_finishing", + throw new ManagedAgentException(409, "child_workspace_finishing", "The child Workspace is being merged."); } if (MERGE.equals(row.finishRequest()) && row.resultCommit() == null) { @@ -423,11 +387,4 @@ public class ChildWorkspaceStore { } /** The columns a transition sets, in order. */ - public static Map columns(Object... pairs) { - Map columns = new LinkedHashMap<>(); - for (int index = 0; index + 1 < pairs.length; index += 2) { - columns.put((String) pairs[index], pairs[index + 1]); - } - return columns; - } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationStore.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationStore.java index 3772045e4e4..af5d4f7e469 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationStore.java @@ -1,12 +1,13 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ExternalStorageRegistration; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalStorageRegistry; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.spi.ExternalStorageRegistry; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Qualifier; @@ -205,7 +206,7 @@ public final class ExternalStorageRegistrationStore implements ExternalStorageRe try { return json.writeValueAsString(value); } catch (JsonProcessingException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -213,7 +214,7 @@ public final class ExternalStorageRegistrationStore implements ExternalStorageRe try { return json.readValue(value, type); } catch (JsonProcessingException | IllegalArgumentException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStore.java similarity index 90% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStore.java index b70c4e548fd..794806cb0d9 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStore.java @@ -1,14 +1,16 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedHarnessReleaseRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -22,8 +24,8 @@ import java.util.Objects; /** Durable ownership; an endpoint disappearing never authorizes another writer. */ @Repository @Qualifier("managedAgentTransactionManager") -public class HarnessPlacementStore { - public static final String RELEASE_DOMAIN = "hosted_harness_release"; +public class HarnessPlacementStore + implements com.alibaba.qwen.code.managedagent.repository.HarnessPlacementStore { private static final String COLUMNS = "tenant_id, session_id, workspace_id, harness_instance_id, " + "harness_owner_boot_id, harness_placement_epoch, harness_placement_state"; private final JdbcTemplate jdbc; @@ -32,10 +34,6 @@ public class HarnessPlacementStore { this.jdbc = jdbc; } - public record Placement(String tenantId, String sessionId, String workspaceId, - String instanceId, String bootId, long epoch, String state) { - } - public Placement get(String tenant, String session) { var rows = placements(" WHERE tenant_id = ? AND session_id = ?", tenant, session); return rows.isEmpty() || rows.getFirst().instanceId() == null ? null : rows.getFirst(); @@ -53,7 +51,7 @@ public class HarnessPlacementStore { } public Map releaseReceipt(String tenant, String session, long epoch, String operationId) { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String key = ManagedExtensionProjection.recordKey(session, RELEASE_DOMAIN, releaseId(epoch)); var saved = jdbc.query("SELECT b.inline_bytes, b.sha256 FROM ma_session_record r JOIN ma_resource b" + " ON b.session_scope_key = r.session_scope_key AND b.resource_id = r.record_resource_id" @@ -67,7 +65,7 @@ public class HarnessPlacementStore { if (!DigestUtils.sha256Hex(bytes).equals(row.getString(2))) { throw blocked("harness_release_receipt_corrupt"); } - return ManagedExtensionRecordStore.parse(new String(bytes, StandardCharsets.UTF_8)); + return com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(new String(bytes, StandardCharsets.UTF_8)); }, scope, key, tenant, session, "managed-hosted_harness_release"); if (saved.isEmpty()) { return null; @@ -204,13 +202,13 @@ public class HarnessPlacementStore { } static String releaseId(long epoch) { - return "placement:" + epoch; + return com.alibaba.qwen.code.managedagent.repository.HarnessPlacementStore.releaseId(epoch); } static boolean released(JdbcTemplate jdbc, String tenant, String session, long epoch) { return !jdbc.query("SELECT record_id FROM ma_session_record WHERE session_scope_key = ?" + " AND record_key = ? AND tenant_id = ? AND session_id = ? AND domain = ? AND record_kind = 'DOMAIN'", - (row, index) -> row.getString(1), ManagedSessionStore.sessionScopeKey(tenant, session), + (row, index) -> row.getString(1), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), ManagedExtensionProjection.recordKey(session, RELEASE_DOMAIN, releaseId(epoch)), tenant, session, RELEASE_DOMAIN).isEmpty(); } @@ -244,7 +242,7 @@ public class HarnessPlacementStore { if (!List.of("CLOSE", "ARCHIVE", "DELETE").contains(operation.kind().name()) || operation.lifecycleProtocolVersion() < 0 || operation.lifecycleProtocolVersion() > 2 || operation.leaseOwner() == null) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_claim_fenced"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_claim_fenced"); } var rows = jdbc.query("SELECT o.claim_generation, o.claim_until, o.claim_owner, o.delivery_state," + " o.lifecycle_protocol_version, f.phase, f.operation_id FROM ma_operation o JOIN ma_session_head f" @@ -257,7 +255,7 @@ public class HarnessPlacementStore { JdbcRuntimeBindingRepository.harnessDrainKey(operation.tenantId()), JdbcRuntimeBindingRepository.harnessDrainKey(operation.sessionId())); if (rows.size() != 1) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_claim_fenced"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_claim_fenced"); } Object[] row = rows.getFirst(); if (!Objects.equals(row[0], operation.claimGeneration()) || (Long) row[1] <= now(jdbc) @@ -266,14 +264,14 @@ public class HarnessPlacementStore { || !(operation.lifecycleProtocolVersion() == 0 ? "DRAINING".equals(row[5]) && row[6] == null : List.of("DRAINING", "LIFECYCLE_ONLY").contains(row[5]) && operation.operationId().equals(row[6]))) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_claim_fenced"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_claim_fenced"); } } private Placement lock(String tenant, String session) { var rows = placements(" WHERE tenant_id = ? AND session_id = ? FOR UPDATE", tenant, session); if (rows.isEmpty()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", "The Session does not exist."); + throw new ManagedAgentException(404, "session_not_found", "The Session does not exist."); } return rows.getFirst(); } @@ -301,6 +299,6 @@ public class HarnessPlacementStore { } static ManagedAgentException blocked(String code) { - return new ManagedAgentException(HttpStatus.CONFLICT, code, "The Harness ownership boundary refused this operation."); + return new ManagedAgentException(409, code, "The Harness ownership boundary refused this operation."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStore.java similarity index 87% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStore.java index aefb6f154bd..149dc0bf425 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStore.java @@ -29,23 +29,9 @@ import java.util.concurrent.ConcurrentHashMap; * the cursor back to the start once a page comes up short. */ @Component -public class HostedCleanupDebtStore { +public class HostedCleanupDebtStore implements com.alibaba.qwen.code.managedagent.repository.HostedCleanupDebtStore { private static final Logger LOG = LoggerFactory.getLogger(HostedCleanupDebtStore.class); - public record CleanupDebt(String tenantId, String workspaceId, String sessionId, - String ownerKey, String promptId) { - } - - /** - * One advancing page. {@code nextCreate}/{@code nextOwnerKey} name the last - * raw row read (valid or not) so the next call resumes after it; - * {@code exhausted} is true when the query returned fewer rows than the - * limit, meaning the cursor has reached the end and should wrap. - */ - public record ScanPage(List debts, long nextCreate, String nextOwnerKey, - boolean exhausted) { - } - private static final String OWED = "SELECT r.tenant_id, r.workspace_id, r.session_id, r.owner_key, r.gmt_create," + " m.inline_bytes FROM ma_resource_ref r JOIN ma_resource m" @@ -96,7 +82,7 @@ public class HostedCleanupDebtStore { private String promptId(String tenant, String workspace, String session, String owner, byte[] bytes) { JsonNode body = bytes == null ? null - : ManagedExtensionRecordStore.parse(new String(bytes, StandardCharsets.UTF_8)); + : com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(new String(bytes, StandardCharsets.UTF_8)); JsonNode key = body == null ? null : body.path("sessionKey"); String promptId = body == null ? null : body.path("promptId").asText(null); if (body == null || body.path("v").asInt() != 1 || promptId == null || key == null diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupReferences.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupReferences.java similarity index 81% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupReferences.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupReferences.java index 4a0695904e6..a064642291c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupReferences.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupReferences.java @@ -1,10 +1,9 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredResource; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import java.nio.charset.StandardCharsets; @@ -20,24 +19,24 @@ final class HostedCleanupReferences { static void apply(JdbcTemplate jdbc, String tenant, String workspace, String session, JsonNode payload, Function resources, long revision, long now) { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String owner = ManagedSchemaKeys.key("hosted-cleanup", payload.path("cleanupId").textValue()); String resourceId = payload.path("descriptorRef").path("resourceId").textValue(); StoredResource resource = resources.apply(resourceId); - JsonNode descriptor = ManagedExtensionRecordStore.parse(new String(resource.bytes(), StandardCharsets.UTF_8)); + JsonNode descriptor = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(new String(resource.bytes(), StandardCharsets.UTF_8)); if (descriptor == null) { throw new IllegalStateException("Validated cleanup descriptor is unreadable"); } String binding = bindingKey(tenant, workspace, descriptor.path("bindingId").textValue()); long generation = Long.parseLong(descriptor.path("generation").textValue()); - String key = ManagedSessionStore.resourceKey(scope, resourceId); + String key = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, resourceId); var previous = jdbc.queryForList("SELECT resource_key, runtime_binding_key, owner_revision" + " FROM ma_resource_ref WHERE session_scope_key = ? AND owner_kind = 'HOSTED_CLEANUP'" + " AND owner_key = ? FOR UPDATE", scope, owner); for (var row : previous) { if (!key.equals(row.get("resource_key")) || !binding.equals(row.get("runtime_binding_key")) || generation != ((Number) row.get("owner_revision")).longValue()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "managed_session_cleanup_conflict", + throw new ManagedAgentException(409, "managed_session_cleanup_conflict", "Cleanup identity changed its original Runtime descriptor"); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelDeliveryRepository.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelDeliveryRepository.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelDeliveryRepository.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelDeliveryRepository.java index 96dc820c617..79b59f1b78d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelDeliveryRepository.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelDeliveryRepository.java @@ -1,6 +1,8 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelStoreSupport; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.dao.DuplicateKeyException; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelRouteRepository.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelRouteRepository.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelRouteRepository.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelRouteRepository.java index 72b7957b665..e49dbc91e89 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelRouteRepository.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcChannelRouteRepository.java @@ -1,10 +1,12 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelRouteRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelStoreSupport; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.jdbc.core.JdbcTemplate; @@ -70,7 +72,7 @@ public final class JdbcChannelRouteRepository implements ChannelRouteRepository jdbc.update("INSERT INTO ma_operation (operation_key, session_scope_key, scope_key, operation_kind, operation_id, tenant_id," + " session_id, target_kind, target_key, request_digest, request_json, result_json, phase, state," + " gmt_create, gmt_modified) VALUES (?, ?, ?, 'CHANNEL_INGRESS', ?, ?, ?, 'SESSION', ?, ?, ?, ?, ?, ?, ?, ?)", - operation, ManagedSessionStore.sessionScopeKey(candidate.tenantId(), candidate.sessionId()), scope, route, candidate.tenantId(), candidate.sessionId(), target, digest, request, + operation, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(candidate.tenantId(), candidate.sessionId()), scope, route, candidate.tenantId(), candidate.sessionId(), target, digest, request, candidate.inputId() == null ? null : JSON.createObjectNode().put("inputId", candidate.inputId()).toString(), "admitted".equals(candidate.state()) ? "ADMITTED" : "STAGING", candidate.state(), now, now); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageService.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcExtensionPackageRepository.java similarity index 53% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageService.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcExtensionPackageRepository.java index b492a60d633..7bd0648f388 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageService.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcExtensionPackageRepository.java @@ -1,148 +1,52 @@ -package com.alibaba.qwen.code.managedagent.extensions; +package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Manifest; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Operation; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Package; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Pin; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Settings; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.SettingsCommand; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.Catalog; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.Manifest; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.Operation; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.Package; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.Pin; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.Settings; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.SettingsCommand; -import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.managedagent.repository.ExtensionPackageRepository; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceAccess; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.security.ProtectedSecret; import com.alibaba.qwen.code.runtimebroker.security.SecretProtector; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Service; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionTemplate; import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; import java.util.ArrayList; -import java.util.Base64; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.TreeMap; -import java.util.UUID; -@Service -public class ManagedExtensionPackageService { +@org.springframework.stereotype.Repository +public class JdbcExtensionPackageRepository implements ExtensionPackageRepository { private static final TypeReference> SETTINGS = new TypeReference<>() { }; private static final TypeReference> KEYS = new TypeReference<>() { }; private final JdbcTemplate jdbc; private final TransactionTemplate transaction; - private final ManagedWorkspaceRegistry workspaces; - private final ManagedAgentProperties properties; - private final ManagedExtensionArchiveStore archives; private final ObjectMapper json; private final SecretProtector secrets; - - public ManagedExtensionPackageService(@Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, @Qualifier("managedAgentTransactionManager") PlatformTransactionManager manager, - ManagedWorkspaceRegistry workspaces, ManagedAgentProperties properties, - ManagedExtensionArchiveStore archives, @Qualifier("managedAgentObjectMapper") ObjectMapper json, ObjectProvider secrets) { + public JdbcExtensionPackageRepository(@Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, + @Qualifier("managedAgentTransactionManager") PlatformTransactionManager manager, + @Qualifier("managedAgentObjectMapper") ObjectMapper json, ObjectProvider secrets) { this.jdbc = jdbc; this.transaction = new TransactionTemplate(manager); - this.workspaces = workspaces; - this.properties = properties; - this.archives = archives; this.json = json; this.secrets = secrets.getIfAvailable(); } - - public Catalog list(String tenant, String actor, String workspace) { - requireAccess(tenant, actor, workspace, false); - var packages = jdbc.query("SELECT p.extension_id, p.package_revision, p.package_digest, p.manifest_json, p.byte_length" - + " FROM ma_extension_package p WHERE p.scope_key = ? AND p.state = 'PUBLISHED'" - + " AND p.package_revision = (SELECT MAX(v.package_revision) FROM ma_extension_package v" - + " WHERE v.scope_key = p.scope_key AND v.extension_id = p.extension_id AND v.state = 'PUBLISHED')" - + " ORDER BY extension_id, package_revision DESC LIMIT 257", - (row, index) -> new Package(new Pin(row.getString(1), row.getLong(2), row.getString(3), null), - decode(row.getString(4), Manifest.class), row.getLong(5)), scope(tenant, workspace)); - if (packages.size() > 256) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "extension_catalog_too_large", - "The Workspace extension catalog exceeds 256 extensions."); - } - return new Catalog(properties.getExtensions().isEnabled() && archives.configured(), packages); - } - - public void requireExecutionAccess(String tenant, String actor, String workspace) { - requireAccess(tenant, actor, workspace, true); - } - - public void requireSelection(String tenant, String actor, String workspace, List pins) { - requireExecutionAccess(tenant, actor, workspace); - if (pins == null || pins.size() > 32 || pins.stream().anyMatch(java.util.Objects::isNull) - || pins.stream().map(Pin::extensionId).distinct().count() != pins.size()) { - throw invalid(); - } - for (Pin pin : pins) { - get(tenant, workspace, pin); - if (pin.settingsRevision() != null && jdbc.queryForObject("SELECT COUNT(*) FROM ma_extension_settings WHERE settings_key = ?", - Long.class, settingsKey(tenant, workspace, pin.extensionId(), pin.settingsRevision())) != 1) { - throw missing(); - } - } - } - - public Manifest decodeManifest(String encoded) { - try { - if (encoded == null || encoded.length() > ((ManagedExtensionPackages.MAX_MANIFEST_BYTES + 2) / 3) * 4) { - throw invalid(); - } - byte[] bytes = Base64.getUrlDecoder().decode(encoded); - if (bytes.length > ManagedExtensionPackages.MAX_MANIFEST_BYTES) { - throw invalid(); - } - Manifest manifest = json.readValue(bytes, Manifest.class); - if (manifest == null || manifest.schemaVersion() != 1 || manifest.compilerVersion() != 1 - || manifest.name() == null || !manifest.name().matches("[A-Za-z0-9][A-Za-z0-9._-]{0,127}") - || manifest.version() == null || manifest.version().isBlank() || manifest.version().length() > 128 - || manifest.target() == null || manifest.target().platform() == null || manifest.target().arch() == null - || !Set.of("linux", "darwin").contains(manifest.target().platform()) - || !Set.of("x64", "arm64", "any").contains(manifest.target().arch()) - || manifest.components() == null || manifest.components().size() > ManagedExtensionPackages.COMPONENTS.size() - || manifest.components().stream().anyMatch(java.util.Objects::isNull) - || !ManagedExtensionPackages.COMPONENTS.containsAll(manifest.components()) - || Set.copyOf(manifest.components()).size() != manifest.components().size() - || (manifest.sourceType() == null) != (manifest.sourceRevision() == null) - || manifest.sourceType() != null && (!Set.of("git", "github-release", "npm", "directory", "archive", - "archive-url", "link", "snapshot").contains(manifest.sourceType()) - || manifest.sourceRevision().isBlank() || manifest.sourceRevision().length() > 256 - || manifest.sourceRevision().chars().anyMatch(Character::isISOControl))) { - throw invalid(); - } - return manifest; - } catch (IOException | IllegalArgumentException error) { - throw invalid(); - } - } - - public Operation publish(String tenant, String actor, String workspace, String extensionId, - String digest, String idempotencyKey, Manifest manifest, InputStream archive) throws IOException { - ManagedAgentService.validateIdempotencyKey(idempotencyKey); - requireDigest(digest); - String operationId = operationId(tenant, actor, workspace, "publish", idempotencyKey); - String id = extensionId == null ? "ext_" + UUID.nameUUIDFromBytes(operationId.getBytes(StandardCharsets.UTF_8)) : extensionId; - requireId(id); - String encodedManifest = encode(manifest); - String requestDigest = DigestUtils.sha256Hex(encode(List.of(id, digest, manifest))); - Operation operation = transaction.execute(status -> { - lockWorkspace(tenant, actor, workspace); - Operation existing = existingOperation(tenant, actor, workspace, operationId, requestDigest); + public Operation preparePublish(String tenant, String actor, String workspace, String extensionId, + String id, String digest, String operationId, String requestDigest, String encodedManifest, Access access) { + return transaction.execute(status -> { + lockWorkspace(tenant, actor, workspace, access); + Operation existing = existingOperation(tenant, actor, workspace, operationId, requestDigest, access); if (existing != null) { return existing; } @@ -150,7 +54,7 @@ public class ManagedExtensionPackageService { requireExtension(tenant, workspace, id); } else if (jdbc.queryForObject("SELECT COUNT(DISTINCT extension_id) FROM ma_extension_package WHERE scope_key = ?", Long.class, scope(tenant, workspace)) >= 256) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "extension_catalog_too_large", + throw new ManagedAgentException(409, "extension_catalog_too_large", "The Workspace extension catalog is limited to 256 extensions."); } long revision = nextPackageRevision(tenant, workspace, id); @@ -162,12 +66,12 @@ public class ManagedExtensionPackageService { insertOperation(tenant, actor, workspace, operationId, id, "publish", revision, digest, requestDigest, "pending"); return new Operation(operationId, id, "publish", "pending", revision, digest); }); - if ("settled".equals(operation.state())) { - return operation; - } - long length = archives.put(tenant, workspace, digest, archive); + } + + public Operation finishPublish(String tenant, String actor, String workspace, String id, + String digest, String operationId, long length, Operation operation, Access access) { return transaction.execute(status -> { - lockWorkspace(tenant, actor, workspace); + lockWorkspace(tenant, actor, workspace, access); jdbc.update("UPDATE ma_extension_package SET state = 'PUBLISHED', byte_length = ? WHERE package_key = ?", length, packageKey(tenant, workspace, id, operation.revision())); jdbc.update("UPDATE ma_extension_operation SET state = 'settled' WHERE operation_key = ?", @@ -176,73 +80,26 @@ public class ManagedExtensionPackageService { }); } - public Operation operation(String tenant, String actor, String workspace, String operationId) { - requireAccess(tenant, actor, workspace, false); - var rows = jdbc.query("SELECT extension_id, operation_kind, state, revision, package_digest" - + " FROM ma_extension_operation WHERE operation_key = ? AND actor_key = ?", - (row, index) -> new Operation(operationId, row.getString(1), row.getString(2), row.getString(3), - row.getLong(4), row.getString(5)), operationKey(tenant, workspace, operationId), actorKey(tenant, actor)); - if (rows.size() != 1) { - throw missing(); - } - return rows.getFirst(); - } - - public Package get(String tenant, String actor, String workspace, String extensionId, long revision, String digest) { - requireAccess(tenant, actor, workspace, false); - return get(tenant, workspace, new Pin(extensionId, revision, digest, null)); - } - - public Path archive(String tenant, String actor, String workspace, String extensionId, long revision, - String digest) throws IOException { - Package saved = get(tenant, actor, workspace, extensionId, revision, digest); - return archives.verify(tenant, workspace, digest, saved.byteLength()); - } - - public Settings settings(String tenant, String actor, String workspace, String extensionId) { - requireAccess(tenant, actor, workspace, false); - requireExtension(tenant, workspace, extensionId); - var rows = jdbc.query("SELECT settings_revision, configured_keys_json FROM ma_extension_settings" - + " WHERE scope_key = ? AND extension_id = ? ORDER BY settings_revision DESC LIMIT 1", - (row, index) -> new Settings(row.getLong(1), readKeys(row.getString(2))), scope(tenant, workspace), extensionId); - return rows.isEmpty() ? new Settings(0, List.of()) : rows.getFirst(); - } - public Operation saveSettings(String tenant, String actor, String workspace, String extensionId, - String idempotencyKey, SettingsCommand command) { - ManagedAgentService.validateIdempotencyKey(idempotencyKey); - requireId(extensionId); - if (command == null || command.expectedRevision() < 0 || command.expectedRevision() >= 9_007_199_254_740_991L - || command.values() == null || command.values().size() > 128 - || command.values().entrySet().stream().anyMatch(entry -> entry.getKey() == null - || !entry.getKey().matches("[A-Za-z_][A-Za-z0-9_]{0,127}") || entry.getValue() == null)) { - throw invalid(); - } - byte[] plaintext = encode(new TreeMap<>(command.values())).getBytes(StandardCharsets.UTF_8); - if (plaintext.length > ManagedExtensionPackages.MAX_SETTINGS_BYTES) { - throw new ManagedAgentException(HttpStatus.PAYLOAD_TOO_LARGE, "extension_settings_too_large", - "Extension settings exceed 8 KiB."); - } - String operationId = operationId(tenant, actor, workspace, "settings", idempotencyKey); - String requestDigest = DigestUtils.sha256Hex(encode(List.of(extensionId, command.expectedRevision()))); + String operationId, String requestDigest, SettingsCommand command, byte[] plaintext, Access access) { return transaction.execute(status -> { - lockWorkspace(tenant, actor, workspace); + lockWorkspace(tenant, actor, workspace, access); requireExtension(tenant, workspace, extensionId); - Operation existing = existingOperation(tenant, actor, workspace, operationId, requestDigest); + Operation existing = existingOperation(tenant, actor, workspace, operationId, requestDigest, access); if (existing != null) { if (!settingsValues(tenant, workspace, extensionId, existing.revision()).equals(command.values())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "idempotency_conflict", + throw new ManagedAgentException(409, "idempotency_conflict", "The extension operation key was used for another request."); } return existing; } - long current = settings(tenant, actor, workspace, extensionId).settingsRevision(); + long current = settings(tenant, actor, workspace, extensionId, access).settingsRevision(); if (command.expectedRevision() != current) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "extension_settings_revision_conflict", + throw new ManagedAgentException(409, "extension_settings_revision_conflict", "Extension settings changed. Refresh before applying settings."); } if (secrets == null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "extension_settings_unavailable", + throw new ManagedAgentException(409, "extension_settings_unavailable", "Encrypted extension settings are unavailable."); } long revision = current + 1; @@ -259,21 +116,35 @@ public class ManagedExtensionPackageService { }); } - public ResolvedPackage resolvePin(String tenant, String workspace, Pin pin) throws IOException { - Package saved = get(tenant, workspace, pin); - Map settings = pin.settingsRevision() == null ? Map.of() - : settingsValues(tenant, workspace, pin.extensionId(), pin.settingsRevision()); - return new ResolvedPackage(saved, archives.verify(tenant, workspace, pin.packageDigest(), saved.byteLength()), settings); + public Operation operation(String tenant, String actor, String workspace, String operationId, Access access) { + access.require(false); + var rows = jdbc.query("SELECT extension_id, operation_kind, state, revision, package_digest" + + " FROM ma_extension_operation WHERE operation_key = ? AND actor_key = ?", + (row, index) -> new Operation(operationId, row.getString(1), row.getString(2), row.getString(3), + row.getLong(4), row.getString(5)), operationKey(tenant, workspace, operationId), actorKey(tenant, actor)); + if (rows.size() != 1) { + throw missing(); + } + return rows.getFirst(); } - private Map settingsValues(String tenant, String workspace, String extensionId, long revision) { + public Settings settings(String tenant, String actor, String workspace, String extensionId, Access access) { + access.require(false); + requireExtension(tenant, workspace, extensionId); + var rows = jdbc.query("SELECT settings_revision, configured_keys_json FROM ma_extension_settings" + + " WHERE scope_key = ? AND extension_id = ? ORDER BY settings_revision DESC LIMIT 1", + (row, index) -> new Settings(row.getLong(1), readKeys(row.getString(2))), scope(tenant, workspace), extensionId); + return rows.isEmpty() ? new Settings(0, List.of()) : rows.getFirst(); + } + + public Map settingsValues(String tenant, String workspace, String extensionId, long revision) { var rows = jdbc.query("SELECT encryption_key_id, encrypted_values FROM ma_extension_settings WHERE settings_key = ?", (row, index) -> new ProtectedSecret(row.getString(1), row.getString(2)), settingsKey(tenant, workspace, extensionId, revision)); if (rows.size() != 1) { throw missing(); } if (secrets == null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "extension_settings_unavailable", + throw new ManagedAgentException(409, "extension_settings_unavailable", "Encrypted extension settings are unavailable."); } byte[] plaintext = secrets.unprotect(settingsContext(tenant, workspace, extensionId, revision), rows.getFirst()); @@ -284,50 +155,12 @@ public class ManagedExtensionPackageService { } } - public record ResolvedPackage(Package descriptor, Path archive, Map settings) { - @Override - public String toString() { - return "ResolvedPackage[descriptor=" + descriptor + ", settings=redacted]"; - } - } - - private Package get(String tenant, String workspace, Pin pin) { - requireId(pin.extensionId()); - requireDigest(pin.packageDigest()); - if (pin.packageRevision() < 1 || pin.packageRevision() > 9_007_199_254_740_991L - || pin.settingsRevision() != null && (pin.settingsRevision() < 1 || pin.settingsRevision() > 9_007_199_254_740_991L)) { - throw invalid(); - } - var rows = jdbc.query("SELECT manifest_json, byte_length FROM ma_extension_package WHERE package_key = ?" - + " AND package_digest = ? AND state = 'PUBLISHED'", - (row, index) -> new Package(pin, decode(row.getString(1), Manifest.class), row.getLong(2)), - packageKey(tenant, workspace, pin.extensionId(), pin.packageRevision()), pin.packageDigest()); - if (rows.size() != 1) { - throw missing(); - } - return rows.getFirst(); - } - - private void requireAccess(String tenant, String actor, String workspace, boolean write) { - WorkspaceAccess access = workspaces.accessOf(tenant, actor, workspace); - if (!access.canRead()) { - throw missing(); - } - if (write && (!access.atLeast(WorkspaceAccess.OPERATOR) - || !properties.getExtensions().isEnabled() || !archives.configured() - || !(properties.getExtensions().getAllowedActors().contains(actor) - || properties.getExtensions().getAllowedActors().contains("*")))) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, "extension_management_forbidden", - "Extension management requires a Workspace operator and deployment permission."); - } - } - - private void lockWorkspace(String tenant, String actor, String workspace) { + private void lockWorkspace(String tenant, String actor, String workspace, Access access) { var states = jdbc.queryForList("SELECT state FROM ma_workspace WHERE tenant_id = ? AND workspace_id = ? FOR UPDATE", String.class, tenant, workspace); - requireAccess(tenant, actor, workspace, true); + access.require(true); if (states.size() != 1 || !"ACTIVE".equals(states.getFirst())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "workspace_unavailable", "Workspace is not active."); + throw new ManagedAgentException(409, "workspace_unavailable", "Workspace is not active."); } } @@ -346,17 +179,17 @@ public class ManagedExtensionPackageService { return (maximum == null ? 0 : maximum) + 1; } - private Operation existingOperation(String tenant, String actor, String workspace, String operationId, String digest) { + private Operation existingOperation(String tenant, String actor, String workspace, String operationId, String digest, Access access) { var rows = jdbc.queryForList("SELECT request_digest FROM ma_extension_operation WHERE operation_key = ?", String.class, operationKey(tenant, workspace, operationId)); if (rows.isEmpty()) { return null; } if (!digest.equals(rows.getFirst())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "idempotency_conflict", + throw new ManagedAgentException(409, "idempotency_conflict", "The extension operation key was used for another request."); } - return operation(tenant, actor, workspace, operationId); + return operation(tenant, actor, workspace, operationId, access); } private void insertOperation(String tenant, String actor, String workspace, String operationId, String extensionId, @@ -393,34 +226,77 @@ public class ManagedExtensionPackageService { } private static String scope(String tenant, String workspace) { return ManagedSchemaKeys.key(tenant, workspace); } + private static String actorKey(String tenant, String actor) { return ManagedSchemaKeys.key(tenant, actor); } + private static String operationKey(String tenant, String workspace, String id) { return ManagedSchemaKeys.key(tenant, workspace, id); } + private static String packageKey(String tenant, String workspace, String id, long revision) { return ManagedSchemaKeys.key(tenant, workspace, id, Long.toString(revision)); } + private static String settingsKey(String tenant, String workspace, String id, long revision) { return ManagedSchemaKeys.key("extension-settings", tenant, workspace, id, Long.toString(revision)); } + private static String settingsContext(String tenant, String workspace, String id, long revision) { return "managed-extension-settings:" + settingsKey(tenant, workspace, id, revision); } - private static String operationId(String tenant, String actor, String workspace, String kind, String key) { - return "eop_" + ManagedSchemaKeys.key(tenant, actor, workspace, kind, key); - } + private static void requireId(String id) { if (id == null || !id.matches("ext_[a-f0-9-]{36}")) { throw invalid(); } } + private static void requireDigest(String digest) { if (digest == null || !digest.matches("[a-f0-9]{64}")) { throw invalid(); } } + private static ManagedAgentException invalid() { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", "Invalid extension package request."); + return new ManagedAgentException(400, "invalid_request", "Invalid extension package request."); } + private static ManagedAgentException missing() { - return new ManagedAgentException(HttpStatus.NOT_FOUND, "extension_package_not_found", "Extension package was not found."); + return new ManagedAgentException(404, "extension_package_not_found", "Extension package was not found."); + } + + public Package get(String tenant, String workspace, Pin pin) { + requireId(pin.extensionId()); + requireDigest(pin.packageDigest()); + if (pin.packageRevision() < 1 || pin.packageRevision() > 9_007_199_254_740_991L + || pin.settingsRevision() != null && (pin.settingsRevision() < 1 || pin.settingsRevision() > 9_007_199_254_740_991L)) { + throw invalid(); + } + var rows = jdbc.query("SELECT manifest_json, byte_length FROM ma_extension_package WHERE package_key = ?" + + " AND package_digest = ? AND state = 'PUBLISHED'", + (row, index) -> new Package(pin, decode(row.getString(1), Manifest.class), row.getLong(2)), + packageKey(tenant, workspace, pin.extensionId(), pin.packageRevision()), pin.packageDigest()); + if (rows.size() != 1) { + throw missing(); + } + return rows.getFirst(); + } + + public List list(String tenant, String workspace) { + var packages = jdbc.query("SELECT p.extension_id, p.package_revision, p.package_digest, p.manifest_json, p.byte_length" + + " FROM ma_extension_package p WHERE p.scope_key = ? AND p.state = 'PUBLISHED'" + + " AND p.package_revision = (SELECT MAX(v.package_revision) FROM ma_extension_package v" + + " WHERE v.scope_key = p.scope_key AND v.extension_id = p.extension_id AND v.state = 'PUBLISHED')" + + " ORDER BY extension_id, package_revision DESC LIMIT 257", + (row, index) -> new Package(new Pin(row.getString(1), row.getLong(2), row.getString(3), null), + decode(row.getString(4), Manifest.class), row.getLong(5)), scope(tenant, workspace)); + if (packages.size() > 256) { + throw new ManagedAgentException(409, "extension_catalog_too_large", + "The Workspace extension catalog exceeds 256 extensions."); + } + return packages; + } + + public boolean hasSettings(String tenant, String workspace, Pin pin) { + return jdbc.queryForObject("SELECT COUNT(*) FROM ma_extension_settings WHERE settings_key = ?", + Long.class, settingsKey(tenant, workspace, pin.extensionId(), pin.settingsRevision())) == 1; } } diff --git a/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcSessionRecoveryRepository.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcSessionRecoveryRepository.java new file mode 100644 index 00000000000..49f61c4791b --- /dev/null +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/JdbcSessionRecoveryRepository.java @@ -0,0 +1,30 @@ +package com.alibaba.qwen.code.managedagent.persistence; + +import com.alibaba.qwen.code.managedagent.repository.SessionRecoveryRepository; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public class JdbcSessionRecoveryRepository implements SessionRecoveryRepository { + private final JdbcTemplate jdbc; + public JdbcSessionRecoveryRepository(@Qualifier("managedJdbcTemplate") JdbcTemplate jdbc) { this.jdbc = jdbc; } + + @Override + public List heads(String tenantId, String sessionId) { + return jdbc.query("SELECT recovery_status, recovery_detail_code" + + " FROM ma_session_head WHERE tenant_id = ? AND session_id = ?" + + " AND head_kind = 'MANAGED_SESSION'", + (row, index) -> new HeadRecovery(row.getString("recovery_status"), + row.getString("recovery_detail_code")), tenantId, sessionId); + } + + @Override + public long nonInitialTurns(String tenantId, String sessionId) { + return jdbc.queryForObject("SELECT COUNT(*) FROM ma_turn WHERE tenant_id = ? AND session_id = ?" + + " AND (status <> 'CANCELLED' OR submission_attempted = TRUE OR harness_event_epoch IS NOT NULL)", + Long.class, tenantId, sessionId); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageOwners.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageOwners.java similarity index 89% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageOwners.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageOwners.java index 23e893290cc..dacb1a1933a 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageOwners.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageOwners.java @@ -1,9 +1,10 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -31,11 +32,11 @@ final class LocalStorageOwners { private LocalStorageOwners() { } - static String physicalKey(WorkspaceStorageGuard.Identity identity) { + static String physicalKey(StorageIdentityReader.Identity identity) { return ManagedSchemaKeys.key("local-storage/1", identity.hostId(), identity.device(), identity.inode(), identity.birthTime()); } - static void requireNonOverlapping(JdbcTemplate jdbc, WorkspaceStorageGuard.Identity identity) { + static void requireNonOverlapping(JdbcTemplate jdbc, StorageIdentityReader.Identity identity) { WorkspaceStorageKindGuard.lockDomain(jdbc, "local-storage-host/" + identity.hostId()); String key = physicalKey(identity); var owners = jdbc.queryForList("SELECT storage_key, mount_root FROM ma_storage_owner" @@ -63,7 +64,7 @@ final class LocalStorageOwners { var row = rows.getFirst(); if (!tenant.equals(row.get("tenant_id")) || !storage.equals(row.get("storage_id")) || !java.util.Set.of("LOCAL", "UNREGISTERED").contains(row.get("storage_kind"))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return row; } @@ -74,13 +75,13 @@ final class LocalStorageOwners { if (alias == null || !"LOCAL".equals(alias.get("storage_kind")) || !java.util.Set.of("REGISTERED", "RETIRING").contains(alias.get("registration_state")) || !(alias.get("physical_key") instanceof String physical)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } var owners = WorkspaceOperations.rows(jdbc, "SELECT storage_key, physical_key, storage_kind FROM ma_storage_owner WHERE storage_key = ?" + (lock ? " FOR UPDATE" : ""), physical); if (owners.size() != 1 || !physical.equals(owners.getFirst().get("physical_key")) || !"LOCAL".equals(owners.getFirst().get("storage_kind"))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return physical; } @@ -90,7 +91,7 @@ final class LocalStorageOwners { } var source = alias(jdbc, tenant, storage, false); if (source == null || !(source.get("physical_key") instanceof String physical)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } var aliases = jdbc.query("SELECT alias_key, tenant_id, storage_id, storage_kind FROM ma_storage WHERE physical_key = ? ORDER BY alias_key LIMIT 129 FOR UPDATE", (row, index) -> new String[] {row.getString("alias_key"), row.getString("tenant_id"), row.getString("storage_id"), row.getString("storage_kind")}, physical); @@ -102,11 +103,11 @@ final class LocalStorageOwners { for (var candidate : aliases) { if (!candidate[0].equals(JdbcRuntimeBindingRepository.storageAliasKey(candidate[1], candidate[2])) || !"LOCAL".equals(candidate[3])) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } if (!physical.equals(key(jdbc, tenant, storage))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } // The caller may already hold a RR snapshot. Admissions wait on the locked aliases; // an independent, nonlocking read sees committed authorities without reversing binding locks. diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStore.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStore.java index ae8ee88460b..94ef166778f 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStore.java @@ -1,24 +1,23 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedHostedRecoveryRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationTarget; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredResource; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.dao.support.DataAccessUtils; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -27,7 +26,6 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -37,8 +35,8 @@ import java.util.function.Function; @Repository @Qualifier("managedAgentTransactionManager") -public class ManagedActionStore { - private static final ObjectMapper JSON = new ObjectMapper(); +public class ManagedActionStore + implements com.alibaba.qwen.code.managedagent.repository.ManagedActionStore { private final JdbcTemplate jdbc; private final AgentStateStore sessions; @@ -47,16 +45,6 @@ public class ManagedActionStore { this.sessions = sessions; } - public record Action( - String id, - String state, - JsonNode options, - String decisionReceiptId, - String decisionDigest) {} - - public record Response( - String actionId, JsonNode body, String errorCode, String decisionReceiptId) {} - public String approvalMode(String tenantId, String sessionId) { return jdbc.queryForObject( "SELECT approval_mode FROM ma_session WHERE tenant_id = ? AND session_id" @@ -71,9 +59,9 @@ public class ManagedActionStore { private static byte[] actorScopeKey(String tenantId, String actorId) { try { return actorId == null - ? null : ManagedWorkspaceRegistry.actorKey(tenantId, actorId); + ? null : com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId); } catch (IllegalArgumentException error) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, + throw new ManagedAgentException(403, "actor_scope_mismatch", "Authenticated actor scope is invalid."); } } @@ -113,14 +101,14 @@ public class ManagedActionStore { private static ManagedAgentException forbidden() { return new ManagedAgentException( - HttpStatus.FORBIDDEN, + 403, "action_forbidden", "Only the Session's owner or a Workspace operator may answer its Actions."); } private static ManagedAgentException shellForbidden() { return new ManagedAgentException( - HttpStatus.FORBIDDEN, + 403, "action_forbidden", "Only the Session's recorded owner may answer its Shell Actions."); } @@ -301,7 +289,7 @@ public class ManagedActionStore { && body.path("inputRevision").equals(options.path("inputRevision")) && body.path("policyRevision") .equals(options.path("policyRevision")) - && decided.digest().equals(decisionDigest(body))); + && decided.digest().equals(com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.decisionDigest(body))); receipt = "decision_" + ManagedExtensionProjection.recordKey( @@ -333,7 +321,7 @@ public class ManagedActionStore { } else { require(decision.isNull()); } - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); String recordKey = ManagedSchemaKeys.key(scope, "ACTION", id); if (previous == null) { jdbc.update("INSERT INTO ma_session_record (session_scope_key, record_key, tenant_id, workspace_id, session_id, record_kind, domain, record_id, action_id, revision, action_version, state, options_json, gmt_create, first_sequence) VALUES (?, ?, ?, ?, ?, 'ACTION', 'action', ?, ?, ?, ?, ?, ?, ?, ?)", @@ -369,7 +357,7 @@ public class ManagedActionStore { private void replaceActionReferences(String tenantId, String workspaceId, String sessionId, String recordKey, long revision, JsonNode optionsRef, JsonNode options, JsonNode decisionRef) { - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); List refs = new ArrayList<>(List.of(optionsRef)); if (options.has("inputRef")) { refs.add(options.get("inputRef")); @@ -414,13 +402,11 @@ public class ManagedActionStore { read(r.getString("options_json")), r.getString("decision_receipt_id"), r.getString("decision_digest")), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, id) + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, id) .stream() .findFirst(); } - public record ScopedAction(String sessionId, Action action) { } - public List listWithChildren(String tenant, String parent, Long before, String beforeSession, String beforeId, int limit) { List args = new ArrayList<>(List.of(tenant, parent, parent)); @@ -445,7 +431,7 @@ public class ManagedActionStore { public List list( String tenantId, String sessionId, Long before, String beforeId, int limit) { - List args = new ArrayList<>(List.of(ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId)); + List args = new ArrayList<>(List.of(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId)); String cursor = ""; if (before != null) { cursor = " AND (gmt_create < ? OR gmt_create = ? AND action_id < ?)"; @@ -501,14 +487,14 @@ public class ManagedActionStore { + " 'ACTION_RESPONSE' AND actor_digest = ? AND idempotency_digest = ?" + " AND idempotency_key = ?", (r, row) -> r.getString(1), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), ManagedSchemaKeys.key(sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), ManagedSchemaKeys.key(sessionId), tenantId, sessionId, actorDigest, ManagedSchemaKeys.key(key), key); if (!replay.isEmpty()) { OperationRecord existing = sessions.findOperation(tenantId, sessionId, replay.get(0)).orElseThrow(); if (!existing.requestDigest().equals(requestDigest)) { throw new ManagedAgentException( - HttpStatus.CONFLICT, + 409, "idempotency_conflict", "The idempotency key was reused with different content."); } @@ -586,7 +572,7 @@ public class ManagedActionStore { if (ownerRow.workspaceId() != null && !sessions.hasExecutionRegistryFacts(tenantId, sessionId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "workspace_unavailable", "Hosted Workspace execution is not available."); } @@ -621,7 +607,7 @@ public class ManagedActionStore { && "decided".equals(action.get().state()) && action.get().decisionDigest() != null && action.get().decisionDigest().equals( - decisionDigest(failedResponse.body()))) { + com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.decisionDigest(failedResponse.body()))) { // The heal settles the row: same gate, same reason. if (!admitted && !workspaceOperatorAdmits(tenantId, ownerRow, actorId)) { @@ -667,27 +653,27 @@ public class ManagedActionStore { // reach — whichever arm admitted the caller. if (ownerRow.workspaceId() != null && !sessions.hasExecutionRegistryFacts(tenantId, sessionId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "workspace_unavailable", + throw new ManagedAgentException(409, "workspace_unavailable", "Hosted Workspace execution is not available."); } WorkspaceMigrationAdmission.requireSessionOpen(jdbc, tenantId, sessionId); String sessionStatus = jdbc.queryForObject("SELECT status FROM ma_session" + " WHERE tenant_id = ? AND session_id = ?", String.class, tenantId, sessionId); if (!"ACTIVE".equals(sessionStatus)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "session_inactive", "The Session does not accept responses."); + throw new ManagedAgentException(409, "session_inactive", "The Session does not accept responses."); } Action action = find(tenantId, sessionId, actionId) .orElseThrow( () -> new ManagedAgentException( - HttpStatus.NOT_FOUND, + 404, "action_not_found", "The Action was not found.")); JsonNode o = action.options(); if (publicPlanExit(tenantId, sessionId, o) && (!admitted || !workspaceOperatorAdmits(tenantId, ownerRow, actorId))) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, "action_forbidden", + throw new ManagedAgentException(403, "action_forbidden", "Only the Session owner with current OPERATOR access may approve a public plan."); } if (body.size() != 3 @@ -697,23 +683,23 @@ public class ManagedActionStore { || body.path("inputRevision").asLong() != o.path("inputRevision").asLong() || !body.path("policyRevision").equals(o.path("policyRevision"))) { throw new ManagedAgentException( - HttpStatus.BAD_REQUEST, + 400, "invalid_action_response", "The response does not match the original Action."); } if (!"requested".equals(action.state())) { throw new ManagedAgentException( - HttpStatus.CONFLICT, - endedCode(action.state()), + 409, + com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.endedCode(action.state()), "The Action has already ended."); } if (now >= o.path("expiresAt").asLong()) { throw new ManagedAgentException( - HttpStatus.CONFLICT, "action_expired", "The Action has expired."); + 409, "action_expired", "The Action has expired."); } - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); String actionScope = ManagedSchemaKeys.key(scope, actionId, Long.toString(o.path("inputRevision").asLong())); - String decision = decisionDigest(body); + String decision = com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.decisionDigest(body); List reserved = jdbc.query( "SELECT request_digest, result_operation_key FROM ma_request_receipt WHERE" + " namespace = 'ACTION_DECISION' AND tenant_key = ? AND" @@ -721,7 +707,7 @@ public class ManagedActionStore { (r, row) -> new String[]{r.getString(1), r.getString(2)}, ManagedSchemaKeys.key(tenantId), actionScope); if (!reserved.isEmpty()) { if (!decision.equals(reserved.getFirst()[0])) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "action_already_resolved", "A different Action decision is already admitted."); + throw new ManagedAgentException(409, "action_already_resolved", "A different Action decision is already admitted."); } String original = jdbc.queryForObject("SELECT operation_id FROM ma_operation WHERE operation_key = ?", String.class, reserved.getFirst()[1]); @@ -792,12 +778,12 @@ public class ManagedActionStore { Response response = response(target.tenantId(), target.sessionId(), target.operationId()); Action action = find(target.tenantId(), target.sessionId(), response.actionId()).orElseThrow(); boolean matched = "decided".equals(action.state()) - && decisionDigest(response.body()).equals(action.decisionDigest()); + && com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.decisionDigest(response.body()).equals(action.decisionDigest()); jdbc.update( "UPDATE ma_operation SET state = ?, phase = 'SETTLED', admission_stage = 'HARNESS_CONFIRMED', delivery_state = 'CONFIRMED', receipt_id = ?, failure_code = ?, decision_receipt_id = ?, claim_owner = NULL, claim_until = NULL, gmt_modified = ?, completed_at = ? WHERE tenant_id = ? AND session_id = ? AND operation_id = ? AND operation_kind = 'ACTION_RESPONSE' AND delivery_state IN ('PENDING', 'LEASED')", matched ? "COMPLETED" : "FAILED", "rcpt_" + UUID.randomUUID().toString().replace("-", ""), - matched ? null : endedCode(action.state()), + matched ? null : com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.endedCode(action.state()), matched ? action.decisionReceiptId() : null, now, now, target.tenantId(), target.sessionId(), target.operationId()); } @@ -828,27 +814,6 @@ public class ManagedActionStore { op.claimGeneration()); } - public static String endedCode(String state) { - return switch (state) { - case "expired" -> "action_expired"; - case "cancelled" -> "action_cancelled"; - default -> "action_already_resolved"; - }; - } - - public static String decisionDigest(JsonNode response) { - Map decision = new LinkedHashMap<>(); - decision.put("v", 1); - decision.put("optionId", response.path("optionId").asText()); - decision.put("inputRevision", response.path("inputRevision").asLong()); - decision.put("policyRevision", response.path("policyRevision").asText()); - try { - return DigestUtils.sha256Hex(JSON.writeValueAsBytes(decision)); - } catch (Exception error) { - throw new IllegalStateException(error); - } - } - private static StoredResource resource( JsonNode ref, String kind, Function resources) { closed(ref, "resourceId", "kind", "schemaVersion", "byteLength", "digest"); @@ -888,7 +853,7 @@ public class ManagedActionStore { } private JsonNode read(String value) { - JsonNode result = ManagedExtensionRecordStore.parse(value); + JsonNode result = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(value); require(result != null); return result; } @@ -896,7 +861,7 @@ public class ManagedActionStore { private static void require(boolean valid) { if (!valid) { throw new ManagedAgentException( - HttpStatus.BAD_REQUEST, + 400, "managed_session_action_rejected", "The Action journal record is invalid."); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentDefinitionStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentDefinitionStore.java similarity index 85% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentDefinitionStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentDefinitionStore.java index 887d5b96023..59be836d389 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentDefinitionStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentDefinitionStore.java @@ -3,14 +3,13 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.dao.DuplicateKeyException; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -20,15 +19,15 @@ import java.util.Map; import java.util.Optional; /** - * Tenant-scoped AgentDefinition revisions (D8a). Revisions are append-only: - * an update adds the next revision unless its content digest equals the - * latest one, and no revision is ever rewritten. Each create or update - * command is recorded under its Idempotency-Key, so a replay returns the - * revision it produced the first time. + * Tenant-scoped AgentDefinition revisions (D8a). Revisions are append-only: an update adds the next + * revision unless its content digest equals the latest one, and no revision is ever rewritten. Each + * create or update command is recorded under its Idempotency-Key, so a replay returns the revision + * it produced the first time. */ @Repository @Qualifier("managedAgentTransactionManager") -public class ManagedAgentDefinitionStore { +public class ManagedAgentDefinitionStore + implements com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore { private static final ObjectMapper JSON = new ObjectMapper(); private final JdbcTemplate jdbc; private final RequestDigests digests; @@ -38,32 +37,11 @@ public class ManagedAgentDefinitionStore { this.digests = digests; } - public record DefinitionRevision(String agentId, long revision, - String digest, String definitionJson, long createdAt) { - } - - public record Admission(DefinitionRevision revision, boolean replayed) { - } - /** * A concurrent request committed first. The losing transaction rolls * back; the caller then reads the committed command and replays it when * it is the same request, or answers {@link #code()}. */ - public static final class ConcurrentWriteException - extends RuntimeException { - private final String code; - - public ConcurrentWriteException(String code, String message) { - super(message); - this.code = code; - } - - public String code() { - return code; - } - } - /** Creates a definition with revision 1 under a new agent ID. */ @Transactional public Admission create(String tenantId, String idempotencyKey, @@ -94,7 +72,7 @@ public class ManagedAgentDefinitionStore { return replay.get(); } DefinitionRevision latest = latest(tenantId, agentId) - .orElseThrow(ManagedAgentDefinitionStore::notFound); + .orElseThrow(com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore::notFound); DefinitionRevision result = latest; if (!latest.digest().equals(digest)) { result = new DefinitionRevision(agentId, latest.revision() + 1, @@ -115,7 +93,7 @@ public class ManagedAgentDefinitionStore { public Map pinnedContent(SessionRecord session) { DefinitionRevision revision = find(session.tenantId(), session.agentId(), Long.parseLong(session.agentRevision())) - .orElseThrow(ManagedAgentDefinitionStore::notFound); + .orElseThrow(com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore::notFound); Map content; try { content = JSON.readValue(revision.definitionJson(), @@ -126,7 +104,7 @@ public class ManagedAgentDefinitionStore { if (!revision.digest().equals(session.agentDefinitionDigest()) || !digests.digest(content) .equals("sha256:" + session.agentDefinitionDigest())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "hosted_agent_definition_conflict", "The pinned agent definition digest does not match."); } @@ -188,7 +166,7 @@ public class ManagedAgentDefinitionStore { } String[] command = commands.getFirst(); if (!command[0].equals(requestDigest)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "idempotency_conflict", "The Idempotency-Key was used with a different request."); } @@ -240,9 +218,4 @@ public class ManagedAgentDefinitionStore { private static Optional first(List rows) { return rows.isEmpty() ? Optional.empty() : Optional.of(rows.getFirst()); } - - public static ManagedAgentException notFound() { - return new ManagedAgentException(HttpStatus.NOT_FOUND, "agent_not_found", - "The agent definition was not found."); - } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStore.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStore.java index 3e1ce1f97fe..91a64b01b86 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStore.java @@ -1,11 +1,20 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.daemon.HostedPermissionRules; -import com.alibaba.qwen.code.managedagent.catalog.AgentDefinitionExtensions; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.event.CommittedEventPublisher; -import com.alibaba.qwen.code.managedagent.event.JournalEventTransport; +import com.alibaba.qwen.code.managedagent.contracts.catalog.AgentDefinitionExtensions; +import com.alibaba.qwen.code.managedagent.contracts.event.CommittedEventPublisher; +import com.alibaba.qwen.code.managedagent.contracts.event.JournalEventTransport; +import com.alibaba.qwen.code.managedagent.contracts.persistence.AgentDefinitionCompiler; +import com.alibaba.qwen.code.managedagent.contracts.persistence.EventIdentity; +import com.alibaba.qwen.code.managedagent.contracts.persistence.EventIdentity.Identity; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedConfigInstall; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRules; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellApprovalChangeResult; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellApprovalSelection; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellLifecycleOperation; @@ -44,20 +53,17 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnPage; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnSummary; -import com.alibaba.qwen.code.managedagent.persistence.EventIdentity.Identity; -import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry.ResolvedBinding; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; +import com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.ResolvedBinding; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceAccess; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; @@ -69,7 +75,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowCallbackHandler; import org.springframework.jdbc.core.RowMapper; @@ -98,6 +103,12 @@ import java.util.regex.Pattern; @Repository @Qualifier("managedAgentTransactionManager") public class ManagedAgentStore implements AgentStateStore { + private com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles worktreeFiles; + @org.springframework.beans.factory.annotation.Autowired + public void configureWorktreeFiles(com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles value) { worktreeFiles = value; } + + private final HarnessRules harnessRules; + private static final Logger LOG = LoggerFactory.getLogger( ManagedAgentStore.class); private static final TypeReference>> INPUT_TYPE = @@ -151,7 +162,7 @@ public class ManagedAgentStore implements AgentStateStore { private ManagedAgentDefinitionStore definitions; private ManagedSessionSkills creationSkills; private SessionWorktreeStore sessionWorktrees; - private com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackageService extensionPackages; + private com.alibaba.qwen.code.managedagent.contracts.extensions.ExtensionPackages extensionPackages; @org.springframework.beans.factory.annotation.Autowired(required = false) public void setSessionWorktrees(SessionWorktreeStore value) { @@ -160,16 +171,16 @@ public class ManagedAgentStore implements AgentStateStore { @Override public SessionWorktreeStore.Descriptor findSessionWorktree(String tenantId, String sessionId) { - return SessionWorktreeStore.descriptor(jdbc, tenantId, sessionId); + return SessionWorktreeStore.descriptor(worktreeFiles, jdbc, tenantId, sessionId); } @Override public Map findSessionWorktrees(String tenantId, List sessionIds) { - return SessionWorktreeStore.descriptors(jdbc, tenantId, sessionIds); + return SessionWorktreeStore.descriptors(worktreeFiles, jdbc, tenantId, sessionIds); } @org.springframework.beans.factory.annotation.Autowired - public void setExtensionPackages(com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackageService value) { + public void setExtensionPackages(com.alibaba.qwen.code.managedagent.contracts.extensions.ExtensionPackages value) { this.extensionPackages = value; } @@ -184,7 +195,7 @@ public class ManagedAgentStore implements AgentStateStore { private final boolean workspaceChildAgentsEnabled; private final boolean managedExtensionExecutionEnabled; private final long lifecycleHandoffMillis; - private final List workspaceMounts; + private final List workspaceMounts; private final ResidentWorkspaces residentWorkspaces; private final WorkspaceCreationRoots createdRoots; private JournalEventTransport journalEventTransport; @@ -338,7 +349,8 @@ public class ManagedAgentStore implements AgentStateStore { public ManagedAgentStore(@Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, @Qualifier("managedAgentObjectMapper") ObjectMapper objectMapper, @Qualifier("managedAgentClock") Clock clock, CommittedEventPublisher eventPublisher, ManagedWorkspaceRegistry workspaces, - ManagedAgentProperties properties) { + ManagedAgentSettings properties, HarnessRules harnessRules) { + this.harnessRules = harnessRules; this.jdbc = jdbc; this.objectMapper = objectMapper; this.creationSkills = new ManagedSessionSkills(jdbc, objectMapper, @@ -486,7 +498,7 @@ public class ManagedAgentStore implements AgentStateStore { String title, List> input, String payloadDigest, WorkspaceSelection selection, String requestedApprovalMode, boolean approvalSelectionSupported, Boolean requestedPlanMode, boolean planModeSupported, List> skills, boolean publicChildrenSupported, String modelId, - List extensionPins, + List extensionPins, Set extensionLifecycleProfiles) { return insertWorkspaceSessionCommand(tenantId, actorId, idempotencyKey, requestDigest, agentId, requestedRevision, title, input, payloadDigest, selection, requestedApprovalMode, @@ -501,7 +513,7 @@ public class ManagedAgentStore implements AgentStateStore { String title, List> input, String payloadDigest, WorkspaceSelection selection, String requestedApprovalMode, boolean approvalSelectionSupported, Boolean requestedPlanMode, boolean planModeSupported, List> skills, boolean publicChildrenSupported, String modelId, - List extensionPins, + List extensionPins, Set extensionLifecycleProfiles, SessionWorktreeStore.Plan worktree) { if (!input.isEmpty() && !workspaceFilesEnabled) { throw workspaceExecutionUnavailable(); @@ -516,7 +528,7 @@ public class ManagedAgentStore implements AgentStateStore { } if (modelId != null && (!"qwen-code".equals(agentId) || !workspaceFilesEnabled || modelCatalog.stream().noneMatch(option -> option.modelId().equals(modelId)))) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_model", + throw new ManagedAgentException(400, "unsupported_model", "The model is not published for this Agent."); } AgentPin pin = selectApprovalMode(resolveAgentPin(tenantId, agentId, @@ -525,7 +537,7 @@ public class ManagedAgentStore implements AgentStateStore { if (planMode && (!planModeSupported || !approvalSelectionSupported || pin.hooks() || pin.mcp() || !(WorkspaceToolProfiles.FILES.equals(pin.toolProfile()) || WorkspaceToolProfiles.isPublicShell(pin.toolProfile())))) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "Plan mode is unavailable"); + throw new ManagedAgentException(409, "unsupported_feature", "Plan mode is unavailable"); } if (!input.isEmpty() && WorkspaceToolProfiles.isPublicShell(pin.toolProfile()) && !workspaceShellEnabled) { @@ -540,7 +552,7 @@ public class ManagedAgentStore implements AgentStateStore { } if ((!WorkspaceToolProfiles.SHELL.equals(pin.toolProfile()) && !WorkspaceToolProfiles.MCP.equals(pin.toolProfile())) || !extensionLifecycleProfiles.contains(pin.toolProfile())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", + throw new ManagedAgentException(409, "unsupported_feature", "Extension configuration requires a negotiated Shell or MCP Session lifecycle profile."); } extensionPackages.requireSelection(tenantId, actorId, workspace.binding().getWorkspaceId(), extensionPins); @@ -576,7 +588,7 @@ public class ManagedAgentStore implements AgentStateStore { merged.addAll(skills); var effectiveSkills = AgentDefinitionExtensions.sessionSkills(merged); if (worktree != null && sessionWorktrees == null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "worktree_unavailable", "Session worktrees are unavailable."); + throw new ManagedAgentException(409, "worktree_unavailable", "Session worktrees are unavailable."); } ResolvedBinding target = worktree == null ? workspace : worktree.target(workspace); Admission admission = insertSession(tenantId, "CREATE_SESSION", idempotencyKey, @@ -654,7 +666,7 @@ public class ManagedAgentStore implements AgentStateStore { List existing = findWorkspaceCommand(tenantId, actorId, idempotencyKey); if (existing.isEmpty()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "idempotency_conflict", "The creation command is missing."); } return replayWorkspaceCommand(tenantId, actorId, requestDigest, @@ -707,7 +719,7 @@ public class ManagedAgentStore implements AgentStateStore { private void requireCsiJournalIsolation() { if (journalEventTransportEnabled || journalEventTransport != null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "csi_journal_transport_unsupported", + throw new ManagedAgentException(409, "csi_journal_transport_unsupported", "The private CSI lane requires an isolated journal without an event transport."); } } @@ -804,7 +816,7 @@ public class ManagedAgentStore implements AgentStateStore { parentSessionId); if (parent.workspace() == null || residentWorkspaces.find(parent.workspace()) != null || !"ACTIVE".equals(parent.status()) || CsiFilesRetirementProfile.PROFILE.equals(parent.toolProfile())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "child_parent_unavailable", "The parent Session cannot admit a child."); } @@ -827,11 +839,11 @@ public class ManagedAgentStore implements AgentStateStore { || !snapshot.path("modelRoute").path("endpointDigest").asText().matches("[a-f0-9]{64}") || !snapshot.path("modelRoute").path("providerPin").isTextual() || !snapshot.path("modelRoute").path("providerPin").asText().matches("[a-f0-9]{64}")) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_launch_snapshot_invalid", "The public child launch snapshot is invalid"); + throw new ManagedAgentException(409, "child_launch_snapshot_invalid", "The public child launch snapshot is invalid"); } if (snapshot.path("version").asInt() == 2) { if (configRecords == null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_launch_snapshot_invalid", "Package agent validation is unavailable"); + throw new ManagedAgentException(409, "child_launch_snapshot_invalid", "Package agent validation is unavailable"); } ManagedExtensionAgentLaunch.require(jdbc, configRecords, parent, snapshot); } @@ -845,11 +857,11 @@ public class ManagedAgentStore implements AgentStateStore { || !origin.path("endpointDigest").asText().matches("[a-f0-9]{64}") || !origin.path("providerPin").isTextual() || !origin.path("providerPin").asText().matches("[a-f0-9]{64}")) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_launch_snapshot_invalid", "The public child model origin is invalid"); + throw new ManagedAgentException(409, "child_launch_snapshot_invalid", "The public child model origin is invalid"); } } } else if (snapshot != null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_launch_snapshot_invalid", "The parent has no public child admission"); + throw new ManagedAgentException(409, "child_launch_snapshot_invalid", "The parent has no public child admission"); } requireLegacyMutation(tenantId, parentSessionId); if (!input.isEmpty() && !workspaceFilesEnabled) { @@ -858,7 +870,7 @@ public class ManagedAgentStore implements AgentStateStore { if (!existing.isEmpty()) { WorkspaceCommand command = existing.getFirst(); if (!command.requestDigest().equals(requestDigest)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key was reused with different content."); } @@ -875,7 +887,7 @@ public class ManagedAgentStore implements AgentStateStore { // is the same key the idempotent admission derives, so the // locking read never becomes a cross-tenant table scan under // REPEATABLE READ. - String fenceScopeKey = ManagedSessionStore.sessionScopeKey(tenantId, + String fenceScopeKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, parentSessionId); String fenceRecordKey = ManagedExtensionProjection.recordKey( parentSessionId, "child_run", lineage.parentChildRunId()); @@ -889,7 +901,7 @@ public class ManagedAgentStore implements AgentStateStore { && ("completed".equals(runState.getFirst()) || "failed".equals(runState.getFirst()) || "cancelled".equals(runState.getFirst()))) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_run_settled", + throw new ManagedAgentException(409, "child_run_settled", "The child run already settled; creation owes no more Session."); } // A child bound to a snapshot reads with the read-only profile @@ -948,7 +960,7 @@ public class ManagedAgentStore implements AgentStateStore { payloadDigest, now); } ManagedReceipts.insertCreation(jdbc, tenantId, - ManagedWorkspaceRegistry.actorKey(tenantId, childActorOf(parentSessionId)), + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, childActorOf(parentSessionId)), idempotencyKey, requestDigest, sessionId, turnId, now); var inheritedSkills = snapshot == null ? creationSkills.snapshot(tenantId, parentSessionId) : creationSkills.fromLaunchReference(tenantId, parentSessionId, snapshot.path("skillsRef")); @@ -1011,7 +1023,7 @@ public class ManagedAgentStore implements AgentStateStore { childRunId, tenantId); if (ready.size() != 1 || !ready.getFirst()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_workspace_not_ready", + throw new ManagedAgentException(409, "child_workspace_not_ready", "The child run's Workspace is not ready to bind a child Session."); } // The request is written once, when the row is admitted, so the @@ -1023,7 +1035,7 @@ public class ManagedAgentStore implements AgentStateStore { + " AND w.child_run_id = ? AND w.tenant_id = ?", String.class, parentSessionId, childRunId, tenantId); if (requests.size() != 1) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_workspace_not_ready", + throw new ManagedAgentException(409, "child_workspace_not_ready", "The child run's Workspace is not ready to bind a child Session."); } return ChildWorkspaceStore.workspaceMode(requests.getFirst()); @@ -1037,7 +1049,7 @@ public class ManagedAgentStore implements AgentStateStore { SessionRecord parent = requireSessionForUpdate(tenantId, parentSessionId); if (CsiFilesRetirementProfile.PROFILE.equals(parent.toolProfile())) { throw new ManagedAgentException( - HttpStatus.CONFLICT, + 409, "child_parent_unavailable", "The parent Session cannot admit a child."); } @@ -1045,12 +1057,12 @@ public class ManagedAgentStore implements AgentStateStore { List existing = findWorkspaceCommand(tenantId, childActorOf(parentSessionId), idempotencyKey); if (existing.isEmpty()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "idempotency_conflict", "The creation command is missing."); } WorkspaceCommand command = existing.getFirst(); if (!command.requestDigest().equals(requestDigest)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key was reused with different content."); } @@ -1088,17 +1100,17 @@ public class ManagedAgentStore implements AgentStateStore { || internalTeamLead != WorkspaceToolProfiles.INTERNAL_TEAM_LEAD.equals(session.toolProfile()) || internalTeamLead && (!"qwen-code".equals(session.agentId()) || session.agentDefinitionDigest() != null)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key was reused with different content."); } ContextBinding bound = session.workspace(); if (bound == null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "workspace_unavailable", "Workspace binding is missing."); } if (!workspaces.canRead(tenantId, actorId, bound.getWorkspaceId())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, + throw new ManagedAgentException(404, "workspace_not_found", "Workspace not found."); } return new Admission(command.sessionId(), command.turnId(), true, @@ -1109,7 +1121,7 @@ public class ManagedAgentStore implements AgentStateStore { private List findWorkspaceCommand(String tenantId, String actorId, String idempotencyKey) { ManagedReceipts.Receipt row = ManagedReceipts.creation(jdbc, tenantId, - ManagedWorkspaceRegistry.actorKey(tenantId, actorId), idempotencyKey); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId), idempotencyKey); return row == null ? List.of() : List.of( @@ -1129,17 +1141,17 @@ public class ManagedAgentStore implements AgentStateStore { } private static ManagedAgentException workspaceExecutionUnavailable() { - return new ManagedAgentException(HttpStatus.CONFLICT, + return new ManagedAgentException(409, "workspace_unavailable", "Hosted Workspace execution is not available."); } private void requireCreationScope(String tenantId, String idempotencyKey, boolean workspaceBound) { - var namespace = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_MODE; - var scope = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope.TENANT; + var namespace = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_MODE; + var scope = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope.TENANT; try { ManagedReceipts.insert(jdbc, namespace, tenantId, scope, tenantId, idempotencyKey, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(Boolean.toString(workspaceBound)), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(Boolean.toString(workspaceBound)), null, null, null, null, "RESERVED", null, workspaceBound, clock.millis()); } catch (DuplicateKeyException existing) { // The independently persisted creation-mode claim cannot be overwritten by replay. @@ -1148,7 +1160,7 @@ public class ManagedAgentStore implements AgentStateStore { tenantId, idempotencyKey, true); if (existing == null || !Boolean.valueOf(workspaceBound).equals(existing.workspaceBound())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "idempotency_conflict", + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key was reused with different content."); } } @@ -1209,7 +1221,7 @@ public class ManagedAgentStore implements AgentStateStore { if (!availableApprovalModes(pin).contains(requested) && !(supported && pin.digest() == null && "yolo".equals(requested) && WorkspaceToolProfiles.isPublicShell(pin.toolProfile()))) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_approval_mode", + throw new ManagedAgentException(400, "unsupported_approval_mode", "The selected approval mode is not available for this Agent."); } return new AgentPin(pin.revision(), pin.digest(), requested, pin.toolProfile(), pin.hooks(), pin.mcp()); @@ -1221,7 +1233,7 @@ public class ManagedAgentStore implements AgentStateStore { String requested, boolean workspaceBound) { if ("qwen-code".equals(agentId)) { if (requested != null && !requested.equals(agentRevision)) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "unsupported_feature", "Only the current agent revision can be selected."); } @@ -1232,7 +1244,7 @@ public class ManagedAgentStore implements AgentStateStore { return new AgentPin(agentRevision, null, approvalMode, toolProfile, false, false); } if (definitions == null) { - throw ManagedAgentDefinitionStore.notFound(); + throw com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore.notFound(); } ManagedAgentDefinitionStore.DefinitionRevision revision = resolveDefinitionRevision(tenantId, agentId, requested); @@ -1263,21 +1275,21 @@ public class ManagedAgentStore implements AgentStateStore { WorkspaceMigrationAdmission.lockTenant(jdbc, tenantId); SessionRecord session = requireLegacySessionForUpdate(tenantId, sessionId); if (session.workspace() == null || "DELETED".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", "The Session was not found."); + throw new ManagedAgentException(404, "session_not_found", "The Session was not found."); } WorkspaceAccess access = workspaces.accessOf(tenantId, actorId, session.workspace().getWorkspaceId()); if (!access.canRead()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", "The Session was not found."); + throw new ManagedAgentException(404, "session_not_found", "The Session was not found."); } if (!access.atLeast(WorkspaceAccess.OPERATOR)) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, "session_operation_forbidden", + throw new ManagedAgentException(403, "session_operation_forbidden", "Only a Workspace operator may manage MCP servers."); } if (!WorkspaceToolProfiles.SHELL.equals(session.toolProfile()) && !WorkspaceToolProfiles.MCP.equals(session.toolProfile()) || !workspaceFilesEnabled || !workspaceShellEnabled && WorkspaceToolProfiles.SHELL.equals(session.toolProfile()) || isSshWorkspace(session.workspace()) || residentWorkspaces.find(session.workspace()) != null || findChildLineage(tenantId, sessionId) != null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "MCP management is unavailable."); + throw new ManagedAgentException(409, "unsupported_feature", "MCP management is unavailable."); } requireSessionStatus(session.status(), "ACTIVE"); requireCwdChangeRegistryFacts(session); @@ -1286,7 +1298,7 @@ public class ManagedAgentStore implements AgentStateStore { + " WHERE tenant_id=? AND session_id=? AND command_id=? AND operation='manageMcp' LIMIT 1", tenantId, sessionId, operationId).isEmpty(); if (operationId != null && !replay) { - ManagedConfigInstall.requireOpen(jdbc, tenantId, sessionId); + ManagedConfigStore.requireOpen(jdbc, tenantId, sessionId); requireMcpManagementIdle(session); } return session; @@ -1299,7 +1311,7 @@ public class ManagedAgentStore implements AgentStateStore { if (session.workspace() == null || !workspaceId.equals(session.workspace().getWorkspaceId()) || !WorkspaceToolProfiles.SHELL.equals(session.toolProfile()) && !WorkspaceToolProfiles.MCP.equals(session.toolProfile()) || findChildLineage(tenantId, sessionId) != null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "MCP management is unavailable."); + throw new ManagedAgentException(409, "unsupported_feature", "MCP management is unavailable."); } requireSessionStatus(session.status(), "ACTIVE"); requireMcpManagementIdle(session); @@ -1311,14 +1323,14 @@ public class ManagedAgentStore implements AgentStateStore { WorkspaceMigrationAdmission.lockTenant(jdbc, tenantId); SessionRecord session = requireLegacySessionForUpdate(tenantId, sessionId); if (session.workspace() == null || "DELETED".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", "The Session was not found."); + throw new ManagedAgentException(404, "session_not_found", "The Session was not found."); } WorkspaceAccess access = workspaces.accessOf(tenantId, actorId, session.workspace().getWorkspaceId()); if (!access.canRead()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", "The Session was not found."); + throw new ManagedAgentException(404, "session_not_found", "The Session was not found."); } if (mutation && !access.atLeast(WorkspaceAccess.OPERATOR)) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, + throw new ManagedAgentException(403, "session_operation_forbidden", "Only a Workspace operator may apply extensions."); } var lineage = findChildLineage(tenantId, sessionId); @@ -1329,14 +1341,14 @@ public class ManagedAgentStore implements AgentStateStore { || !workspaceFilesEnabled || !Set.of(WorkspaceToolProfiles.SHELL, WorkspaceToolProfiles.MCP, WorkspaceToolProfiles.FILES, WorkspaceToolProfiles.READ) .contains(session.toolProfile())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "Extension management is unavailable."); + throw new ManagedAgentException(409, "unsupported_feature", "Extension management is unavailable."); } if (mutation) { requireSessionStatus(session.status(), "ACTIVE"); requireCwdChangeRegistryFacts(session); WorkspaceMigrationAdmission.requireOpen(jdbc, tenantId, session.workspace().getStorageId()); if (!same) { - ManagedConfigInstall.requireOpen(jdbc, tenantId, sessionId); + ManagedConfigStore.requireOpen(jdbc, tenantId, sessionId); requireMcpManagementIdle(session); } } @@ -1348,7 +1360,7 @@ public class ManagedAgentStore implements AgentStateStore { public void requireConfigurationCommit(String tenantId, String workspaceId, String sessionId, JsonNode body) { SessionRecord session = requireLegacySessionForUpdate(tenantId, sessionId); if (session.workspace() == null || !workspaceId.equals(session.workspace().getWorkspaceId())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "Extension management is unavailable."); + throw new ManagedAgentException(409, "unsupported_feature", "Extension management is unavailable."); } requireSessionStatus(session.status(), "ACTIVE"); var lineage = findChildLineage(tenantId, sessionId); @@ -1365,13 +1377,13 @@ public class ManagedAgentStore implements AgentStateStore { tenantId, sessionId).isEmpty(); if (!beforeModel || !isBootstrapRevision(body) || !objectMapper.valueToTree(initial).equals(body.path("command"))) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "extension_initialization_conflict", "Initial extension selection conflicts with its creation receipt."); + throw new ManagedAgentException(409, "extension_initialization_conflict", "Initial extension selection conflicts with its creation receipt."); } return; } JsonNode configuration = ManagedMcpSettings.find(jdbc, tenantId, sessionId); if (configuration == null || !Set.of("active", "failed").contains(configuration.path("phase").asText())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "extension_initialization_required", "Initial extension configuration must settle first."); + throw new ManagedAgentException(409, "extension_initialization_required", "Initial extension configuration must settle first."); } } // Every phase observes the same locked Session row as Turn admission. @@ -1386,7 +1398,7 @@ public class ManagedAgentStore implements AgentStateStore { && jdbc.queryForList("SELECT turn_id FROM ma_turn WHERE tenant_id=? AND session_id=? AND submission_attempted=TRUE LIMIT 1 FOR UPDATE", child.tenantId(), child.sessionId()).isEmpty(); if (!onlyInitialTurn || !isBootstrapRevision(body) || !command.equals(body.path("command"))) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_launch_snapshot_invalid", "Child extension bootstrap conflicts with its launch snapshot"); + throw new ManagedAgentException(409, "child_launch_snapshot_invalid", "Child extension bootstrap conflicts with its launch snapshot"); } } @@ -1399,13 +1411,13 @@ public class ManagedAgentStore implements AgentStateStore { private JsonNode childExtensionBootstrapCommand(SessionRecord child, StoreModels.SessionLineage lineage) { if (configRecords == null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "child_launch_snapshot_invalid", "Package agent validation is unavailable"); + throw new ManagedAgentException(409, "child_launch_snapshot_invalid", "Package agent validation is unavailable"); } var run = configRecords.findRecord(child.tenantId(), lineage.parentSessionId(), "child_run", lineage.parentChildRunId()).orElseThrow(); var envelope = configRecords.readRecordResource(child.tenantId(), lineage.parentSessionId(), run.required("inputRef")); var launch = envelope.path("publicLaunch"); if (launch.path("version").asInt() != 2) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "Child extension updates are unavailable"); + throw new ManagedAgentException(409, "unsupported_feature", "Child extension updates are unavailable"); } var ref = launch.required("extensionAgent").required("bundleRef"); var bundle = configRecords.readRecordResource(child.tenantId(), lineage.parentSessionId(), ref); @@ -1426,38 +1438,38 @@ public class ManagedAgentStore implements AgentStateStore { // Plan/Turn admission that won the same Session row before this settings commit. if (Boolean.TRUE.equals(jdbc.queryForObject("SELECT plan_enabled FROM ma_session WHERE tenant_id=? AND session_id=? FOR UPDATE", Boolean.class, tenantId, sessionId))) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "Disable Plan mode before managing MCP servers."); + throw new ManagedAgentException(409, "unsupported_feature", "Disable Plan mode before managing MCP servers."); } if (!jdbc.queryForList("SELECT turn_id FROM ma_turn WHERE tenant_id=? AND session_id=?" + " AND status IN ('ACCEPTED', 'RUNNING', 'CANCELLING') LIMIT 1 FOR UPDATE", tenantId, sessionId).isEmpty()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "turn_active", "The Session has an active Turn or approval."); + throw new ManagedAgentException(409, "turn_active", "The Session has an active Turn or approval."); } if (!jdbc.queryForList("SELECT record_id FROM ma_session_record WHERE record_kind='DOMAIN'" + " AND session_scope_key=? AND tenant_id=? AND session_id=? AND domain='child_run'" - + " AND settled_at IS NULL LIMIT 1 FOR UPDATE", ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + + " AND settled_at IS NULL LIMIT 1 FOR UPDATE", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId).isEmpty()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "session_context_busy", "The Session has an unsettled child run."); + throw new ManagedAgentException(409, "session_context_busy", "The Session has an unsettled child run."); } long now = lifecycleDatabaseTime(); for (String options : jdbc.queryForList("SELECT options_json FROM ma_session_record WHERE session_scope_key=?" + " AND record_kind='ACTION' AND state='requested' AND tenant_id=? AND session_id=? FOR UPDATE", - String.class, ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId)) { + String.class, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId)) { Object expiresAt = readMap(options).get("expiresAt"); if (expiresAt instanceof Number expiration && now < expiration.longValue()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "turn_active", "The Session has an active Turn or approval."); + throw new ManagedAgentException(409, "turn_active", "The Session has an active Turn or approval."); } } if (!jdbc.queryForList("SELECT operation_id FROM ma_operation WHERE scope_key=? AND target_key=?" + " AND operation_kind IN " + SESSION_OPERATION_KINDS + " AND (state IN ('PENDING', 'RUNNING', 'UNKNOWN') OR state='RECOVERY_BLOCKED' AND operation_kind <> 'TASK_CANCEL')" - + " LIMIT 1 FOR UPDATE", ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId)).isEmpty() + + " LIMIT 1 FOR UPDATE", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId)).isEmpty() || !jdbc.queryForList("SELECT operation_id FROM ma_operation WHERE operation_kind='RENAME'" + " AND target_key=? AND tenant_id=? AND session_id=? AND state IN ('PENDING', 'RUNNING') LIMIT 1 FOR UPDATE", renameTarget(tenantId, sessionId), tenantId, sessionId).isEmpty() || !jdbc.queryForList("SELECT receipt_key FROM ma_request_receipt WHERE namespace='SESSION_COMMAND'" + " AND tenant_key=? AND tenant_id=? AND session_id=? AND state='PENDING' LIMIT 1 FOR UPDATE", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(tenantId), tenantId, sessionId).isEmpty()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "session_context_busy", "The Session has an active operation."); + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(tenantId), tenantId, sessionId).isEmpty()) { + throw new ManagedAgentException(409, "session_context_busy", "The Session has an active operation."); } } @@ -1465,14 +1477,14 @@ public class ManagedAgentStore implements AgentStateStore { String tenantId, String agentId, String requested) { if (requested == null) { return definitions.latestForUpdate(tenantId, agentId) - .orElseThrow(ManagedAgentDefinitionStore::notFound); + .orElseThrow(com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore::notFound); } if (!DEFINITION_REVISION.matcher(requested).matches()) { - throw ManagedAgentDefinitionStore.notFound(); + throw com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore.notFound(); } return definitions .find(tenantId, agentId, Long.parseLong(requested)) - .orElseThrow(ManagedAgentDefinitionStore::notFound); + .orElseThrow(com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore::notFound); } private Admission insertSession(String tenantId, String operation, @@ -1538,7 +1550,7 @@ public class ManagedAgentStore implements AgentStateStore { String promptId = input.isEmpty() ? null : UUID.randomUUID().toString(); byte[] actorKey = actorId == null ? null - : ManagedWorkspaceRegistry.actorKey(tenantId, actorId); + : com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId); jdbc.update("INSERT INTO ma_session (tenant_id," + " session_id, agent_id, agent_revision," + " agent_definition_digest, title," @@ -1647,13 +1659,13 @@ public class ManagedAgentStore implements AgentStateStore { WorkspaceMigrationAdmission.requireOpen(jdbc, tenantId, session.workspace().getStorageId()); } if (!"ACTIVE".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "session_not_active", + throw new ManagedAgentException(409, "session_not_active", "The Session does not accept new Turns."); } - SessionWorktreeStore.requireReady(jdbc, tenantId, sessionId); - ManagedConfigInstall.requireOpen(jdbc, tenantId, sessionId); + SessionWorktreeStore.requireReady(worktreeFiles, jdbc, tenantId, sessionId); + ManagedConfigStore.requireOpen(jdbc, tenantId, sessionId); if (hasActiveTurn(tenantId, sessionId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "turn_active", + throw new ManagedAgentException(409, "turn_active", "The Session already has an active Turn."); } record QueuedInput(String messageId, String state, String turnId, String actorId, String inputJson) { } @@ -1666,7 +1678,7 @@ public class ManagedAgentStore implements AgentStateStore { if (!"claimed".equals(first.state()) || first.turnId() != null || !("user-queue:" + first.messageId()).equals(idempotencyKey) || !Objects.equals(actorId, first.actorId()) || !readInput(first.inputJson()).equals(input)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "user_message_queue_pending", "Send new input through the Session user message queue."); + throw new ManagedAgentException(409, "user_message_queue_pending", "Send new input through the Session user message queue."); } } // The W2 handshake: an open execution operation on a bound Session @@ -1679,7 +1691,7 @@ public class ManagedAgentStore implements AgentStateStore { // intentionally cover them stay untouched. if (session.workspace() != null && hasOpenExecutionOperation(tenantId, sessionId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "session_context_busy", "The Session has an open operation."); } @@ -1788,7 +1800,7 @@ public class ManagedAgentStore implements AgentStateStore { + " = ? AND idempotency_digest = ? AND tenant_id = ? AND session_id = ?" + " ORDER BY target_revision DESC LIMIT 1 FOR UPDATE", renameTarget(tenantId, sessionId), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( key), tenantId, sessionId); @@ -1797,20 +1809,20 @@ public class ManagedAgentStore implements AgentStateStore { if (!key.equals(row.get("idempotency_key")) || !digest.equals(row.get("request_digest")) || !title.equals(readMap((String) row.get("request_json")).get("title"))) { throw new ManagedAgentException( - HttpStatus.CONFLICT, + 409, "idempotency_conflict", "The idempotency key was reused with different content."); } if ("SUPERSEDED".equals(row.get("state"))) { throw new ManagedAgentException( - HttpStatus.CONFLICT, + 409, "session_mutation_superseded", "A later Session title attempt has been admitted."); } // A parked delivery awaits the reconciler and is never re-sent. if ("RECOVERY_BLOCKED".equals(row.get("state"))) { throw new ManagedAgentException( - HttpStatus.CONFLICT, + 409, "operation_outcome_unknown", "The original title delivery is awaiting reconciliation."); } @@ -1867,15 +1879,15 @@ public class ManagedAgentStore implements AgentStateStore { + " gmt_create, gmt_modified) VALUES (?, ?, ?, ?, 'RENAME', ?, ?, 'SESSION', ?," + " ?, ?, ?, ?, ?, 'PENDING', 'ADMITTED', 'PENDING', ?, ?, ?)", renameKey(tenantId, sessionId, next), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), "rename:" + next, tenantId, sessionId, renameTarget(tenantId, sessionId), next, key, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(key), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(key), digest, writeJson(Map.of("title", title)), now, @@ -1902,10 +1914,10 @@ public class ManagedAgentStore implements AgentStateStore { && !workspaces.createdSession(session.tenantId(), actorId, session.sessionId())) { if ("DELETED".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", + throw new ManagedAgentException(404, "session_not_found", "The Session was not found."); } - throw new ManagedAgentException(HttpStatus.CONFLICT, "workspace_unavailable", + throw new ManagedAgentException(409, "workspace_unavailable", "Hosted Workspace execution is not available."); } } @@ -1950,7 +1962,7 @@ public class ManagedAgentStore implements AgentStateStore { readMap((String) rows.getFirst().get("request_json")) .get("title"))) { throw new ManagedAgentException( - HttpStatus.CONFLICT, + 409, "session_mutation_superseded", "A later Session title attempt has been admitted."); } @@ -1963,7 +1975,7 @@ public class ManagedAgentStore implements AgentStateStore { || !List.of("RUNNING", "RECOVERY_BLOCKED") .contains(operation.get("state"))) { throw new ManagedAgentException( - HttpStatus.CONFLICT, + 409, "operation_claim_lost", "The title delivery claim was lost."); } @@ -2068,7 +2080,7 @@ public class ManagedAgentStore implements AgentStateStore { delivery.claimGeneration()); if (changed != 1) { throw new ManagedAgentException( - HttpStatus.CONFLICT, + 409, "operation_claim_lost", "The title delivery claim was lost."); } @@ -2132,14 +2144,14 @@ public class ManagedAgentStore implements AgentStateStore { } private static String renameTarget(String tenant, String session) { - return com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + return com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( "SESSION", tenant, session); } private static String renameKey(String tenant, String session, long revision) { - return com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.operationKey( - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract + return com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.operationKey( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract .OperationKind.RENAME, - ManagedSessionStore.sessionScopeKey(tenant, session), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), renameTarget(tenant, session), "rename:" + revision); } @@ -2182,7 +2194,7 @@ public class ManagedAgentStore implements AgentStateStore { CommandRecord command = existing.get(); if (!command.requestDigest().equals(requestDigest) || !command.sessionId().equals(sessionId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key was reused with different content."); } @@ -2243,7 +2255,7 @@ public class ManagedAgentStore implements AgentStateStore { new IllegalStateException( "Session mutation command is unavailable")); if (!command.sessionId().equals(sessionId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key belongs to another Session."); } @@ -2261,7 +2273,7 @@ public class ManagedAgentStore implements AgentStateStore { // completing this one now would revert that newer outcome. if ("FAILED".equals(command.status()) && supersededByLaterMutation( tenantId, sessionId, operation, idempotencyKey, kind)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "session_mutation_superseded", "A later change to the Session completed after this" + " request was retired."); @@ -2376,7 +2388,7 @@ public class ManagedAgentStore implements AgentStateStore { } List arguments = new ArrayList<>(sessionIds.size() + 1); arguments.add(tenantId); - sessionIds.forEach(id -> arguments.add(ManagedSessionStore.sessionScopeKey(tenantId, id))); + sessionIds.forEach(id -> arguments.add(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, id))); return new HashSet<>(jdbc.queryForList("SELECT session_id FROM" + " ma_operation WHERE tenant_id = ?" + " AND scope_key IN (" + placeholders(sessionIds.size()) @@ -2391,10 +2403,10 @@ public class ManagedAgentStore implements AgentStateStore { private void requireWorkspaceCreator(SessionRecord session, String actorId) { if (!workspaces.canRead(session.tenantId(), actorId, session.workspace().getWorkspaceId())) { throw new ManagedAgentException( - HttpStatus.NOT_FOUND, "session_not_found", "The Session was not found."); + 404, "session_not_found", "The Session was not found."); } if (!workspaces.isSessionOwner(session.tenantId(), actorId, session.sessionId())) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, "session_operation_forbidden", + throw new ManagedAgentException(403, "session_operation_forbidden", "Only the Session owner may manage it."); } } @@ -2412,7 +2424,7 @@ public class ManagedAgentStore implements AgentStateStore { Optional existing = findCommand(tenantId, namespace, scopedKey, true); if (existing.isPresent()) { if (!existing.get().requestDigest().equals(requestDigest) || !existing.get().sessionId().equals(sessionId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "idempotency_conflict", + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key was reused with different content."); } return new SessionMutation(session, true); @@ -2421,7 +2433,7 @@ public class ManagedAgentStore implements AgentStateStore { // refusal: a fresh key against a deleted Session must not answer // a retryable 409 for a Session that will never come back. if ("DELETED".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", "The Session was not found."); + throw new ManagedAgentException(404, "session_not_found", "The Session was not found."); } if (WorkspaceToolProfiles.isShellOrMcp(session.toolProfile()) && (WorkspaceToolProfiles.lifecycleProtocol(session.toolProfile()) != 2 @@ -2499,11 +2511,11 @@ public class ManagedAgentStore implements AgentStateStore { String sessionId, String turnId, OperationKind kind, String command, String idempotencyKey, String requestDigest, byte[] actorKey, String sessionStatus, long now) { - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); String target = turnOperationTarget(turnId); String operationId = publicId("op"); - String operationKey = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.operationKey( - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind.valueOf(kind.name()), + String operationKey = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.operationKey( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind.valueOf(kind.name()), scope, target, operationId); jdbc.update( "INSERT INTO ma_operation (operation_key, scope_key, session_scope_key," @@ -2520,7 +2532,7 @@ public class ManagedAgentStore implements AgentStateStore { turnId, writeJson(Map.of("schemaVersion", 1, "kind", kind.name(), "sessionId", sessionId, "turnId", turnId)), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( idempotencyKey), tenantId, sessionId, @@ -2539,7 +2551,7 @@ public class ManagedAgentStore implements AgentStateStore { } private static String turnOperationTarget(String turnId) { - return com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(turnId); + return com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(turnId); } /** @@ -2626,7 +2638,7 @@ public class ManagedAgentStore implements AgentStateStore { + " operation_kind = ? AND target_key = ? AND state =" + " 'PENDING' FOR UPDATE", String.class, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), kind.name(), turnOperationTarget(turnId))) { jdbc.update("UPDATE ma_operation SET state = ?, phase = ?," + " admission_stage = ?, delivery_state = ?," @@ -2656,20 +2668,20 @@ public class ManagedAgentStore implements AgentStateStore { } return operationMapper.mapRow(result, row); }, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), operationId) .stream() .findFirst(); } private static String sessionOperationTarget(String sessionId) { - return com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(sessionId); + return com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(sessionId); } private static String sessionOperationKey(String tenantId, String sessionId, OperationKind kind, String operationId) { - return com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.operationKey( - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind.valueOf(kind.name()), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), operationId); + return com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.operationKey( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind.valueOf(kind.name()), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), operationId); } private Optional replayOperation(String tenantId, String sessionId, @@ -2688,11 +2700,11 @@ public class ManagedAgentStore implements AgentStateStore { } return operationMapper.mapRow(result, row); }, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), kind.name(), sessionOperationTarget(sessionId), actorDigest, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( idempotencyKey)) .stream() .findFirst(); @@ -2713,7 +2725,7 @@ public class ManagedAgentStore implements AgentStateStore { } return operationMapper.mapRow(result, row); }, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), operationId) .stream() @@ -2748,7 +2760,7 @@ public class ManagedAgentStore implements AgentStateStore { || actor.length == 0 || !java.util.Arrays.equals(actor, creator)) { throw new ManagedAgentException( - HttpStatus.NOT_FOUND, + 404, "session_not_found", "The child Session lineage was not found."); } @@ -2797,14 +2809,14 @@ public class ManagedAgentStore implements AgentStateStore { kind, actorDigest, idempotencyKey); if (existing.isPresent()) { if (!existing.get().requestDigest().equals(requestDigest)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key was reused with different content."); } return new OperationAdmission(existing.get(), true); } if ("DELETED".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", + throw new ManagedAgentException(404, "session_not_found", "The Session was not found."); } // A terminally FAILED operation leaves the Session in the pending @@ -2833,7 +2845,7 @@ public class ManagedAgentStore implements AgentStateStore { } if (protocolVersion < 0 || protocolVersion > 2 || protocolVersion != 0 && protocolVersion != WorkspaceToolProfiles.lifecycleProtocol(session.toolProfile())) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_protocol_unavailable"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_protocol_unavailable"); } if (publicLifecycle && WorkspaceToolProfiles.isShellOrMcp(session.toolProfile()) && (WorkspaceToolProfiles.lifecycleProtocol(session.toolProfile()) != 2 @@ -2842,7 +2854,7 @@ public class ManagedAgentStore implements AgentStateStore { throw workspaceExecutionUnavailable(); } if (hasPendingRenameDelivery(tenantId, sessionId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "session_operation_active", + throw new ManagedAgentException(409, "session_operation_active", "The Session has a title delivery awaiting completion."); } if (session.workspace() != null) { @@ -2902,8 +2914,8 @@ public class ManagedAgentStore implements AgentStateStore { + " (?, ?, ?, 'SESSION', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'JAVA_DURABLE'," + " ?, ?, ?, ?, ?, ?, ?, ?)", sessionOperationKey(tenantId, sessionId, kind, operationId), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), sessionId, archive ? "COMPLETED" : "PENDING", @@ -2919,7 +2931,7 @@ public class ManagedAgentStore implements AgentStateStore { sessionStatusBefore, "protocolVersion", protocolVersion)), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( idempotencyKey), tenantId, sessionId, @@ -2979,7 +2991,7 @@ public class ManagedAgentStore implements AgentStateStore { } } else if (WorkspaceToolProfiles.lifecycleProtocol(protocolVersion)) { jdbc.execute((org.springframework.jdbc.core.ConnectionCallback) connection -> { - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.beginHarnessLifecycle(connection, tenantId, sessionId, operationId); + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.beginHarnessLifecycle(connection, tenantId, sessionId, operationId); return null; }); if (protocolVersion == 2) { @@ -3016,38 +3028,38 @@ public class ManagedAgentStore implements AgentStateStore { SessionRecord session = requireLegacySessionForUpdate(tenantId, sessionId); ContextBinding binding = session.workspace(); if (binding == null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_feature", + throw new ManagedAgentException(400, "unsupported_feature", "Context compression requires a Workspace Session."); } WorkspaceAccess access = workspaces.accessOf(tenantId, actorId, binding.getWorkspaceId()); if (!access.canRead()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", "The Session was not found."); + throw new ManagedAgentException(404, "session_not_found", "The Session was not found."); } Optional replay = replayOperation(tenantId, sessionId, OperationKind.CONTEXT_COMPRESSION, actorDigest, key); if (replay.isPresent()) { if (!digest.equals(replay.get().requestDigest())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "idempotency_conflict", + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key was reused with different content."); } return new OperationAdmission(replay.get(), true); } if (!access.atLeast(WorkspaceAccess.OPERATOR)) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, "session_operation_forbidden", + throw new ManagedAgentException(403, "session_operation_forbidden", "Only a Workspace operator may compress Session context."); } if (!supported || !workspaceFilesEnabled) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", + throw new ManagedAgentException(409, "unsupported_feature", "Hosted context compression is unavailable."); } requireSessionStatus(session.status(), "ACTIVE"); WorkspaceMigrationAdmission.requireOpen(jdbc, tenantId, binding.getStorageId()); requireCwdChangeRegistryFacts(session); if (contextCompressionState(tenantId, sessionId).compactionRevision() != expectedCompactionRevision) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "compaction_revision_conflict", + throw new ManagedAgentException(409, "compaction_revision_conflict", "The Session compaction revision has changed."); } - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); if (hasOpenOperation(tenantId, sessionId) || hasPendingRenameDelivery(tenantId, sessionId) || hasActiveTurn(tenantId, sessionId) || hasDecidableAction(session) || !jdbc.queryForList("SELECT 1 FROM ma_session_record WHERE session_scope_key = ?" @@ -3058,7 +3070,7 @@ public class ManagedAgentStore implements AgentStateStore { || !jdbc.queryForList("SELECT 1 FROM ma_session_head WHERE tenant_id = ? AND session_id = ?" + " AND head_kind = 'MANAGED_SESSION' AND (state <> 'ACTIVE' OR recovery_status <> 'READY')", tenantId, sessionId).isEmpty()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "session_context_busy", + throw new ManagedAgentException(409, "session_context_busy", "The Session has an active Turn, approval, Hook, recovery or context operation."); } String operationId = publicId("op"); @@ -3079,14 +3091,14 @@ public class ManagedAgentStore implements AgentStateStore { sessionOperationKey(tenantId, sessionId, OperationKind.CONTEXT_COMPRESSION, operationId), scope, scope, sessionOperationTarget(sessionId), sessionId, writeJson(request), ManagedSchemaKeys.key(key), tenantId, sessionId, operationId, actorDigest, key, digest, - session.status(), ManagedWorkspaceRegistry.actorKey(tenantId, actorId), now, now, now); + session.status(), com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId), now, now, now); return new OperationAdmission(findOperation(tenantId, sessionId, operationId).orElseThrow(), false); } @Override public ContextCompressionState contextCompressionState(String tenantId, String sessionId) { ensureCompactionProjection(tenantId, sessionId); - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); List revisions = jdbc.queryForList("SELECT revision FROM ma_session_record WHERE" + " session_scope_key = ? AND record_key = ? AND record_kind = 'DOMAIN'", Long.class, scope, ManagedExtensionProjection.recordKey(sessionId, "hosted_context_compaction", "latest")); @@ -3105,7 +3117,7 @@ public class ManagedAgentStore implements AgentStateStore { } List arguments = new ArrayList<>(); arguments.add(tenantId); - sessionIds.forEach(id -> arguments.add(ManagedSessionStore.sessionScopeKey(tenantId, id))); + sessionIds.forEach(id -> arguments.add(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, id))); Map result = new HashMap<>(); Set indexed = new HashSet<>(); sessionIds.forEach(id -> result.put(id, new ContextCompressionState(0, null))); @@ -3146,7 +3158,7 @@ public class ManagedAgentStore implements AgentStateStore { } private void ensureCompactionProjection(String tenantId, String sessionId) { - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); String key = ManagedExtensionProjection.recordKey(sessionId, "hosted_context_compaction", "latest"); if (!jdbc.queryForList("SELECT 1 FROM ma_session_record WHERE session_scope_key = ? AND record_key = ?", scope, key).isEmpty()) { @@ -3187,7 +3199,7 @@ public class ManagedAgentStore implements AgentStateStore { throw compressionStateUnavailable(); } for (String line : new String(bytes, StandardCharsets.UTF_8).split("\n")) { - var record = ManagedExtensionRecordStore.parse(line); + var record = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(line); if (record == null) { throw compressionStateUnavailable(); } @@ -3243,7 +3255,7 @@ public class ManagedAgentStore implements AgentStateStore { } private static ManagedAgentException compressionStateUnavailable() { - return new ManagedAgentException(HttpStatus.CONFLICT, "context_compression_state_unavailable", + return new ManagedAgentException(409, "context_compression_state_unavailable", "The retained journal cannot establish the Session compaction revision."); } @@ -3251,7 +3263,7 @@ public class ManagedAgentStore implements AgentStateStore { public Map contextCompressionRequest(String tenantId, String sessionId, String operationId) { return jdbc.queryForList("SELECT request_json FROM ma_operation WHERE scope_key = ?" + " AND operation_kind = 'CONTEXT_COMPRESSION' AND operation_id = ?", String.class, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), operationId).stream() + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), operationId).stream() .findFirst().map(this::readMap).orElse(Map.of()); } @@ -3274,7 +3286,7 @@ public class ManagedAgentStore implements AgentStateStore { + " scope_key = ? AND operation_kind = 'CONTEXT_COMPRESSION' AND operation_id = ?" + " AND state IN ('PENDING', 'RUNNING', 'RECOVERY_BLOCKED') AND available_at <= " + ManagedSchemaTime.SQL_NOW + " AND (claim_owner IS NULL OR claim_until < " + ManagedSchemaTime.SQL_NOW + ")", - owner, duration.toMillis(), ManagedSessionStore.sessionScopeKey(tenantId, sessionId), operationId); + owner, duration.toMillis(), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), operationId); return changed == 1 ? findOperation(tenantId, sessionId, operationId) : Optional.empty(); } @@ -3288,7 +3300,7 @@ public class ManagedAgentStore implements AgentStateStore { if (!List.of("PENDING", "RUNNING", "RECOVERY_BLOCKED").contains(operation.state()) || session.workspace() == null || !"ACTIVE".equals(session.status()) || operation.actorKey() == null || !initiatorKeepsOperate(session, operation)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "context_compression_authorization_revoked", + throw new ManagedAgentException(409, "context_compression_authorization_revoked", "The compression initiator no longer has execution authority."); } requireCwdChangeRegistryFacts(session); @@ -3386,7 +3398,7 @@ public class ManagedAgentStore implements AgentStateStore { + " AND target_key = ? AND tenant_id = ? AND session_id = ? AND" + " operation_id = ? FOR UPDATE", Boolean.class, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, @@ -3421,7 +3433,7 @@ public class ManagedAgentStore implements AgentStateStore { } if (failure == null) { try { - SessionWorktreeStore.requireReady(jdbc, tenantId, sessionId); + SessionWorktreeStore.requireReady(worktreeFiles, jdbc, tenantId, sessionId); SessionWorktreeStore.requireCwd(jdbc, tenantId, sessionId, operation.targetCwdRelative()); } catch (ManagedAgentException error) { failure = error.getCode(); @@ -3440,7 +3452,7 @@ public class ManagedAgentStore implements AgentStateStore { failure, now, now, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, @@ -3466,7 +3478,7 @@ public class ManagedAgentStore implements AgentStateStore { result, now, now, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, @@ -3501,7 +3513,7 @@ public class ManagedAgentStore implements AgentStateStore { failureCode, now, now, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, @@ -3529,18 +3541,18 @@ public class ManagedAgentStore implements AgentStateStore { OperationKind.TASK_CANCEL, actorDigest, idempotencyKey); if (existing.isPresent()) { if (!existing.get().requestDigest().equals(requestDigest)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key was reused with different content."); } return new OperationAdmission(existing.get(), true); } if ("DELETED".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", + throw new ManagedAgentException(404, "session_not_found", "The Session was not found."); } if (!"ACTIVE".equals(session.status())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "session_not_active", + throw new ManagedAgentException(409, "session_not_active", "The Session is " + session.status().toLowerCase( java.util.Locale.ROOT) + " and accepts no task cancellation."); @@ -3554,15 +3566,15 @@ public class ManagedAgentStore implements AgentStateStore { + " AND task_kind IS NOT NULL FOR UPDATE", (result, row) -> new String[] {result.getString("task_kind"), result.getString("task_state")}, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), taskId.substring("task_".length())); if (task.isEmpty()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "task_not_found", + throw new ManagedAgentException(404, "task_not_found", "The task was not found."); } if (!ManagedExtensionProjection.taskActions(task.getFirst()[0], task.getFirst()[1]).contains("cancel")) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "task_action_unavailable", "The task does not accept a cancellation now."); } @@ -3585,8 +3597,8 @@ public class ManagedAgentStore implements AgentStateStore { + " gmt_modified) VALUES (?, ?, ?, 'SESSION', ?, ?, 'PENDING', ?, ?, ?, ?, ?, ?," + " ?, ?, ?, 'PENDING', 'JAVA_DURABLE', 'PENDING', ?, ?, ?, ?, ?)", sessionOperationKey(tenantId, sessionId, OperationKind.TASK_CANCEL, operationId), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), sessionId, writeJson( @@ -3599,7 +3611,7 @@ public class ManagedAgentStore implements AgentStateStore { sessionId, "taskId", taskId)), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( idempotencyKey), tenantId, sessionId, @@ -3724,7 +3736,7 @@ public class ManagedAgentStore implements AgentStateStore { String sessionId, String operationId) { String turnId = jdbc.queryForList("SELECT target_id FROM ma_operation WHERE" + " scope_key = ? AND operation_kind = 'SUBMIT_INPUT' AND operation_id = ?", - String.class, ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + String.class, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), operationId).stream().findFirst().orElse(null); TurnRecord turn = turnId == null ? null : findTurn(tenantId, sessionId, turnId).orElse(null); @@ -3786,7 +3798,7 @@ public class ManagedAgentStore implements AgentStateStore { var rows = jdbc.queryForList("SELECT target_revision, request_json FROM ma_operation" + " WHERE scope_key = ? AND operation_kind = 'RENAME' AND operation_id = ?" + " AND tenant_id = ? AND session_id = ? AND state = 'RECOVERY_BLOCKED'", - ManagedSessionStore.sessionScopeKey(parked.tenantId(), parked.sessionId()), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(parked.tenantId(), parked.sessionId()), parked.operationId(), parked.tenantId(), parked.sessionId()); if (rows.isEmpty()) { return Optional.empty(); @@ -3824,7 +3836,7 @@ public class ManagedAgentStore implements AgentStateStore { + " FROM ma_operation WHERE scope_key = ? AND operation_kind = 'RENAME'" + " AND operation_id = ? AND tenant_id = ? AND session_id = ? AND state =" + " 'RECOVERY_BLOCKED' AND delivery_state = 'BLOCKED' FOR UPDATE", - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), parked.operationId(), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), parked.operationId(), tenantId, sessionId); if (rows.isEmpty()) { return false; @@ -3938,7 +3950,7 @@ public class ManagedAgentStore implements AgentStateStore { default -> throw new IllegalArgumentException( "Unknown task cancel outcome " + outcome.status()); }; - arguments.addAll(List.of(ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, operationId, owner, claimGeneration)); + arguments.addAll(List.of(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, operationId, owner, claimGeneration)); return jdbc.update( update + " WHERE scope_key = ? AND operation_kind IN " @@ -3974,7 +3986,7 @@ public class ManagedAgentStore implements AgentStateStore { } private void requireCwdChangeRegistryFacts(SessionRecord session) { - SessionWorktreeStore.requireReady(jdbc, session.tenantId(), session.sessionId()); + SessionWorktreeStore.requireReady(worktreeFiles, jdbc, session.tenantId(), session.sessionId()); if (!hasExecutionRegistryFacts(session.tenantId(), session.sessionId())) { throw workspaceExecutionUnavailable(); @@ -4092,7 +4104,7 @@ public class ManagedAgentStore implements AgentStateStore { // matching the recovery scan's own population. private boolean hasOpenExecutionOperation(String tenantId, String sessionId) { - if (ManagedConfigInstall.blocked(jdbc, tenantId, sessionId)) { + if (ManagedConfigStore.blocked(jdbc, tenantId, sessionId)) { return true; } Integer operations = jdbc.queryForObject("SELECT COUNT(*) FROM" @@ -4100,13 +4112,13 @@ public class ManagedAgentStore implements AgentStateStore { + " target_key = ? AND operation_kind IN" + " ('CLOSE', 'ARCHIVE', 'DELETE', 'CWD_CHANGE', 'CONTEXT_COMPRESSION') AND state IN" + " ('PENDING', 'RUNNING', 'RECOVERY_BLOCKED', 'UNKNOWN')", - Integer.class, ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId)); + Integer.class, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId)); return operations != null && operations > 0; } private boolean hasOpenOperation(String tenantId, String sessionId, String excludingOperationId) { - if (ManagedConfigInstall.blocked(jdbc, tenantId, sessionId)) { + if (ManagedConfigStore.blocked(jdbc, tenantId, sessionId)) { return true; } Integer commands = @@ -4115,7 +4127,7 @@ public class ManagedAgentStore implements AgentStateStore { + " 'SESSION_COMMAND' AND tenant_key = ? AND tenant_id = ? AND" + " session_id = ? AND state = 'PENDING'", Integer.class, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( tenantId), tenantId, sessionId); @@ -4131,9 +4143,9 @@ public class ManagedAgentStore implements AgentStateStore { + " 'UNKNOWN') OR state = 'RECOVERY_BLOCKED' AND operation_kind <>" + " 'TASK_CANCEL') AND operation_id <> ?", Integer.class, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys .identity( excludingOperationId == null ? "" : excludingOperationId)); return (commands != null && commands > 0) @@ -4207,7 +4219,7 @@ public class ManagedAgentStore implements AgentStateStore { owner, leaseDuration.toMillis(), now, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, @@ -4248,7 +4260,7 @@ public class ManagedAgentStore implements AgentStateStore { + " AND target_key = ? AND tenant_id = ? AND session_id = ? AND" + " operation_id = ? FOR UPDATE", Boolean.class, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, @@ -4267,7 +4279,7 @@ public class ManagedAgentStore implements AgentStateStore { } if (WorkspaceToolProfiles.lifecycleProtocol(operation.lifecycleProtocolVersion())) { if (lifecycle == null) { - throw WorkspaceLifecycleStore.blocked("workspace_close_identity_unverified"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_close_identity_unverified"); } lifecycle.verifyCompletion(operation); } @@ -4307,7 +4319,7 @@ public class ManagedAgentStore implements AgentStateStore { publicId("rcpt"), now, now, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, @@ -4349,7 +4361,7 @@ public class ManagedAgentStore implements AgentStateStore { + " AND claim_owner = ? AND claim_generation = ?" + " AND claim_until > " + ManagedSchemaTime.SQL_NOW, failureCode, publicId("rcpt"), now, now, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, operationId, owner, claimGeneration); if (updated == 1) { @@ -4399,7 +4411,7 @@ public class ManagedAgentStore implements AgentStateStore { + " AND target_key = ? AND tenant_id = ? AND session_id = ? AND operation_id = ? AND state = 'RUNNING' AND delivery_state = 'LEASED' AND claim_owner = ? AND claim_generation = ? AND claim_until > " + ManagedSchemaTime.SQL_NOW + "", duration.toMillis(), now, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, @@ -4419,7 +4431,7 @@ public class ManagedAgentStore implements AgentStateStore { + SESSION_OPERATION_KINDS + " AND target_key = ? AND tenant_id = ? AND session_id = ? AND" + " operation_id = ?", - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, @@ -4476,7 +4488,7 @@ public class ManagedAgentStore implements AgentStateStore { failureCode, Math.addExact(now, Math.max(0, availableAt - clock.millis())), now, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, @@ -4531,7 +4543,7 @@ public class ManagedAgentStore implements AgentStateStore { + " AND target_key = ? AND tenant_id = ? AND session_id = ? AND operation_id = ? AND state = 'RUNNING' AND delivery_state = 'LEASED' AND claim_owner = ? AND claim_generation = ? AND claim_until > " + ManagedSchemaTime.SQL_NOW + "", availableAt, now, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, @@ -4572,7 +4584,7 @@ public class ManagedAgentStore implements AgentStateStore { + " 'LEASED' AND claim_owner = ? AND claim_generation = ? AND" + " claim_until > " + ManagedSchemaTime.SQL_NOW, now, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId, @@ -4586,12 +4598,12 @@ public class ManagedAgentStore implements AgentStateStore { ManagedReceipts.Receipt command = ManagedReceipts.command(jdbc, tenantId, operation, idempotencyKey, false); if (command == null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key is already in use."); } if (!command.requestDigest().equals(requestDigest)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key was reused with different content."); } @@ -4658,7 +4670,7 @@ public class ManagedAgentStore implements AgentStateStore { List arguments = new ArrayList<>(); arguments.add(tenantId); arguments.add(actorId == null ? null - : ManagedWorkspaceRegistry.actorKey(tenantId, actorId)); + : com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId)); String archiveClause = switch (archiveState) { case "archived" -> " AND ma_session.status = 'ARCHIVED'"; case "unarchived" -> " AND ma_session.status <> 'ARCHIVED'"; @@ -4724,7 +4736,7 @@ public class ManagedAgentStore implements AgentStateStore { } var selectedWorktrees = rows.stream().map(row -> worktrees.get(row.sessionId())) .filter(java.util.Objects::nonNull).toList(); - return new SessionPage(List.copyOf(rows), hasMore, SessionWorktreeStore.descriptors(jdbc, selectedWorktrees)); + return new SessionPage(List.copyOf(rows), hasMore, SessionWorktreeStore.descriptors(worktreeFiles, jdbc, selectedWorktrees)); } public Map @@ -4862,7 +4874,7 @@ public class ManagedAgentStore implements AgentStateStore { ? null : new WebShellModelSelection(row.getString("model_id"), row.getLong("model_revision")), tenantId, sessionId, promptId); if (rows.size() != 1) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "turn_not_found", "The model snapshot Turn was not found"); + throw new ManagedAgentException(409, "turn_not_found", "The model snapshot Turn was not found"); } return rows.getFirst(); } @@ -4875,17 +4887,17 @@ public class ManagedAgentStore implements AgentStateStore { WorkspaceMigrationAdmission.lockTenant(jdbc, tenantId); SessionRecord session = requireLegacySessionForUpdate(tenantId, sessionId); if (session.workspace() == null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_feature", + throw new ManagedAgentException(400, "unsupported_feature", "Model selection requires a Workspace Session"); } WorkspaceAccess access = workspaces.accessOf(tenantId, actorId, session.workspace().getWorkspaceId()); if (!access.canRead()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", "The Session was not found"); + throw new ManagedAgentException(404, "session_not_found", "The Session was not found"); } - String scope = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys + String scope = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys .key("SET_MODEL", sessionId, actorId); - var namespace = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace.SESSION_COMMAND; - var scopeKind = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope.ACTOR; + var namespace = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace.SESSION_COMMAND; + var scopeKind = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope.ACTOR; ManagedReceipts.Receipt replay = ManagedReceipts.find(jdbc, namespace, tenantId, scopeKind, scope, key, false); if (replay != null) { @@ -4894,7 +4906,7 @@ public class ManagedAgentStore implements AgentStateStore { || !"SET_MODEL".equals(replay.operation()) || !java.util.Arrays.equals(actorId.getBytes(StandardCharsets.UTF_8), replay.actorKey())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "idempotency_conflict", + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key was reused with different content"); } Map result = readMap(replay.resultJson()); @@ -4902,20 +4914,20 @@ public class ManagedAgentStore implements AgentStateStore { (String) result.get("modelId"), ((Number) result.get("revision")).longValue()), true); } if (!access.atLeast(WorkspaceAccess.OPERATOR)) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, "session_operation_forbidden", + throw new ManagedAgentException(403, "session_operation_forbidden", "Only a Workspace operator may change the model"); } if (!supported || modelCatalog.isEmpty() || !workspaceFilesEnabled) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "Model selection is unavailable"); + throw new ManagedAgentException(409, "unsupported_feature", "Model selection is unavailable"); } if (modelCatalog.stream().noneMatch(option -> option.modelId().equals(modelId))) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_model", "The model is not published by this host"); + throw new ManagedAgentException(400, "unsupported_model", "The model is not published by this host"); } requirePolicyChange(session, false); long current = jdbc.queryForObject("SELECT model_revision FROM ma_session WHERE tenant_id = ?" + " AND session_id = ?", Long.class, tenantId, sessionId); if (current != expectedRevision || current >= 9_007_199_254_740_991L) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "model_selection_conflict", "The model revision changed"); + throw new ManagedAgentException(409, "model_selection_conflict", "The model revision changed"); } long revision = current + 1; long now = clock.millis(); @@ -4927,9 +4939,9 @@ public class ManagedAgentStore implements AgentStateStore { "COMPLETED", session.status(), true, now); jdbc.update("UPDATE ma_request_receipt SET result_json = ? WHERE receipt_key = ?", writeJson(Map.of("modelId", modelId, "revision", revision)), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.receiptKey( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.receiptKey( namespace, tenantId, scopeKind, scope, key)); - String sessionScope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String sessionScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); String observation = ManagedExtensionProjection.recordKey(sessionId, "hosted_context_usage", "latest"); jdbc.update("DELETE FROM ma_resource_ref WHERE session_scope_key = ? AND owner_kind =" + " 'SESSION_RECORD' AND owner_key = ?", sessionScope, observation); @@ -4966,7 +4978,7 @@ public class ManagedAgentStore implements AgentStateStore { } List args = new ArrayList<>(); args.add(tenantId); - args.add(ManagedWorkspaceRegistry.actorKey(tenantId, actorId)); + args.add(com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId)); args.addAll(sessionIds); return new HashSet<>(jdbc.queryForList("SELECT session_id FROM ma_session WHERE tenant_id=?" + " AND COALESCE(owner_actor_key, creator_actor_key)=? AND session_id IN (" @@ -4991,14 +5003,14 @@ public class ManagedAgentStore implements AgentStateStore { } String mode = row.getString("approval_mode"); if (mode == null && row.getLong("selected_revision") != 0) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "approval_snapshot_missing", "The Turn has no approval snapshot"); + throw new ManagedAgentException(409, "approval_snapshot_missing", "The Turn has no approval snapshot"); } // Legacy prompt replays must preserve the original absence of a selection. return mode == null ? null : new WebShellApprovalSelection(mode, row.getLong("approval_revision"), row.getBoolean("plan_enabled"), readPermissionRules(row.getString("permission_rules_json"))); }, tenantId, sessionId, promptId); if (rows.size() != 1) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "turn_not_found", "The Turn was not found"); + throw new ManagedAgentException(409, "turn_not_found", "The Turn was not found"); } return rows.getFirst(); } @@ -5040,22 +5052,22 @@ public class ManagedAgentStore implements AgentStateStore { WorkspaceMigrationAdmission.lockTenant(jdbc, tenantId); SessionRecord session = requireLegacySessionForUpdate(tenantId, sessionId); if (session.workspace() == null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_feature", + throw new ManagedAgentException(400, "unsupported_feature", "Approval selection requires a Workspace Session"); } WorkspaceAccess access = workspaces.accessOf(tenantId, actorId, session.workspace().getWorkspaceId()); if (!access.canRead()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", "The Session was not found"); + throw new ManagedAgentException(404, "session_not_found", "The Session was not found"); } if (!workspaces.isSessionOwner(tenantId, actorId, sessionId) || !access.atLeast(WorkspaceAccess.OPERATOR)) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, "session_operation_forbidden", + throw new ManagedAgentException(403, "session_operation_forbidden", "Only the Session owner with OPERATOR access may change approval policy"); } - String scope = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys + String scope = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys .key("SET_APPROVAL", sessionId, actorId); - var namespace = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace.SESSION_COMMAND; - var scopeKind = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope.ACTOR; + var namespace = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace.SESSION_COMMAND; + var scopeKind = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope.ACTOR; ManagedReceipts.Receipt replay = ManagedReceipts.find(jdbc, namespace, tenantId, scopeKind, scope, key, false); if (replay != null) { @@ -5064,48 +5076,48 @@ public class ManagedAgentStore implements AgentStateStore { || !"SET_APPROVAL".equals(replay.operation()) || !java.util.Arrays.equals(actorId.getBytes(StandardCharsets.UTF_8), replay.actorKey())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "idempotency_conflict", + throw new ManagedAgentException(409, "idempotency_conflict", "The idempotency key was reused with different content"); } Map result = readMap(replay.resultJson()); return new WebShellApprovalChangeResult(sessionId, (String) result.get("mode"), ((Number) result.get("revision")).longValue(), true, Boolean.TRUE.equals(result.get("planMode")), - result.get("permissionRules") instanceof Map rules ? HostedPermissionRules.nonEmpty(HostedPermissionRules.copyOf(rules)) : null); + result.get("permissionRules") instanceof Map rules ? harnessRules.nonEmptyPermissionRules(harnessRules.permissionRules(rules)) : null); } if (!supported || session.agentDefinitionDigest() != null && !agentDefinitionSupported || !workspaceFilesEnabled || !WorkspaceToolProfiles.supportsApprovalSelection(session) || WorkspaceToolProfiles.isPublicShell(session.toolProfile()) && !workspaceShellEnabled) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "Approval selection is unavailable"); + throw new ManagedAgentException(409, "unsupported_feature", "Approval selection is unavailable"); } if (!WorkspaceToolProfiles.supportsApprovalMode(mode) || "auto".equals(mode) && !autoSupported) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_approval_mode", "The approval mode is unavailable"); + throw new ManagedAgentException(400, "unsupported_approval_mode", "The approval mode is unavailable"); } var currentPolicy = jdbc.queryForMap("SELECT approval_revision, permission_rules_json FROM ma_session WHERE tenant_id=? AND session_id=?", tenantId, sessionId); Map> rules = requestedRules == null ? readPermissionRules((String) currentPolicy.get("permission_rules_json")) - : HostedPermissionRules.nonEmpty(HostedPermissionRules.copyOf(requestedRules)); + : harnessRules.nonEmptyPermissionRules(harnessRules.permissionRules(requestedRules)); if ((requestedRules != null || rules != null) && !rulesSupported) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "Permission rules are unavailable"); + throw new ManagedAgentException(409, "unsupported_feature", "Permission rules are unavailable"); } boolean planMode = requestedPlanMode == null ? Boolean.TRUE.equals(jdbc.queryForObject("SELECT plan_enabled FROM ma_session WHERE tenant_id=? AND session_id=?", Boolean.class, tenantId, sessionId)) : requestedPlanMode; if (planMode && (!planModeSupported || !supportsPlanMode(session))) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "Plan mode is unavailable"); + throw new ManagedAgentException(409, "unsupported_feature", "Plan mode is unavailable"); } if (requestedRules != null && requestedPlanMode == null) { if (hasActiveTurn(tenantId, sessionId) || hasDecidableAction(session)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "turn_active", "The Session has an active Turn or approval"); + throw new ManagedAgentException(409, "turn_active", "The Session has an active Turn or approval"); } if (hasOpenOperation(tenantId, sessionId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "session_context_busy", "The Session requires settlement"); + throw new ManagedAgentException(409, "session_context_busy", "The Session requires settlement"); } } requirePolicyChange(session, requestedPlanMode == null && (liveSupported || requestedRules != null)); long current = ((Number) currentPolicy.get("approval_revision")).longValue(); if (current != expectedRevision || current >= 9_007_199_254_740_991L || planMode && current >= 9_007_199_254_740_990L) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "approval_selection_conflict", "The approval revision changed"); + throw new ManagedAgentException(409, "approval_selection_conflict", "The approval revision changed"); } long revision = current + 1; long now = clock.millis(); @@ -5121,7 +5133,7 @@ public class ManagedAgentStore implements AgentStateStore { } jdbc.update("UPDATE ma_request_receipt SET result_json = ? WHERE receipt_key = ?", writeJson(result), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.receiptKey( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.receiptKey( namespace, tenantId, scopeKind, scope, key)); appendEvent(tenantId, sessionId, null, "session.approval.changed", result, false, null, now); @@ -5129,12 +5141,12 @@ public class ManagedAgentStore implements AgentStateStore { } private Map> readPermissionRules(String json) { - return json == null ? null : HostedPermissionRules.nonEmpty(HostedPermissionRules.copyOf(readMap(json))); + return json == null ? null : harnessRules.nonEmptyPermissionRules(harnessRules.permissionRules(readMap(json))); } private void requirePublicChildImmutable(SessionRecord session) { if (session.publicChildAgentsVersion() == 1 && findChildLineage(session.tenantId(), session.sessionId()) != null) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "unsupported_feature", "Public child Sessions cannot change their delegated execution"); + throw new ManagedAgentException(409, "unsupported_feature", "Public child Sessions cannot change their delegated execution"); } } @@ -5146,7 +5158,7 @@ public class ManagedAgentStore implements AgentStateStore { requireSessionStatus(session.status(), "ACTIVE"); requireCwdChangeRegistryFacts(session); if (!live && (hasActiveTurn(tenantId, sessionId) || hasDecidableAction(session))) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "turn_active", "The Session has an active Turn or approval"); + throw new ManagedAgentException(409, "turn_active", "The Session has an active Turn or approval"); } if ((live ? hasOpenExecutionOperation(tenantId, sessionId) : hasOpenOperation(tenantId, sessionId)) || hasPendingRenameDelivery(tenantId, sessionId) @@ -5159,7 +5171,7 @@ public class ManagedAgentStore implements AgentStateStore { || !jdbc.queryForList("SELECT 1 FROM ma_session_head WHERE tenant_id = ?" + " AND session_id = ? AND head_kind = 'MANAGED_SESSION' AND" + " (state <> 'ACTIVE' OR recovery_status <> 'READY')", tenantId, sessionId).isEmpty()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "session_context_busy", "The Session requires settlement"); + throw new ManagedAgentException(409, "session_context_busy", "The Session requires settlement"); } } @@ -5328,11 +5340,11 @@ public class ManagedAgentStore implements AgentStateStore { "SELECT csi_guard FROM ma_session WHERE tenant_id = ? AND session_id = ?", Boolean.class, tenantId, sessionId))); if (limit < 1 || limit > 100) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_limit", + throw new ManagedAgentException(400, "invalid_limit", "Limit must be between 1 and 100."); } if (afterSequence < 0) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_event_cursor", + throw new ManagedAgentException(400, "invalid_event_cursor", "Item sequence must be non-negative."); } var progress = jdbc.queryForMap("SELECT covered_sequence, projection_version, last_sequence, replay_floor_sequence FROM ma_event_stream" @@ -5343,10 +5355,10 @@ public class ManagedAgentStore implements AgentStateStore { if (covered < ((Number) progress.get("replay_floor_sequence")).longValue() || (!backward && org.apache.commons.lang3.StringUtils.isBlank(cursor) && covered < ((Number) progress.get("last_sequence")).longValue())) { - throw new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, "message_projection_pending", + throw new ManagedAgentException(503, "message_projection_pending", "Retry after pending messages have been projected."); } - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); MessagePosition position = new MessagePosition(1, scope, backward, projection, covered, backward ? covered + 1 : -1, ""); if (org.apache.commons.lang3.StringUtils.isNotBlank(cursor)) { @@ -5360,7 +5372,7 @@ public class ManagedAgentStore implements AgentStateStore { pageArguments.add(afterSequence); } pageArguments.addAll(List.of(position.sequence(), position.sequence(), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.identity(position.item()), limit + 1)); + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.identity(position.item()), limit + 1)); List candidates = jdbc.query( "SELECT m.item_id, m.first_sequence, OCTET_LENGTH(m.attributes_json) +" + " OCTET_LENGTH(m.parts_json) + COALESCE(OCTET_LENGTH(r.inline_bytes), 0) AS byte_length" @@ -5393,7 +5405,7 @@ public class ManagedAgentStore implements AgentStateStore { } List arguments = new ArrayList<>(List.of(tenantId, sessionId)); selected.forEach(item -> arguments.add( - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.identity(item.id()))); + ManagedSchemaKeys.identity(item.id()))); // Bodies are fetched only after the metadata-only lookahead has selected a bounded page. List rows = withParts(jdbc.query( "SELECT " + ManagedSchemaProjection.MESSAGE.columns() + " FROM ma_message WHERE tenant_id = ?" @@ -5415,7 +5427,7 @@ public class ManagedAgentStore implements AgentStateStore { List turns = items.stream().map(ItemRecord::turnId).distinct().toList(); List arguments = new ArrayList<>(List.of(tenantId, sessionId, covered)); turns.forEach(turn -> arguments.add( - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.identity(turn))); + ManagedSchemaKeys.identity(turn))); arguments.add(tenantId); arguments.add(sessionId); var sizes = jdbc.query("SELECT e.sequence_id, OCTET_LENGTH(e.data_json) AS byte_length FROM ma_public_event e" @@ -5457,7 +5469,7 @@ public class ManagedAgentStore implements AgentStateStore { throw new IllegalArgumentException(); } if (position.projection() != projection) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "message_projection_changed", + throw new ManagedAgentException(409, "message_projection_changed", "Reload message history after its projection changed."); } if (position.through() > covered) { @@ -5465,7 +5477,7 @@ public class ManagedAgentStore implements AgentStateStore { } return position; } catch (java.io.IOException | IllegalArgumentException invalid) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_message_cursor", + throw new ManagedAgentException(400, "invalid_message_cursor", "The message cursor is invalid for this Session and direction."); } } @@ -5480,7 +5492,7 @@ public class ManagedAgentStore implements AgentStateStore { } private static ManagedAgentException messagePageTooLarge() { - return new ManagedAgentException(HttpStatus.PAYLOAD_TOO_LARGE, "message_page_too_large", + return new ManagedAgentException(413, "message_page_too_large", "The message page exceeds its byte budget."); } @@ -5527,7 +5539,7 @@ public class ManagedAgentStore implements AgentStateStore { tenantId, sessionId); if (rows.isEmpty()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", + throw new ManagedAgentException(404, "session_not_found", "The Session was not found."); } return rows.getFirst(); @@ -6306,19 +6318,19 @@ public class ManagedAgentStore implements AgentStateStore { parts); } } - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); var iterator = parts.entrySet().iterator(); while (iterator.hasNext()) { var entry = iterator.next(); List retained = entry.getValue(); if (retained.isEmpty()) { ManagedProjectionResourceCollector.release(jdbc, scope, "MESSAGE", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(scope, entry.getKey())); + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scope, entry.getKey())); jdbc.update( "DELETE FROM ma_resource_ref WHERE session_scope_key = ? AND owner_kind =" + " 'MESSAGE' AND owner_key = ?", scope, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( scope, entry.getKey())); jdbc.update( "DELETE FROM ma_message WHERE tenant_id = ? AND session_id = ? AND item_id" @@ -6721,14 +6733,14 @@ public class ManagedAgentStore implements AgentStateStore { + " AND session_status_before IS NOT NULL" + " ORDER BY gmt_create DESC LIMIT 1", (rows, row) -> rows.getString(1), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionOperationTarget(sessionId), tenantId, sessionId) .stream().findFirst(); } public SessionRecord requireSession(String tenantId, String sessionId) { return findSession(tenantId, sessionId).orElseThrow(() -> - new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", + new ManagedAgentException(404, "session_not_found", "The Session was not found.")); } @@ -6941,13 +6953,13 @@ public class ManagedAgentStore implements AgentStateStore { private void flushParts( String tenantId, String sessionId, Map> projectedParts) { - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); for (var entry : projectedParts.entrySet()) { String item = entry.getKey(); byte[] bytes = writeJson(entry.getValue()).getBytes(java.nio.charset.StandardCharsets.UTF_8); String owner = - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( scope, item); Map previous = jdbc.queryForMap( @@ -6969,7 +6981,7 @@ public class ManagedAgentStore implements AgentStateStore { if (bytes.length > 64 * 1024) { String digest = WorkspaceEvidenceResources.digest(bytes); String id = "message:" + owner + ":" + revision + ":" + digest; - resource = ManagedSessionStore.resourceKey(scope, id); + resource = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, id); try { jdbc.update( "INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, state, gmt_create, last_verified_at) VALUES (?, ?, ?, ?, ?, 'managed-message-parts', 1, ?, ?, 'MYSQL_INLINE', ?, 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ", " + ManagedSchemaTime.SQL_NOW + ")", @@ -7073,7 +7085,7 @@ public class ManagedAgentStore implements AgentStateStore { return bytes; }, resourceKey, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, "managed-message-parts"); @@ -7173,7 +7185,7 @@ public class ManagedAgentStore implements AgentStateStore { tenantId, sessionId); } catch (EmptyResultDataAccessException error) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, + throw new ManagedAgentException(404, "session_not_found", "The Session was not found."); } } @@ -7181,7 +7193,7 @@ public class ManagedAgentStore implements AgentStateStore { private TurnRecord requireTurn(String tenantId, String sessionId, String turnId) { return findTurn(tenantId, sessionId, turnId).orElseThrow(() -> - new ManagedAgentException(HttpStatus.NOT_FOUND, "turn_not_found", + new ManagedAgentException(404, "turn_not_found", "The Turn was not found.")); } @@ -7193,7 +7205,7 @@ public class ManagedAgentStore implements AgentStateStore { + " turn_id = ? FOR UPDATE", turnMapper, tenantId, sessionId, turnId); } catch (EmptyResultDataAccessException error) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "turn_not_found", + throw new ManagedAgentException(404, "turn_not_found", "The Turn was not found."); } } @@ -7265,7 +7277,7 @@ public class ManagedAgentStore implements AgentStateStore { return null; } try { - return ManagedWorkspaceRegistry.actorKey(tenantId, actorId); + return com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId); } catch (IllegalArgumentException error) { return null; } @@ -7355,7 +7367,7 @@ public class ManagedAgentStore implements AgentStateStore { private void requireNoOpenOperation(String tenantId, String sessionId) { if (hasOpenOperation(tenantId, sessionId)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "session_operation_active", "The Session already has a lifecycle operation in progress."); } @@ -7377,7 +7389,7 @@ public class ManagedAgentStore implements AgentStateStore { if ("ACTIVE".equals(status) && (hasLiveTurn(session.tenantId(), session.sessionId()) || session.workspace() != null && hasDecidableAction(session))) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "turn_active", + throw new ManagedAgentException(409, "turn_active", "The Session has an active Turn."); } } @@ -7405,7 +7417,7 @@ public class ManagedAgentStore implements AgentStateStore { "SELECT options_json FROM ma_session_record WHERE session_scope_key = ? AND" + " record_kind = 'ACTION' AND state = 'requested' AND tenant_id = ?" + " AND session_id = ?", String.class, - ManagedSessionStore.sessionScopeKey(session.tenantId(), session.sessionId()), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(session.tenantId(), session.sessionId()), session.tenantId(), session.sessionId())) { try { if (now < objectMapper.readTree(options).path("expiresAt").asLong()) { @@ -7463,7 +7475,7 @@ public class ManagedAgentStore implements AgentStateStore { } private static ManagedAgentException sessionStateConflict(String status) { - return new ManagedAgentException(HttpStatus.CONFLICT, + return new ManagedAgentException(409, "session_state_conflict", "The Session is " + status.toLowerCase(Locale.ROOT) + " and cannot perform this operation."); @@ -7728,4 +7740,12 @@ public class ManagedAgentStore implements AgentStateStore { createdAt, updatedAt, revision, content); } } + @Override + @Transactional + public Admission insertSessionCommand(String tenantId, String operation, + String idempotencyKey, String requestDigest, String agentId, + String requestedRevision, String title, + List> input, String payloadDigest) { + return AgentStateStore.super.insertSessionCommand(tenantId, operation, idempotencyKey, requestDigest, agentId, requestedRevision, title, input, payloadDigest); + } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedArtifactReader.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedArtifactReader.java similarity index 87% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedArtifactReader.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedArtifactReader.java index 5ccb8168d74..f49f7d6fd70 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedArtifactReader.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedArtifactReader.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedToolResultStore.Artifact; +import com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.Artifact; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.ObjectProvider; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import java.io.InputStream; @@ -14,7 +14,8 @@ import java.util.Map; import java.util.stream.Collectors; @Component -public class ManagedArtifactReader { +public class ManagedArtifactReader + implements com.alibaba.qwen.code.managedagent.repository.ManagedArtifactReader { private static final ObjectMapper JSON = new ObjectMapper(); private final ObjectProvider publications; @@ -72,7 +73,7 @@ public class ManagedArtifactReader { } } - public InputStream open(Artifact artifact, ToolPublicationRetentionStore.ReadLease lease, Runnable guard) { + public InputStream open(Artifact artifact, com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease, Runnable guard) { lease.requireScope(artifact.source().sessionKey()); Runnable protectedGuard = () -> { lease.check(); @@ -93,7 +94,7 @@ public class ManagedArtifactReader { } public byte[] readRange(Artifact artifact, long offset, int length, - ToolPublicationRetentionStore.ReadLease lease, Runnable guard) { + com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease, Runnable guard) { ToolPublicationContract.require(offset >= 0 && length >= 0 && length <= 1024 * 1024 && offset <= artifact.descriptor().path("byte_length").asLong() && length <= artifact.descriptor().path("byte_length").asLong() - offset, @@ -115,12 +116,12 @@ public class ManagedArtifactReader { }); } - public ToolPublicationRetentionStore.ReadLease lease(Artifact artifact) { + public com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease(Artifact artifact) { return data().readLease(artifact.source().sessionKey()); } ToolPublicationDataStore.VerifiedStream verified(Artifact artifact, - ToolPublicationRetentionStore.ReadLease lease, Runnable guard) { + com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease, Runnable guard) { guard.run(); var source = artifact.source(); var binding = artifact.binding(); @@ -150,7 +151,7 @@ public class ManagedArtifactReader { private ToolPublicationDataStore data() { var data = publications.getIfAvailable(); if (data == null) { - throw new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, "artifact_unavailable", + throw new ManagedAgentException(503, "artifact_unavailable", "Artifact storage is unavailable."); } return data; diff --git a/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedConfigStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedConfigStore.java new file mode 100644 index 00000000000..9a9427f854f --- /dev/null +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedConfigStore.java @@ -0,0 +1,19 @@ +package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; +import org.springframework.jdbc.core.JdbcTemplate; +final class ManagedConfigStore { + private ManagedConfigStore() { } + public static void requireOpen(JdbcTemplate jdbc, String tenantId, String sessionId) { + if (blocked(jdbc, tenantId, sessionId)) { + throw new ManagedAgentException(409, + "session_config_recovery_required", "The Session configuration operation must be reconciled first."); + } + } + + public static boolean blocked(JdbcTemplate jdbc, String tenantId, String sessionId) { + return !jdbc.queryForList("SELECT record_key FROM ma_session_record WHERE session_scope_key=? AND tenant_id=?" + + " AND session_id=? AND record_kind='DOMAIN' AND domain='config_install' AND record_id=?" + + " AND settled_at IS NULL LIMIT 1 FOR UPDATE", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + tenantId, sessionId, ManagedMcpSettings.RECORD_ID).isEmpty(); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunch.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunch.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunch.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunch.java index e05bcee23d7..ef5184ff1fe 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunch.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunch.java @@ -1,11 +1,13 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedConfigInstall; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.codec.digest.DigestUtils; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import java.io.ByteArrayOutputStream; @@ -24,7 +26,7 @@ public final class ManagedExtensionAgentLaunch { "modelId", "modelRevision", "modelRoute", "skillsRef", "extensionAgent"), "Package agent launch"); JsonNode agent = launch.get("extensionAgent"); ManagedExtensionRecords.closed(agent, Set.of("configRevision", "bundleRef", "pin", "contributionsRef", "agentName", "toolNames"), "Package agent binding"); - ManagedConfigInstall.requireOpen(jdbc, parent.tenantId(), parent.sessionId()); + ManagedConfigStore.requireOpen(jdbc, parent.tenantId(), parent.sessionId()); JsonNode current = ManagedMcpSettings.find(jdbc, parent.tenantId(), parent.sessionId()); for (int step = 0; ManagedConfigInstall.isPackageConfig(current) && !"active".equals(current.path("phase").asText()); step++) { if (step >= 4096 || current.path("previousRecordRef").isNull()) { @@ -148,7 +150,7 @@ public final class ManagedExtensionAgentLaunch { if (bytes.length != length || !DigestUtils.sha256Hex(bytes).equals(manifest.path("digest").asText())) { throw refused(); } - JsonNode contributions = ManagedExtensionRecordStore.parse(new String(bytes, StandardCharsets.UTF_8)); + JsonNode contributions = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(new String(bytes, StandardCharsets.UTF_8)); if (contributions == null) { throw refused(); } @@ -189,6 +191,6 @@ public final class ManagedExtensionAgentLaunch { } private static ManagedAgentException refused() { - return new ManagedAgentException(HttpStatus.CONFLICT, "child_launch_snapshot_invalid", "The package agent does not belong to the parent's active configuration."); + return new ManagedAgentException(409, "child_launch_snapshot_invalid", "The package agent does not belong to the parent's active configuration."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionCommandVerifier.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionCommandVerifier.java similarity index 92% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionCommandVerifier.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionCommandVerifier.java index d31f7a8b5b3..bb53d82e876 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionCommandVerifier.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionCommandVerifier.java @@ -1,8 +1,11 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedConfigInstall; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedToolStageRecords; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeExtensionCommandVerifier; @@ -60,7 +63,7 @@ public final class ManagedExtensionCommandVerifier implements RuntimeExtensionCo + " WHERE record_kind='DOMAIN' AND session_scope_key=? AND tenant_id=? AND workspace_id=?" + " AND session_id=? AND domain='tool_stage' AND record_id=?", (row, index) -> new Projection(row.getLong(1), row.getString(2), row.getString(3)), - ManagedSessionStore.sessionScopeKey(tenant, harness), tenant, session.getScope().getWorkspaceId(), harness, stageId); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, harness), tenant, session.getScope().getWorkspaceId(), harness, stageId); check(projection.size() == 1); Projection latest = projection.getFirst(); check(latest.revision() == grant.get("operationRevision").longValue() @@ -100,7 +103,7 @@ public final class ManagedExtensionCommandVerifier implements RuntimeExtensionCo && approval.size() == 1 && approval.getFirst().equals(permission.get("approvalMode").textValue()) && (!"shell".equals(stage.get("toolName").textValue()) || WorkspaceToolProfiles.isPublicShell(owner.toolProfile()))); check("running".equals(run.get("state").textValue()) && "dispatch_started".equals(run.get("execution").textValue())); - ManagedConfigInstall.requireOpen(jdbc, tenant, harness); + ManagedConfigStore.requireOpen(jdbc, tenant, harness); JsonNode configuration = ManagedMcpSettings.find(jdbc, tenant, harness); for (int step = 0; ManagedConfigInstall.isPackageConfig(configuration) && !"active".equals(configuration.path("phase").asText()); step++) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordStore.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordStore.java index 8534665acf5..85d12485fec 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordStore.java @@ -1,18 +1,25 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedConfigInstall; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.Body; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.TaskProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedHistoryMaintenanceRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedHookRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedMcpRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedTeamRecords; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellModelContext; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection.Body; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection.TaskProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredResource; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.StreamReadConstraints; import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -26,7 +33,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.dao.DuplicateKeyException; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @@ -56,7 +62,20 @@ import java.util.regex.Pattern; * committed. */ @Repository -public class ManagedExtensionRecordStore { +public class ManagedExtensionRecordStore + implements com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore { + private static final ObjectMapper JSON = JsonMapper.builder(JsonFactory + .builder().streamReadConstraints(StreamReadConstraints + .builder().maxNestingDepth(ManagedSessionStoreModels + .MAX_JSON_DEPTH).build()).build()) + .enable(StreamReadFeature.STRICT_DUPLICATE_DETECTION) + .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS).build(); + public com.fasterxml.jackson.databind.JsonNode currentMcpSettings(String tenant, String session) { return ManagedMcpSettings.find(jdbc, tenant, session); } + + public static final String OPENING_COMMAND_QUERY = "SELECT COUNT(*) FROM" + + " ma_session_record WHERE record_kind = 'DOMAIN' AND" + + " session_scope_key = ? AND operation_hash = ?"; + private static final Logger LOG = LoggerFactory.getLogger( ManagedExtensionRecordStore.class); @@ -64,20 +83,12 @@ public class ManagedExtensionRecordStore { * A session message its Session's lineage refuses (H4d): only this store holds a child's * lineage, so it is a rollbackable non-commit. */ - public static final String ERROR_MESSAGE_LINEAGE = - "session_message_lineage_refused"; /** * A session command its Session's lineage refuses (H4e-b3): the same carve-out as the message * channel's, under its own code. */ - public static final String ERROR_COMMAND_LINEAGE = - "session_command_lineage_refused"; - public static final String ERROR_REJECTED = - "managed_session_extension_record_rejected"; - public static final String OPENING_COMMAND_QUERY = "SELECT COUNT(*) FROM" - + " ma_session_record WHERE record_kind = 'DOMAIN' AND" - + " session_scope_key = ? AND operation_hash = ?"; + private static final String EVENT_SUBTYPE = "managed_session_event_v1"; private static final String HEADER_SUBTYPE = "managed_session_header_v1"; private static final String COMMIT_SUBTYPE = "managed_session_commit_v1"; @@ -108,12 +119,7 @@ public class ManagedExtensionRecordStore { // keys, no trailing content, no deeper nesting, and, checked after // parsing, only finite numbers. The store never accepts a line or a body // that the authority could not read back. - private static final ObjectMapper JSON = JsonMapper.builder(JsonFactory - .builder().streamReadConstraints(StreamReadConstraints - .builder().maxNestingDepth(ManagedSessionStoreModels - .MAX_JSON_DEPTH).build()).build()) - .enable(StreamReadFeature.STRICT_DUPLICATE_DETECTION) - .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS).build(); + private final JdbcTemplate jdbc; private final AgentStateStore sessions; private final ManagedTaskEventStore taskEvents; @@ -138,26 +144,17 @@ public class ManagedExtensionRecordStore { } /** A task's projection row and the resource holding its record's latest committed body. */ - public record TaskRow(String taskId, String kind, - TaskProjection projection, String resourceId) { - } - - public record TaskPage(List tasks, boolean hasMore) { - } /** * H4f: the record a task id names and its latest committed body, which a task cancel reconciles * against — the authority's own durable statement, mirrored in the transaction that committed * it. */ - public record TaskTarget(String domain, String recordId, String kind, - String state, long revision, JsonNode body) { - } public Optional findRecord(String tenantId, String sessionId, String domain, String recordId) { Body body = ManagedExtensionProjection.RECORD_BODIES.get(domain); require(body != null, "Unknown extension record domain."); - JsonNode record = recordBody(ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionId, + JsonNode record = recordBody(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), sessionId, domain, recordId, id -> readResource(tenantId, sessionId, id)); if (record != null) { body.require().accept(record); @@ -175,7 +172,7 @@ public class ManagedExtensionRecordStore { + " AND session_id = ? AND domain = ?" + " ORDER BY gmt_create, record_key", (result, row) -> result.getString("record_resource_id"), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, domain); return ids.stream().map(id -> { JsonNode record = readBody(readResource(tenantId, sessionId, id)); @@ -199,7 +196,7 @@ public class ManagedExtensionRecordStore { arguments.add(tenantId); for (String sessionId : sessionIds) { keys.add("(r.session_scope_key = ? AND r.record_key = ? AND r.session_id = ?)"); - arguments.add(ManagedSessionStore.sessionScopeKey(tenantId, sessionId)); + arguments.add(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId)); arguments.add(ManagedExtensionProjection.recordKey(sessionId, MODEL_CONTEXT_DOMAIN, MODEL_CONTEXT_ID)); arguments.add(sessionId); @@ -240,7 +237,7 @@ public class ManagedExtensionRecordStore { } private static WebShellModelContext modelContext(byte[] bytes, long observedAt) { - JsonNode body = parse(new String(bytes, StandardCharsets.UTF_8)); + JsonNode body = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(new String(bytes, StandardCharsets.UTF_8)); if (body == null || !body.path("version").isIntegralNumber() || !body.path("version").canConvertToLong() || body.path("version").longValue() != 1) { @@ -274,7 +271,7 @@ public class ManagedExtensionRecordStore { + " = ? AND domain = 'hook_registration' AND settled_at IS NOT NULL" + " ORDER BY first_sequence DESC", (result, row) -> result.getString("record_resource_id"), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId) + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId) .stream().map(id -> { JsonNode record = readBody(readResource(tenantId, sessionId, id)); ManagedHookRecords.requireRegistration(record); @@ -304,7 +301,7 @@ public class ManagedExtensionRecordStore { Long.toString(result.getLong("revision")), result.getString("record_resource_id")}; }, - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), recordKey).stream().findFirst().map(row -> { JsonNode body = readBody(readResource(tenantId, sessionId, row[5])); @@ -339,7 +336,7 @@ public class ManagedExtensionRecordStore { result.getString("kind"), result.getInt("schema_version"), result.getLong("byte_length"), result.getString("sha256"), result.getBytes("inline_bytes")), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, resourceId).stream().findFirst() .orElseThrow(() -> rejected("Missing committed MCP resource.")); require(resource.bytes() != null @@ -421,9 +418,9 @@ public class ManagedExtensionRecordStore { String lastSubtype = null; for (int index = 0; index < lines.length; index++) { JsonNode record = - nativeRecords == null ? parse(lines[index]) : nativeRecords.get(index); + nativeRecords == null ? com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(lines[index]) : nativeRecords.get(index); if (record == null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, ManagedSessionStoreModels.ERROR_INVALID_REQUEST, "Record line " + (index + 1) + " is not a JSON object" + " the Session authority can read."); @@ -508,7 +505,7 @@ public class ManagedExtensionRecordStore { ManagedHistoryMaintenanceRecords.requireRecord(maintenance); if ("compressed".equals(maintenance.path("outcome").asText())) { long compaction = maintenance.path("compactionRevision").longValue(); - boolean sameTransaction = java.util.Arrays.stream(lines).map(ManagedExtensionRecordStore::parse) + boolean sameTransaction = java.util.Arrays.stream(lines).map(com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore::parse) .filter(Objects::nonNull).map(line -> line.path("managedSession")) .anyMatch(candidate -> "context.compacted".equals(candidate.path("kind").asText()) && candidate.path("sequence").longValue() == compaction @@ -559,7 +556,7 @@ public class ManagedExtensionRecordStore { ManagedExtensionRecords.durableRef(ref, "compaction.summaryRef"); StoredResource resource = resources.apply(ref.get("resourceId").textValue()); requireReference(resource, ref); - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); String key = ManagedExtensionProjection.recordKey(sessionId, "hosted_context_compaction", "latest"); long sequence = event.get("sequence").longValue(); int inserted = jdbc.update("INSERT IGNORE INTO ma_session_record (record_kind, session_scope_key, record_key, tenant_id," @@ -619,7 +616,7 @@ public class ManagedExtensionRecordStore { JsonNode ref = payload.path("usageRef"); if (!jdbc.queryForList("SELECT 1 FROM ma_session_record WHERE session_scope_key = ?" + " AND record_kind = 'DOMAIN' AND domain = 'history_maintenance' AND settled_at IS NULL LIMIT 1", - ManagedSessionStore.sessionScopeKey(tenantId, sessionId)).isEmpty()) { + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId)).isEmpty()) { return; } if (!"output_committed".equals(payload.path("state").textValue()) @@ -642,7 +639,7 @@ public class ManagedExtensionRecordStore { requireReference(resource, ref); require(resource.byteLength() <= ManagedSessionStoreModels.MAX_INLINE_RESOURCE_BYTES, "Hosted model usage exceeds the inline resource limit."); - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); String key = ManagedExtensionProjection.recordKey(sessionId, MODEL_CONTEXT_DOMAIN, MODEL_CONTEXT_ID); long sequence = event.get("sequence").longValue(); @@ -703,7 +700,7 @@ public class ManagedExtensionRecordStore { Long settledAt = ManagedConfigInstall.barrier(settings) ? null : occurredAt; long revision = settings.get("revision").longValue(); require(revision == (previous == null ? 1 : previous.get("revision").longValue() + 1), "MCP settings revision conflicts"); - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); String key = ManagedExtensionProjection.recordKey(sessionId, ManagedMcpSettings.DOMAIN, ManagedMcpSettings.RECORD_ID); if (previous == null) { jdbc.update("INSERT INTO ma_session_record (record_kind, session_scope_key, record_key, tenant_id, workspace_id," @@ -729,7 +726,7 @@ public class ManagedExtensionRecordStore { } private static void requireConfigurationBinding(JsonNode settings, String[] lines) { - List bindings = java.util.Arrays.stream(lines).map(ManagedExtensionRecordStore::parse) + List bindings = java.util.Arrays.stream(lines).map(com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore::parse) .filter(Objects::nonNull).map(line -> line.path("managedSession")) .filter(event -> "config.bound".equals(event.path("kind").asText())).map(event -> event.path("payload")).toList(); if (!"committed".equals(settings.path("phase").asText())) { @@ -761,7 +758,7 @@ public class ManagedExtensionRecordStore { + " WHERE record_kind = 'DOMAIN' AND session_scope_key = ?" + " AND record_key = ? FOR UPDATE", (result, rowNum) -> result.getString(1), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), ManagedExtensionProjection.recordKey(sessionId, domain, recordId)); // The lineage read must be a locking read on purpose: under @@ -789,7 +786,7 @@ public class ManagedExtensionRecordStore { // non-commit (an authority kept alive for the corrected // retry), never as a write failure that latches the parent's // Session log shut behind it. - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "child_run_lineage_minted", "Child run " + recordId + "'s never-started verdict does" + " not name the Session its creation minted."); @@ -806,9 +803,9 @@ public class ManagedExtensionRecordStore { boolean dispatch = false; var revisions = new java.util.HashMap(); for (String line : new String(bytes, StandardCharsets.UTF_8).split("\n")) { - JsonNode parsed = parse(line); + JsonNode parsed = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(line); if (parsed == null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, ManagedSessionStoreModels.ERROR_INVALID_REQUEST, "Record line is not a JSON object the Session authority can read."); } @@ -816,12 +813,12 @@ public class ManagedExtensionRecordStore { JsonNode payload = event.path("payload"); if ("input.accepted".equals(event.path("kind").asText()) || "tool.intent".equals(event.path("kind").asText())) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); } if ("model.attempt".equals(event.path("kind").asText()) && "started".equals(payload.path("state").asText())) { if (v2) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); } dispatch = true; } @@ -839,20 +836,20 @@ public class ManagedExtensionRecordStore { "schedule", "automation_run", "session_message", "channel_delivery", "tool_stage").contains(domain))) { if (!List.of("monitor_run", "child_run", "child_acceptance", "mcp_configuration", "mcp_operation", "hook_execution", "hook_registration", "schedule", "automation_run", "session_message", "channel_delivery", "tool_stage").contains(domain)) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); } JsonNode next = readBody(resources.apply(payload.path("recordRef").path("resourceId").asText())); JsonNode previous = previousLifecycleRecord(tenantId, sessionId, domain, next, revisions); if (previous == null || requiresLifecycleDispatch(previous, next) || !ManagedExtensionProjection.RECORD_BODIES.get(domain).isSuccessor().test(previous, next)) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); } continue; } if (!hookDomain && !"child_run".equals(domain) && !"child_acceptance".equals(domain)) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); } if (hookDomain) { JsonNode next = readBody(resources.apply( @@ -871,9 +868,9 @@ public class ManagedExtensionRecordStore { Function resources) { var revisions = new java.util.HashMap(); for (String line : new String(bytes, StandardCharsets.UTF_8).split("\n")) { - JsonNode parsed = parse(line); + JsonNode parsed = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(line); if (parsed == null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, ManagedSessionStoreModels.ERROR_INVALID_REQUEST, "Record line is not a JSON object the Session authority can read."); } @@ -882,19 +879,19 @@ public class ManagedExtensionRecordStore { JsonNode payload = event.path("payload"); if ("input.accepted".equals(kind) || "tool.intent".equals(kind) || "model.attempt".equals(kind) && "started".equals(payload.path("state").asText())) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); } if (!"domain.committed".equals(kind)) { continue; } String domain = payload.path("domain").asText(); if (!List.of("hook_execution", "hook_registration").contains(domain)) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); } JsonNode next = readBody(resources.apply(payload.path("recordRef").path("resourceId").asText())); JsonNode previous = previousLifecycleRecord(tenantId, sessionId, domain, next, revisions); if (previous == null || requiresLifecycleDispatch(previous, next)) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); } } } @@ -910,7 +907,7 @@ public class ManagedExtensionRecordStore { + " record_kind = 'DOMAIN' AND session_scope_key = ? AND" + " record_key = ? AND tenant_id = ? AND session_id = ? AND" + " domain = ? AND record_id = ?", - String.class, ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + String.class, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), ManagedExtensionProjection.recordKey(sessionId, domain, id), tenantId, sessionId, domain, id).stream().map(resource -> { JsonNode record = readBody(readResource(tenantId, sessionId, resource)); ManagedExtensionProjection.RECORD_BODIES.get(domain).require().accept(record); @@ -929,7 +926,7 @@ public class ManagedExtensionRecordStore { JsonNode after = next.path("run"); String execution = before.path("execution").asText(); if (!List.of("", "intent").contains(execution) && !before.path("runtime").equals(after.path("runtime"))) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_admission_closed"); } return "intent".equals(execution) && !List.of("intent", "not_started_proven").contains(after.path("execution").asText()); } @@ -937,7 +934,7 @@ public class ManagedExtensionRecordStore { public TaskPage listTasks(String tenantId, String sessionId, Long beforeCreatedAt, String beforeTaskId, int limit) { List arguments = new ArrayList<>(); - arguments.add(ManagedSessionStore.sessionScopeKey(tenantId, + arguments.add(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId)); String cursor = ""; if (beforeCreatedAt != null) { @@ -967,7 +964,7 @@ public class ManagedExtensionRecordStore { return jdbc.query( "SELECT " + ManagedSchemaProjection.SESSION_RECORD.columns() + " FROM ma_session_record WHERE record_kind = 'DOMAIN' AND session_scope_key = ? AND record_key = ? AND task_kind IS NOT NULL", (result, row) -> taskRow(result, tenantId, sessionId), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), recordKey).stream().findFirst(); } @@ -976,7 +973,6 @@ public class ManagedExtensionRecordStore { * committed record names it (the attach, or a verdict that names a minted child), else null. * The record body is the only place that fact lives; no other kind has a child Session. */ - public record ChildTaskDetails(String sessionId, Boolean stopRequested, String approvalMode) { } public Long activeChildCount(String tenantId, String sessionId) { if (sessions == null || sessions.requireSession(tenantId, sessionId).publicChildAgentsVersion() != 1) { @@ -986,7 +982,7 @@ public class ManagedExtensionRecordStore { + " record_kind = 'DOMAIN' AND session_scope_key = ? AND tenant_id = ?" + " AND session_id = ? AND task_kind = 'child_agent'" + " AND task_state NOT IN ('completed', 'failed', 'cancelled')", - Long.class, ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId); + Long.class, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId); } public String childSessionId(String tenantId, String sessionId, TaskRow task) { @@ -1132,7 +1128,7 @@ public class ManagedExtensionRecordStore { List inputs = new ArrayList<>(); for (int index = 0; index < Math.min(eventCount, lines.length); index++) { - JsonNode event = parse(lines[index]); + JsonNode event = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(lines[index]); if (event != null && EVENT_SUBTYPE.equals(event.path("subtype") .textValue()) && "input.accepted".equals(event .path("managedSession").path("kind").textValue())) { @@ -1309,7 +1305,7 @@ public class ManagedExtensionRecordStore { String recordId = body.recordId().apply(record); String recordKey = ManagedExtensionProjection.recordKey(sessionId, domain, recordId); - String scopeKey = ManagedSessionStore.sessionScopeKey(tenantId, + String scopeKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); ManagedHookRecords.AdmissionKeys keys = ManagedHookRecords.admissionKeys(domain, record); @@ -1514,7 +1510,7 @@ public class ManagedExtensionRecordStore { record.get("targetSessionId")), binding); require(record.get("automationRunId").textValue().equals( - AutomationLedgerStore.automationRunId( + com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationRunId( record.get("scheduleId").textValue(), record.get("occurrenceKey").textValue())), "Automation run id must be derived from its definition" @@ -1729,7 +1725,7 @@ public class ManagedExtensionRecordStore { } private static JsonNode readBody(StoredResource resource) { - JsonNode record = parse(new String(resource.bytes(), + JsonNode record = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(new String(resource.bytes(), StandardCharsets.UTF_8)); require(record != null, "The Stage H record is not a JSON object the" + " Session authority can read."); @@ -1737,28 +1733,8 @@ public class ManagedExtensionRecordStore { } /** A JSON object as the authority's reader parses it, or null. */ - public static JsonNode parse(String text) { - try { - JsonNode node = JSON.readTree(text); - return node != null && node.isObject() && finite(node) ? node - : null; - } catch (JsonProcessingException error) { - return null; - } - } /** JavaScript reads a number past the double range as an infinity. */ - private static boolean finite(JsonNode node) { - if (node.isNumber()) { - return Double.isFinite(node.doubleValue()); - } - for (JsonNode child : node) { - if (!finite(child)) { - return false; - } - } - return true; - } private static String sha256(String value) { return sha256(value.getBytes(StandardCharsets.UTF_8)); @@ -1832,7 +1808,7 @@ public class ManagedExtensionRecordStore { && (outbound ? target == null || target.equals(parent) : sender.equals(parent)); if (!follows) { - throw new ManagedAgentException(HttpStatus.CONFLICT, ERROR_MESSAGE_LINEAGE, + throw new ManagedAgentException(409, ERROR_MESSAGE_LINEAGE, outbound ? "Session message to a parent must follow this" + " Session's lineage." @@ -1877,7 +1853,7 @@ public class ManagedExtensionRecordStore { boolean follows = parent != null && childRunId.equals(edge) && (target == null || target.equals(parent)); if (!follows) { - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, ERROR_COMMAND_LINEAGE, "Session command to a parent must follow this" + " Session's lineage."); @@ -2156,7 +2132,7 @@ public class ManagedExtensionRecordStore { } private static ManagedAgentException rejected(String message) { - return new ManagedAgentException(HttpStatus.CONFLICT, ERROR_REJECTED, message); + return new ManagedAgentException(409, ERROR_REJECTED, message); } private record StoredRow(String domain, String recordId, long revision, diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedLegacySessionGuard.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedLegacySessionGuard.java similarity index 87% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedLegacySessionGuard.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedLegacySessionGuard.java index 8ee657b5a94..42f86ae6caa 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedLegacySessionGuard.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedLegacySessionGuard.java @@ -1,7 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.JdbcTemplate; @@ -32,12 +31,10 @@ final class ManagedLegacySessionGuard { statement.setQueryTimeout(10); statement.setBytes( 1, - com.alibaba.qwen.code.runtimebroker.managedschema - .ManagedSchemaKeys.identity(tenantId)); + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.identity(tenantId)); statement.setBytes( 2, - com.alibaba.qwen.code.runtimebroker.managedschema - .ManagedSchemaKeys.identity(sessionId)); + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.identity(sessionId)); try (var rows = statement.executeQuery()) { return rows.next(); } @@ -51,7 +48,7 @@ final class ManagedLegacySessionGuard { static void requireLegacyMutation(boolean privateSession) { if (privateSession) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "csi_managed_mutation_unavailable", + throw new ManagedAgentException(409, "csi_managed_mutation_unavailable", "The private CSI Session does not admit legacy Managed Agent mutations."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMcpSettings.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMcpSettings.java similarity index 92% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMcpSettings.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMcpSettings.java index 50abaf6c985..13bf1d3b0d1 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMcpSettings.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMcpSettings.java @@ -1,5 +1,8 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedConfigInstall; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.jdbc.core.JdbcTemplate; @@ -77,7 +80,7 @@ public final class ManagedMcpSettings { } public static JsonNode find(JdbcTemplate jdbc, String tenantId, String sessionId) { - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); return jdbc.query("SELECT u.inline_bytes, u.byte_length, u.sha256, u.kind, u.schema_version, r.revision," + " u.state, u.storage_kind, u.object_key, u.object_version_id, u.encryption_key_id" + " FROM ma_session_record r LEFT JOIN ma_resource u ON u.session_scope_key=r.session_scope_key" @@ -94,7 +97,7 @@ public final class ManagedMcpSettings { && row.getString("object_key") == null && row.getString("object_version_id") == null && row.getString("encryption_key_id") == null, "The committed MCP settings are corrupt"); - JsonNode body = ManagedExtensionRecordStore.parse(new String(bytes, StandardCharsets.UTF_8)); + JsonNode body = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(new String(bytes, StandardCharsets.UTF_8)); require(body); check(body.get("revision").longValue() == row.getLong("revision"), "MCP settings projection conflicts"); return body; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeJournalSnapshots.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeJournalSnapshots.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeJournalSnapshots.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeJournalSnapshots.java index ede673ad3e7..cc882b357f6 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeJournalSnapshots.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeJournalSnapshots.java @@ -1,13 +1,16 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedHostedRecoveryRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RestoreHead; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredTransaction; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.ToolResultResourceRef; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.RestoreHead; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredTransaction; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.ToolResultResourceRef; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.validation.constraints.Max; @@ -17,7 +20,6 @@ import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Repository; @@ -97,7 +99,7 @@ public class ManagedNativeJournalSnapshots { private Descriptor build(String tenant, String session, String token, PublishRequest request, Basis basis) { RestoreHead head = basis.head(); - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String owner = ownerKey(scope, request.snapshotId()); claim(tenant, request.workspaceId(), session, request.snapshotId()); Descriptor existing = find(tenant, request.workspaceId(), session, token, request.snapshotId()); @@ -107,7 +109,7 @@ public class ManagedNativeJournalSnapshots { return new Descriptor(existing.snapshot(), existing.manifest(), true); } byte[] claimed = jdbc.queryForObject("SELECT inline_bytes FROM ma_resource WHERE resource_key = ?", byte[].class, - ManagedSessionStore.resourceKey(scope, manifestId(scope, request.snapshotId()))); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, manifestId(scope, request.snapshotId()))); require(java.util.Arrays.equals(claimed, "{}".getBytes(StandardCharsets.UTF_8)), "managed_session_snapshot_corrupt"); require(head.journalRevision() == request.expectedJournalRevision() && head.committedSequence() == request.expectedCommittedSequence() @@ -172,7 +174,7 @@ public class ManagedNativeJournalSnapshots { StoredResource stored = new StoredResource(id, MANIFEST_KIND, 1, manifestBytes.length, DigestUtils.sha256Hex(manifestBytes), manifestBytes); jdbc.update("UPDATE ma_resource SET inline_bytes = ?, byte_length = ?, sha256 = ?, kind = ? WHERE resource_key = ?", - manifestBytes, manifestBytes.length, stored.digest(), MANIFEST_KIND, ManagedSessionStore.resourceKey(scope, id)); + manifestBytes, manifestBytes.length, stored.digest(), MANIFEST_KIND, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, id)); basis.lease().checkReadView(); RestoreHead current = sessions.lockSnapshotWriter(tenant, session, token, request); require(current.journalRevision() == request.expectedJournalRevision() @@ -211,7 +213,7 @@ public class ManagedNativeJournalSnapshots { + " WHERE session_scope_key = ? AND owner_kind = 'NATIVE_SNAPSHOT' AND owner_revision <= ?" + " GROUP BY owner_key, owner_revision ORDER BY owner_revision DESC, owner_key LIMIT 1", (row, index) -> new Candidate(row.getString(1), row.getLong(2)), - ManagedSessionStore.sessionScopeKey(tenant, session), frozen.journalRevision()); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), frozen.journalRevision()); if (candidates.isEmpty()) { return null; } @@ -219,7 +221,7 @@ public class ManagedNativeJournalSnapshots { var roots = jdbc.queryForList("SELECT r.resource_id FROM ma_resource_ref f" + " JOIN ma_resource r ON r.resource_key = f.resource_key WHERE f.session_scope_key = ?" + " AND f.owner_kind = 'NATIVE_SNAPSHOT' AND f.owner_key = ? AND f.owner_revision = ? AND r.kind = ?", - String.class, ManagedSessionStore.sessionScopeKey(tenant, session), selected.ownerKey(), + String.class, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), selected.ownerKey(), selected.coverage(), MANIFEST_KIND); require(roots.size() == 1, "managed_session_snapshot_corrupt"); String root = roots.getFirst(); @@ -227,7 +229,7 @@ public class ManagedNativeJournalSnapshots { require(manifest.snapshotId() != null, "managed_session_snapshot_corrupt"); Descriptor descriptor = read(tenant, workspace, session, token, manifest.snapshotId()); require(root.equals(descriptor.manifest().resourceId()) - && selected.ownerKey().equals(ownerKey(ManagedSessionStore.sessionScopeKey(tenant, session), manifest.snapshotId())) + && selected.ownerKey().equals(ownerKey(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), manifest.snapshotId())) && selected.coverage() == descriptor.snapshot().coveredJournalRevision() && descriptor.snapshot().committedSequence() <= frozen.committedSequence() && descriptor.snapshot().activationEpoch() <= frozen.activationEpoch() @@ -247,7 +249,7 @@ public class ManagedNativeJournalSnapshots { sessions.restore(tenant, workspace, session, token); Descriptor descriptor = find(tenant, workspace, session, token, snapshotId); if (descriptor == null) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "managed_session_snapshot_not_found", + throw new ManagedAgentException(404, "managed_session_snapshot_not_found", "The native journal snapshot does not exist."); } sessions.restore(tenant, workspace, session, token); @@ -260,7 +262,7 @@ public class ManagedNativeJournalSnapshots { Manifest manifest = descriptor.snapshot(); require(afterRevision >= 0 && afterRevision <= manifest.coveredJournalRevision() && limit >= 1 && limit <= 100, "invalid_managed_session_store_request"); - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String owner = ownerKey(scope, snapshotId); List result = new ArrayList<>(); int bytes = 0; @@ -293,7 +295,7 @@ public class ManagedNativeJournalSnapshots { private Descriptor find(String tenant, String workspace, String session, String token, String id) { require(id != null && !id.isBlank() && id.getBytes(StandardCharsets.UTF_8).length <= 512, "invalid_managed_session_store_request"); - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String owner = ownerKey(scope, id); List roots = jdbc.queryForList("SELECT f.resource_id FROM ma_resource_ref f JOIN ma_resource r" + " ON r.resource_key = f.resource_key WHERE f.session_scope_key = ? AND f.owner_kind = ?" @@ -318,7 +320,7 @@ public class ManagedNativeJournalSnapshots { } private List census(String tenant, String workspace, String session, String token, long revision) { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); List ids = jdbc.queryForList("SELECT resource_id FROM ma_resource_ref WHERE session_scope_key = ?" + " AND owner_kind = 'JOURNAL' AND owner_key = ? AND owner_revision = ?" + " AND journal_revision = ? ORDER BY resource_id", String.class, scope, scope, revision, revision); @@ -424,7 +426,7 @@ public class ManagedNativeJournalSnapshots { while (!pending.isEmpty()) { JsonNode next = pending.removeFirst(); String id = next.path("resourceId").asText(); - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); Long pinned = building ? jdbc.queryForObject("SELECT COUNT(*) FROM ma_resource_ref WHERE session_scope_key = ?" + " AND owner_kind = 'JOURNAL' AND owner_key = ? AND owner_revision = journal_revision" @@ -475,8 +477,8 @@ public class ManagedNativeJournalSnapshots { private StoredResource put(String tenant, String workspace, String session, String kind, byte[] bytes) { String digest = DigestUtils.sha256Hex(bytes); String id = kind + ":" + digest; - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); - String key = ManagedSessionStore.resourceKey(scope, id); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); + String key = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, id); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id," + " resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, state, gmt_create)" + " VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?, 'MYSQL_INLINE', ?, 'REFERENCED', " @@ -493,18 +495,18 @@ public class ManagedNativeJournalSnapshots { } private void claim(String tenant, String workspace, String session, String snapshotId) { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String id = manifestId(scope, snapshotId); byte[] pending = "{}".getBytes(StandardCharsets.UTF_8); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id," + " resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, state, gmt_create)" + " VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?, 'MYSQL_INLINE', ?, 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ") ON DUPLICATE KEY UPDATE resource_key = resource_key", - ManagedSessionStore.resourceKey(scope, id), scope, tenant, workspace, session, id, MANIFEST_KIND, + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, id), scope, tenant, workspace, session, id, MANIFEST_KIND, pending.length, DigestUtils.sha256Hex(pending), pending); var rows = jdbc.queryForList("SELECT tenant_id, workspace_id, session_id, resource_id, kind, schema_version," + " storage_kind, state, object_key, object_version_id, encryption_key_id FROM ma_resource WHERE resource_key = ?", - ManagedSessionStore.resourceKey(scope, id)); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, id)); require(rows.size() == 1 && tenant.equals(rows.getFirst().get("tenant_id")) && workspace.equals(rows.getFirst().get("workspace_id")) && session.equals(rows.getFirst().get("session_id")) && id.equals(rows.getFirst().get("resource_id")) && MANIFEST_KIND.equals(rows.getFirst().get("kind")) @@ -524,7 +526,7 @@ public class ManagedNativeJournalSnapshots { private void pin(String scope, String owner, long coverage, StoredResource resource, Long revision, String tenant, String workspace, String session) { - String key = ManagedSessionStore.resourceKey(scope, resource.resourceId()); + String key = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, resource.resourceId()); Long count = jdbc.queryForObject("SELECT COUNT(*) FROM ma_resource_ref WHERE session_scope_key = ?" + " AND owner_kind = ? AND owner_key = ? AND owner_revision = ? AND resource_key = ?", Long.class, scope, OWNER, owner, coverage, key); @@ -586,12 +588,12 @@ public class ManagedNativeJournalSnapshots { private static void require(boolean condition, String code) { if (!condition) { throw new ManagedAgentException("invalid_managed_session_store_request".equals(code) - ? HttpStatus.BAD_REQUEST : HttpStatus.CONFLICT, code, "The native journal snapshot was refused."); + ? 400 : 409, code, "The native journal snapshot was refused."); } } private static ManagedAgentException corrupt() { - return new ManagedAgentException(HttpStatus.CONFLICT, "managed_session_snapshot_corrupt", + return new ManagedAgentException(409, "managed_session_snapshot_corrupt", "The native journal snapshot is corrupt."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedProjectionResourceCollector.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedProjectionResourceCollector.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedProjectionResourceCollector.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedProjectionResourceCollector.java index 9e0dcbd7c1a..d4ff8683a4e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedProjectionResourceCollector.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedProjectionResourceCollector.java @@ -54,7 +54,7 @@ final class ManagedProjectionResourceCollector { return false; } ToolPublicationRetentionStore.lockSessionHead(jdbc, tenant, session); - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); var rows = jdbc.queryForList( "SELECT resource_key FROM ma_resource WHERE resource_key = ? AND" diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceipts.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceipts.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceipts.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceipts.java index 2c949364a6b..2dd93906ca6 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceipts.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceipts.java @@ -1,9 +1,9 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.springframework.jdbc.core.JdbcTemplate; import java.util.Base64; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuard.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuard.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuard.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuard.java index d7a9832a30a..0c56aebf54e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuard.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuard.java @@ -1,11 +1,13 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.HarnessOwnership; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiFilesRetirementGuard; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeRetention; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeRetention; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import java.nio.charset.StandardCharsets; import java.sql.Connection; @@ -19,7 +21,7 @@ public final class ManagedRuntimeRetentionGuard implements JdbcRuntimeRetention. throws SQLException { var request = binding.getRequest(); // A shared binding has no isolation key; private-CSI Sessions never place on one. - return JdbcCsiFilesRetirementGuard.isProfile(request.getScope()) + return CsiFilesRetirementProfile.isProfile(request.getScope()) || request.getIsolationKey() != null && exists( connection, @@ -176,7 +178,7 @@ public final class ManagedRuntimeRetentionGuard implements JdbcRuntimeRetention. + " BINARY(2048)) = CAST('child_run' AS BINARY(2048)) AND" + " CAST(record_id AS BINARY(2048)) = CAST(? AS BINARY(2048)) AND" + " settled_at IS NOT NULL LIMIT 1", - ManagedSessionStore.sessionScopeKey(tenantId, parent), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, parent), ManagedExtensionProjection.recordKey(parent, "child_run", run), tenantId, parent, diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaJdbcTemplate.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaJdbcTemplate.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaJdbcTemplate.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaJdbcTemplate.java index 1efe5251736..0c4374c59ba 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaJdbcTemplate.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaJdbcTemplate.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.springframework.jdbc.core.ArgumentPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementSetter; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionRetirementStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionRetirementStore.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionRetirementStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionRetirementStore.java index 9550b11f183..74cde44054b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionRetirementStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionRetirementStore.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -27,7 +27,7 @@ final class ManagedSessionRetirementStore { if (roots.size() != 1 || roots.getFirst().get("retired_at") == null) { throw new IllegalStateException("Session has not been permanently retired"); } - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); var nativeSnapshots = jdbc.queryForList("SELECT owner_key, owner_revision, resource_key FROM ma_resource_ref" + " WHERE session_scope_key = ? AND owner_kind = 'NATIVE_SNAPSHOT'" + " ORDER BY owner_key, owner_revision, resource_key LIMIT ? FOR UPDATE", scope, limit); @@ -258,7 +258,7 @@ final class ManagedSessionRetirementStore { throw new IllegalStateException("Session journal collection is incomplete"); } if (!jdbc.queryForList("SELECT resource_key FROM ma_resource_ref WHERE session_scope_key = ?" - + " AND owner_kind = 'NATIVE_SNAPSHOT' LIMIT 1", ManagedSessionStore.sessionScopeKey(tenant, session)).isEmpty()) { + + " AND owner_kind = 'NATIVE_SNAPSHOT' LIMIT 1", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session)).isEmpty()) { throw new IllegalStateException("Native snapshot collection is incomplete"); } var operations = @@ -270,7 +270,7 @@ final class ManagedSessionRetirementStore { if (operations.size() != 1 || !tenant.equals(operations.getFirst().get("tenant_id")) || !session.equals(operations.getFirst().get("session_id")) - || !ManagedSessionStore.sessionScopeKey(tenant, session) + || !com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session) .equals(operations.getFirst().get("session_scope_key")) || !"SUCCEEDED".equals(operations.getFirst().get("state"))) { throw new IllegalStateException("Session collection is not settled"); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionSkills.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionSkills.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionSkills.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionSkills.java index 4777d439624..09a2423c3ed 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionSkills.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionSkills.java @@ -1,14 +1,14 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.catalog.AgentDefinitionExtensions; +import com.alibaba.qwen.code.managedagent.contracts.catalog.AgentDefinitionExtensions; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedConfigInstall; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @@ -17,7 +17,8 @@ import java.util.List; import java.util.Map; @Repository -public class ManagedSessionSkills { +public class ManagedSessionSkills + implements com.alibaba.qwen.code.managedagent.repository.ManagedSessionSkills { private final JdbcTemplate jdbc; private final ObjectMapper json; private final RequestDigests digests; @@ -125,7 +126,7 @@ public class ManagedSessionSkills { } public void saveInitialSelection(String tenant, String session, - List pins) { + List pins) { String previous = creationJson(tenant, session); List> skills = decode(previous, json, digests); if (initialSelection(tenant, session) != null) { @@ -194,7 +195,7 @@ public class ManagedSessionSkills { } private static ManagedAgentException conflict() { - return new ManagedAgentException(HttpStatus.CONFLICT, "session_skills_conflict", + return new ManagedAgentException(409, "session_skills_conflict", "The immutable Session Skills snapshot is invalid."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStore.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStore.java index e50be173a0d..e34da3a7747 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStore.java @@ -1,36 +1,44 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.catalog.AgentDefinitionExtensions; -import com.alibaba.qwen.code.managedagent.event.JournalEventTransport; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.catalog.AgentDefinitionExtensions; +import com.alibaba.qwen.code.managedagent.contracts.event.JournalEventTransport; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedConfigInstall; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedHarnessReleaseRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedHostedRecoveryRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.BlockRecoveryRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitReceipt; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.PublishToolResultRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RecoveryStateReceipt; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RenewWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RestoreHead; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealReceipt; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredTransaction; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.ToolResultResourceRef; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.TransactionPage; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.WriterGrant; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.RequestDigests; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.BlockRecoveryRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitReceipt; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.PublishToolResultRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.RecoveryStateReceipt; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.RenewWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.RestoreHead; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.SealReceipt; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.SealWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredTransaction; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.ToolResultResourceRef; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.TransactionPage; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.WriterGrant; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiActivationAdmission; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiFilesRetirementGuard; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.ManagedCheckpointHistory; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiActivationAdmission; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiFilesRetirementGuard; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -42,7 +50,6 @@ import org.springframework.dao.DeadlockLoserDataAccessException; import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.PessimisticLockingFailureException; import org.springframework.dao.QueryTimeoutException; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; @@ -73,7 +80,19 @@ import java.util.regex.Pattern; @Repository @Qualifier("managedAgentTransactionManager") -public class ManagedSessionStore { +public class ManagedSessionStore + implements com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore { + private com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRules harnessRules; + @org.springframework.beans.factory.annotation.Autowired + public void configureHarnessRules(com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRules value) { harnessRules = value; } + + private com.alibaba.qwen.code.managedagent.contracts.persistence.StorageReadPolicy readerVersion = + com.alibaba.qwen.code.managedagent.contracts.persistence.StorageReadPolicy.inProcess(); + @org.springframework.beans.factory.annotation.Autowired + public void configureReaderVersion(com.alibaba.qwen.code.managedagent.contracts.persistence.StorageReadPolicy value) { + readerVersion = value; + } + private static final int STORAGE_VERSION = 1; private static final ObjectMapper CSI_JSON = new ObjectMapper(); private static final RequestDigests INITIAL_DIGESTS = new RequestDigests(); @@ -217,7 +236,7 @@ public class ManagedSessionStore { sessionId, authority.operationId()); if (!kind.toLowerCase(java.util.Locale.ROOT).equals(request.kind())) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_operation_conflict"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_operation_conflict"); } } else if (!"DRAINING" .equals( @@ -230,7 +249,7 @@ public class ManagedSessionStore { JdbcRuntimeBindingRepository.harnessDrainKey(sessionId), tenantId, sessionId))) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_hooks_unsettled"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_hooks_unsettled"); } HeadRow head = requireHeadForUpdate(tenantId, sessionId); requireHeadScope(head, tenantId, request.workspaceId(), sessionId); @@ -269,7 +288,7 @@ public class ManagedSessionStore { } private static ManagedAgentException guardContended() { - return new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, + return new ManagedAgentException(503, "tenant_guard_contended", "A tenant-wide Managed Session guard could not be acquired" + " within the hot-path bound; retry."); @@ -390,12 +409,12 @@ public class ManagedSessionStore { if (request.kind() != null && request.kind().startsWith("configuration:")) { String operation = request.kind().substring("configuration:".length()); JsonNode current = ManagedMcpSettings.find(jdbc, tenantId, sessionId); - if (!ManagedConfigInstall.uuid(operation) || ManagedConfigInstall.blocked(jdbc, tenantId, sessionId) + if (!ManagedConfigInstall.uuid(operation) || ManagedConfigStore.blocked(jdbc, tenantId, sessionId) && (!ManagedConfigInstall.isPackageConfig(current) || !operation.equals(current.path("operationId").asText()))) { throw conflict("session_config_recovery_required", "Only the original configuration operation may continue."); } } else { - ManagedConfigInstall.requireOpen(jdbc, tenantId, sessionId); + ManagedConfigStore.requireOpen(jdbc, tenantId, sessionId); } HeadRow head = requireHeadForUpdate(tenantId, sessionId); requireHeadScope(head, tenantId, request.workspaceId(), sessionId); @@ -420,9 +439,9 @@ public class ManagedSessionStore { if (row.get("workspace_id") == null || ("qwen-code".equals(row.get("agent_id")) ? row.get("agent_definition_digest") != null : row.get("agent_definition_digest") == null) - || !(com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles.FILES.equals(profile) - || com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles.FILES_V3.equals(profile) - || com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles.isPublicShell(profile))) { + || !(com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles.FILES.equals(profile) + || com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles.FILES_V3.equals(profile) + || com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles.isPublicShell(profile))) { return null; } String mode = (String) row.get("selected_approval_mode"); @@ -430,14 +449,14 @@ public class ManagedSessionStore { mode = (String) row.get("approval_mode"); } long revision = ((Number) row.get("approval_revision")).longValue(); - if (!com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles.supportsApprovalMode(mode) + if (!com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles.supportsApprovalMode(mode) || revision < 0 || revision > 9_007_199_254_740_991L) { throw conflict("approval_selection_conflict", "The approval selection is invalid."); } java.util.Map result = new java.util.LinkedHashMap<>(java.util.Map.of("mode", mode, "revision", revision)); if (row.get("permission_rules_json") != null) { try { - var rules = com.alibaba.qwen.code.daemon.HostedPermissionRules.nonEmpty(com.alibaba.qwen.code.daemon.HostedPermissionRules.copyOf( + var rules = harnessRules.nonEmptyPermissionRules(harnessRules.permissionRules( CSI_JSON.readValue((String) row.get("permission_rules_json"), java.util.Map.class))); if (rules != null) { result.put("permissionRules", rules); @@ -463,8 +482,8 @@ public class ManagedSessionStore { private void requireNoLifecycleClaim(String tenantId, String sessionId) { if (!jdbc.queryForList("SELECT operation_id FROM ma_operation WHERE scope_key = ?" + " AND operation_kind IN ('CLOSE', 'ARCHIVE', 'DELETE') AND tenant_id = ? AND session_id = ?" - + " AND lifecycle_protocol_version = 2", sessionScopeKey(tenantId, sessionId), tenantId, sessionId).isEmpty()) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_claim_fenced"); + + " AND lifecycle_protocol_version = 2", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId).isEmpty()) { + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_claim_fenced"); } } @@ -666,7 +685,7 @@ public class ManagedSessionStore { throw conflict("managed_session_not_writable", "The Session is closing or closed."); } String tokenHash = tokenHash(writerToken); - ManagedSessionReaderVersion.requireReadable(1); + readerVersion.requireReadable(1); Timestamp createdAt = databaseNow(); Timestamp initialLeaseUntil = plusMillis(createdAt, request.leaseMillis()); @@ -697,9 +716,9 @@ public class ManagedSessionStore { try { jdbc.update( "INSERT INTO ma_session_head (tenant_key, harness_key, harness_session_id, head_kind, tenant_id, workspace_id, session_id, storage_version, state, writer_generation, writer_id, writer_lease_until, lease_token_hash, journal_revision, wake_head_revision, cleanup_head_revision, committed_sequence, activation_epoch, compacted_through_revision, recovery_status, gmt_create, gmt_modified) VALUES (?, ?, ?, 'MANAGED_SESSION', ?, ?, ?, ?, 'ACTIVE', 1, ?, ?, ?, 0, 0, 0, 0, 0, 0, 'READY', ?, ?)", - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository .harnessDrainKey(tenantId), - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository .harnessDrainKey(sessionId), sessionId, tenantId, @@ -863,7 +882,7 @@ public class ManagedSessionStore { activation.activationId(), activation.epoch(), activation.renewalSeq(), until); return new WriterGrant(head.writerGeneration(), epochMillis(leaseUntil), head.journalRevision(), head.committedSequence(), head.lastCommitDigest(), head.activationEpoch(), false, - ManagedSessionReaderVersion.SUPPORTED, true, lease); + com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedStorageVersions.SUPPORTED, true, lease); } return grant(head, leaseUntil, false); } @@ -949,7 +968,7 @@ public class ManagedSessionStore { lockBounded(() -> ToolPublicationRetentionStore.lockTenant(jdbc, tenantId, GUARD_ACQUIRE_TIMEOUT_SECONDS)); ToolPublicationRetentionStore.lockSessionRoot(jdbc, tenantId, sessionId); String recordId = HarnessPlacementStore.releaseId(request.placementEpoch()); - String scope = sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); String recordKey = ManagedExtensionProjection.recordKey(sessionId, HarnessPlacementStore.RELEASE_DOMAIN, recordId); ObjectNode receipt = CSI_JSON.createObjectNode(); receipt.put("format", "qwen-hosted-harness-release/1"); @@ -966,7 +985,7 @@ public class ManagedSessionStore { receipt.set("commitProof", CSI_JSON.valueToTree(request.commitProof())); receipt.put("state", "RELEASED"); // Normalize integer nodes to the representation read back from the stored JSON. - receipt = (ObjectNode) ManagedExtensionRecordStore.parse(receipt.toString()); + receipt = (ObjectNode) com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(receipt.toString()); var saved = jdbc.query("SELECT record_resource_id FROM ma_session_record WHERE session_scope_key = ?" + " AND record_key = ? AND tenant_id = ? AND session_id = ? AND domain = ? AND record_kind = 'DOMAIN'", (row, index) -> row.getString(1), scope, recordKey, tenantId, sessionId, HarnessPlacementStore.RELEASE_DOMAIN); @@ -976,7 +995,7 @@ public class ManagedSessionStore { throw HarnessPlacementStore.blocked("harness_release_receipt_missing"); } verifyStoredResource(resource); - JsonNode original = ManagedExtensionRecordStore.parse(new String(resource.bytes(), StandardCharsets.UTF_8)); + JsonNode original = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(new String(resource.bytes(), StandardCharsets.UTF_8)); if (!receipt.equals(original)) { throw HarnessPlacementStore.blocked("harness_release_operation_conflict"); } @@ -1006,7 +1025,7 @@ public class ManagedSessionStore { throw new IllegalStateException("Ownership release receipt cannot be encoded.", failure); } ManagedHarnessReleaseRecords.require(receipt); - String resourceKey = resourceKey(scope, resourceId); + String resourceKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, resourceId); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id," + " resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, state, gmt_create, last_verified_at)" + " VALUES (?, ?, ?, ?, ?, ?, 'managed-hosted_harness_release', 1, ?, ?, 'MYSQL_INLINE', ?, 'REFERENCED', ?, ?)", @@ -1118,11 +1137,11 @@ public class ManagedSessionStore { if (!"managed-action-options".equals(resource.resource().kind()) || resource.bytes() == null) { return false; } - JsonNode body = ManagedExtensionRecordStore.parse(new String(resource.bytes(), StandardCharsets.UTF_8)); + JsonNode body = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(new String(resource.bytes(), StandardCharsets.UTF_8)); return body != null && body.path("v").asInt() == 3; }); if (promotes) { - ManagedSessionReaderVersion.requireReadable(2); + readerVersion.requireReadable(2); } var continuation = lockCsiContinuation(tenantId, sessionId); var csiOriginal = continuation == null ? null : continuation.original(); @@ -1191,7 +1210,7 @@ public class ManagedSessionStore { validateHead(head, request); long revision = increment(head.journalRevision(), "journal revision"); - String scopeKey = sessionScopeKey(tenantId, sessionId); + String scopeKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); if (csiOriginal != null && java.util.Set.of("commitFileHistory", "toolIntent", "commitCheckpoint") .contains(request.operation())) { @@ -1260,7 +1279,7 @@ public class ManagedSessionStore { boolean wakeInput = false; for (String line : new String(validated.recordBytes(), StandardCharsets.UTF_8).split("\n")) { - JsonNode record = ManagedExtensionRecordStore.parse(line); + JsonNode record = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(line); if (record != null && record.has("managedSession")) { JsonNode event = record.get("managedSession"); wakeInput |= WakeRecoveryStore.isWakeInput(event); @@ -1288,17 +1307,17 @@ public class ManagedSessionStore { if (authority != null && extensionRecords.hasNewLifecycleDispatch(tenantId, sessionId, validated.recordBytes(), reader, v2, resourcePhase)) { if (lifecycleExecution == null || lifecycleSessions == null) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_protocol_unavailable"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_protocol_unavailable"); } try { lifecycleExecution.authorizeLifecycle(lifecycleSessions.requireSession(tenantId, sessionId), authority); } catch (com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException refusal) { if (refusal.getStatusCode() == 409 && "workspace_unavailable".equals(refusal.getCode())) { - throw WorkspaceLifecycleStore.blocked( + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked( "workspace_lifecycle_authorization_revoked"); } if (refusal.getStatusCode() == 409) { - throw new ManagedAgentException(HttpStatus.CONFLICT, refusal.getCode(), refusal.getMessage()); + throw new ManagedAgentException(409, refusal.getCode(), refusal.getMessage()); } throw refusal; } @@ -1875,10 +1894,10 @@ public class ManagedSessionStore { private void requireInitialResource(JsonNode ref, HeadRow head, String kind) throws IOException { WorkspaceRecoveryReader.validateRef(ref); - String scope = sessionScopeKey(head.tenantId(), head.sessionId()); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(head.tenantId(), head.sessionId()); var resources = jdbc.query("SELECT " + ManagedSchemaProjection.RESOURCE.columns() + " FROM ma_resource WHERE resource_key = ? AND session_scope_key = ? AND resource_id = ?", - resourceMapper, resourceKey(scope, ref.path("resourceId").asText()), scope, ref.path("resourceId").asText()); + resourceMapper, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, ref.path("resourceId").asText()), scope, ref.path("resourceId").asText()); if (resources.size() != 1) { throw journalCorrupt(); } @@ -1920,10 +1939,10 @@ public class ManagedSessionStore { requireHeadScope(head, tenantId, workspaceId, sessionId); requireReadGrant(head, writerToken); try (var lease = outputRetention.read(tenantId, sessionId)) { - String scopeKey = sessionScopeKey(tenantId, sessionId); + String scopeKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); ResourceRow resource = findResource(scopeKey, resourceId, updateVerification); if (resource == null) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, + throw new ManagedAgentException(404, ManagedSessionStoreModels.ERROR_RESOURCE_NOT_FOUND, "The Managed Session resource does not exist."); } @@ -2001,7 +2020,7 @@ public class ManagedSessionStore { nativeResourceMetadata(tenantId, sessionId, resourceId); if (resource == null) { throw new ManagedAgentException( - HttpStatus.NOT_FOUND, + 404, ManagedSessionStoreModels.ERROR_RESOURCE_NOT_FOUND, "The Managed Session resource does not exist."); } @@ -2051,20 +2070,16 @@ public class ManagedSessionStore { .put("delivery_expires_at", expires); String encoded = evidence.toString(); String receipt = - com.alibaba.qwen.code.runtimebroker.managedschema - .ManagedSchemaKeys.key("csi-read", id); + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("csi-read", id); jdbc.update( "INSERT INTO ma_request_receipt (receipt_key, namespace, tenant_key, tenant_id, scope_kind, scope_key, scope_id, idempotency_key, idempotency_digest, request_digest, operation, state, session_id, result_json, gmt_create, gmt_modified) VALUES (?, 'SESSION_COMMAND', ?, ?, 'SESSION', ?, ?, ?, ?, ?, 'CSI_RESOURCE_READ', 'OPEN', ?, ?, ?, ?)", receipt, - com.alibaba.qwen.code.runtimebroker.managedschema - .ManagedSchemaKeys.key(tenantId), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(tenantId), tenantId, - com.alibaba.qwen.code.runtimebroker.managedschema - .ManagedSchemaKeys.key("csi-read", tenantId, sessionId), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("csi-read", tenantId, sessionId), sessionId, id, - com.alibaba.qwen.code.runtimebroker.managedschema - .ManagedSchemaKeys.key(id), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(id), sha256(encoded.getBytes(StandardCharsets.UTF_8)), sessionId, encoded, @@ -2110,7 +2125,7 @@ public class ManagedSessionStore { return jdbc.queryForMap( "SELECT " + ManagedSchemaProjection.REQUEST_RECEIPT.columns() + " FROM ma_request_receipt WHERE receipt_key = ? AND namespace = 'SESSION_COMMAND' AND operation = 'CSI_RESOURCE_READ'" + (lock ? " FOR UPDATE" : ""), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( "csi-read", id)); } @@ -2130,7 +2145,7 @@ public class ManagedSessionStore { state, evidence.toString(), ToolPublicationRetentionStore.now(jdbc), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( "csi-read", admission.id())) != 1) { throw nativeReadConflict(); @@ -2147,10 +2162,10 @@ public class ManagedSessionStore { + " session_scope_key = ? AND resource_id = ? AND resource_key = ? FOR" + " UPDATE", resourceMapper, - sessionScopeKey(tenant, session), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), resource, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( - sessionScopeKey(tenant, session), resource)); + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), resource)); return rows.isEmpty() ? null : rows.getFirst(); } @@ -2161,15 +2176,15 @@ public class ManagedSessionStore { "SELECT " + ManagedSchemaProjection.RESOURCE.columns() + " FROM ma_resource WHERE session_scope_key = ? AND resource_id = ? AND resource_key = ?")) { statement.setQueryTimeout(10); statement.setString( - 1, sessionScopeKey(admission.head().tenantId(), admission.head().sessionId())); + 1, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(admission.head().tenantId(), admission.head().sessionId())); statement.setBytes( 2, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.identity( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.identity( admission.resource().resourceId())); statement.setString( 3, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( - sessionScopeKey( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey( admission.head().tenantId(), admission.head().sessionId()), admission.resource().resourceId())); try (var rows = statement.executeQuery()) { @@ -2258,7 +2273,7 @@ public class ManagedSessionStore { "UPDATE ma_resource SET last_verified_at = ?" + " WHERE session_scope_key = ? AND resource_id = ?", databaseNow(), - sessionScopeKey(head.tenantId(), head.sessionId()), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(head.tenantId(), head.sessionId()), fetched.resourceId()); } catch (ManagedAgentException rejected) { failure = rejected; @@ -2354,7 +2369,7 @@ public class ManagedSessionStore { private ToolResultResourceRef publishAgentResource(String tenantId, String workspaceId, String sessionId, String kind, byte[] bytes, String resourceId) { String digest = sha256(bytes); - String scopeKey = sessionScopeKey(tenantId, sessionId); + String scopeKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); Timestamp now = databaseNow(); ResourceRow existing = findResource(scopeKey, resourceId); if (existing == null) { @@ -2364,7 +2379,7 @@ public class ManagedSessionStore { + " byte_length, sha256, storage_kind, inline_bytes, publish_command_id," + " state, gmt_create, last_verified_at) VALUES (?, ?, ?, ?, ?, ?," + " ?, 1, ?, ?, 'MYSQL_INLINE', ?, ?, 'PUBLISHED', ?, ?)", - resourceKey(scopeKey, resourceId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scopeKey, resourceId), scopeKey, tenantId, workspaceId, @@ -2449,7 +2464,7 @@ public class ManagedSessionStore { Timestamp now = databaseNow(); requireWriter(head, request.writerId(), request.writerGeneration(), writerToken, now, true); requireInstalledCsiWriter(csiOriginal); - String scopeKey = sessionScopeKey(tenantId, sessionId); + String scopeKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); ResourceRow existing = findResource(scopeKey, request.resourceId()); requireCsiWriterCurrent( continuation, head, request.writerId(), request.writerGeneration(), writerToken); @@ -2460,7 +2475,7 @@ public class ManagedSessionStore { + " byte_length, sha256, storage_kind, inline_bytes, publish_command_id," + " state, gmt_create, last_verified_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?," + " ?, 'MYSQL_INLINE', ?, ?, 'PUBLISHED', ?, ?)", - resourceKey(scopeKey, request.resourceId()), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scopeKey, request.resourceId()), scopeKey, tenantId, request.workspaceId(), @@ -2564,7 +2579,7 @@ public class ManagedSessionStore { } void requireCsiApplicationCredential(java.sql.Connection target, - com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement.Authority authority, + com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement.Authority authority, String writerToken) throws SQLException { authority.requireConnection(target); if (!org.springframework.jdbc.datasource.DataSourceUtils.isConnectionTransactional( @@ -2592,7 +2607,7 @@ public class ManagedSessionStore { } TerminalCandidate preflightCsiTerminal(java.sql.Connection target, - com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement.Authority authority, + com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement.Authority authority, String writerToken, byte[] exactBody, JdbcRuntimeBindingRepository bindings) throws SQLException { authority.requirePreCut(target); if (!org.springframework.jdbc.datasource.DataSourceUtils.isConnectionTransactional( @@ -2619,10 +2634,10 @@ public class ManagedSessionStore { } String commitCsiTerminal(java.sql.Connection target, - com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement.Authority authority, + com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement.Authority authority, String writerToken, byte[] exactBody, JdbcRuntimeBindingRepository bindings) throws SQLException { authority.requireConnection(target); - if (authority.stage() != com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement.Stage.FROZEN) { + if (authority.stage() != com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement.Stage.FROZEN) { throw nativeReadConflict(); } var frozen = WorkspaceCsiApplicationJson.object(authority.frozenBytes(), WorkspaceCsiApplicationInventory.MAX_BYTES); @@ -2649,7 +2664,7 @@ public class ManagedSessionStore { } requireWriter(head, request.writerId(), request.writerGeneration(), writerToken, databaseNow(), true); }; - commitResources(sessionScopeKey(tenant, session), tenant, session, request, revision, now, validated.resources(), fence); + commitResources(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), tenant, session, request, revision, now, validated.resources(), fence); fence.run(); persistTransaction(tenant, session, candidate.writerTokenSha256(), request, revision, validated, now); fence.run(); @@ -2685,7 +2700,7 @@ public class ManagedSessionStore { private void validatePagedCheckpoint(StoredResource stored, String tenant, String workspace, String session, ManagedHostedRecoveryRecords.HistoricalResources resources) { - JsonNode body = ManagedExtensionRecordStore.parse(new String(stored.bytes(), StandardCharsets.UTF_8)); + JsonNode body = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(new String(stored.bytes(), StandardCharsets.UTF_8)); if (body == null || !ManagedCheckpointHistory.paged(body)) { return; } @@ -2779,14 +2794,14 @@ public class ManagedSessionStore { private Long priorCheckpointResourceRevision(String tenant, String workspace, String session, String resourceId, long maximum) { - String scope = sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); List origins = jdbc.query("SELECT x.owner_revision FROM ma_resource_ref x JOIN ma_session_journal_tx t" + " ON t.tenant_id = x.tenant_id AND t.workspace_id = x.workspace_id AND t.session_id = x.session_id" + " AND t.journal_revision = x.owner_revision WHERE x.resource_key = ? AND x.session_scope_key = ?" + " AND x.tenant_id = ? AND x.workspace_id = ? AND x.session_id = ? AND x.resource_id = ?" + " AND x.owner_kind = 'JOURNAL' AND x.owner_key = ? AND x.owner_revision = x.journal_revision" + " AND x.owner_revision <= ? ORDER BY x.owner_revision LIMIT 1", - (row, index) -> row.getLong(1), resourceKey(scope, resourceId), scope, tenant, workspace, session, + (row, index) -> row.getLong(1), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, resourceId), scope, tenant, workspace, session, resourceId, scope, maximum); return origins.isEmpty() ? null : origins.getFirst(); } @@ -2821,7 +2836,7 @@ public class ManagedSessionStore { + " last_verified_at) VALUES (?, ?, ?, ?, ?, ?," + " ?, ?, ?, ?, 'MYSQL_INLINE', ?, ?," + " 'REFERENCED', ?, ?)", - resourceKey(scopeKey, resource.resourceId()), scopeKey, tenantId, request.workspaceId(), sessionId, + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scopeKey, resource.resourceId()), scopeKey, tenantId, request.workspaceId(), sessionId, resource.resourceId(), resource.kind(), resource.schemaVersion(), resource.byteLength(), resource.digest(), input.bytes(), request.commandId(), @@ -2849,7 +2864,7 @@ public class ManagedSessionStore { scopeKey, scopeKey, revision, - resourceKey(scopeKey, resource.resourceId()), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scopeKey, resource.resourceId()), tenantId, request.workspaceId(), sessionId, @@ -2948,7 +2963,7 @@ public class ManagedSessionStore { if (resource.byteLength() > maxResourceBytes && !("managed-tool-outcome".equals(resource.kind()) && resource.bytesBase64() == null)) { - throw new ManagedAgentException(HttpStatus.NOT_IMPLEMENTED, + throw new ManagedAgentException(501, ManagedSessionStoreModels.ERROR_OSS_DISABLED, "Resources larger than " + (maxResourceBytes / 1024) + " KiB require the disabled" + " OSS storage path."); @@ -3223,8 +3238,7 @@ public class ManagedSessionStore { ? null : new CsiContinuation( original, - com.alibaba.qwen.code.runtimebroker - .JdbcCsiApplicationProgress.lock(target, original)); + com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationProgress.lock(target, original)); }); } @@ -3293,7 +3307,7 @@ public class ManagedSessionStore { private record CsiContinuation( JdbcCsiFilesRetirementGuard.Original original, - com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationProgress.Continuation progress) {} + com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationProgress.Continuation progress) {} private static void requireCsiWriter( JdbcCsiFilesRetirementGuard.Original original, HeadRow head) { @@ -3386,7 +3400,7 @@ public class ManagedSessionStore { "SELECT " + ManagedSchemaProjection.RESOURCE.columns() + " FROM ma_resource WHERE resource_key = ? AND session_scope_key = ?" + " AND resource_id = ?" + (lock ? " FOR UPDATE" : ""), resourceMapper, - resourceKey(scopeKey, resourceId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scopeKey, resourceId), scopeKey, resourceId); return rows.isEmpty() ? null : rows.get(0); @@ -3525,7 +3539,7 @@ public class ManagedSessionStore { return false; } try { - return com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.identity(value).length <= maxBytes; + return com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.identity(value).length <= maxBytes; } catch (IllegalArgumentException invalidUnicode) { return false; } @@ -3671,7 +3685,7 @@ public class ManagedSessionStore { } private static ManagedAgentException resourceCorrupt() { - return new ManagedAgentException(HttpStatus.INTERNAL_SERVER_ERROR, + return new ManagedAgentException(500, "managed_session_resource_corrupt", "The Managed Session resource failed verification."); } @@ -3725,18 +3739,18 @@ public class ManagedSessionStore { } } - private static void requireHeadScope(HeadRow head, String tenantId, + private void requireHeadScope(HeadRow head, String tenantId, String workspaceId, String sessionId) { if (!tenantId.equals(head.tenantId()) || !workspaceId.equals(head.workspaceId()) || !sessionId.equals(head.sessionId())) { throw sessionNotFound(); } - if (head.storageVersion() < 1 || head.storageVersion() > ManagedSessionReaderVersion.SUPPORTED) { + if (head.storageVersion() < 1 || head.storageVersion() > com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedStorageVersions.SUPPORTED) { throw conflict("managed_session_storage_version_unsupported", "The Managed Session storage version is unsupported."); } - ManagedSessionReaderVersion.requireReadable(head.storageVersion()); + readerVersion.requireReadable(head.storageVersion()); if (!HEAD_STATES.contains(head.state()) || !RECOVERY_STATES.contains(head.recoveryStatus()) || head.writerGeneration() < 1 @@ -3784,7 +3798,7 @@ public class ManagedSessionStore { private static void requireTransactionWorkspace(TransactionRow row, String workspaceId) { if (!workspaceId.equals(row.workspaceId())) { - throw new ManagedAgentException(HttpStatus.INTERNAL_SERVER_ERROR, + throw new ManagedAgentException(500, ManagedSessionStoreModels.ERROR_JOURNAL_CORRUPT, "The Managed Session transaction scope is corrupt."); } @@ -3793,7 +3807,7 @@ public class ManagedSessionStore { private static String tokenHash(String writerToken) { if (writerToken == null || !WRITER_TOKEN.matcher(writerToken).matches()) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_writer_token", ManagedSessionStoreModels.WRITER_TOKEN_HEADER + " must contain 32-512 Base64URL characters."); @@ -3815,14 +3829,6 @@ public class ManagedSessionStore { now); } - public static String sessionScopeKey(String tenantId, String sessionId) { - return com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(tenantId, sessionId); - } - - public static String resourceKey(String scopeKey, String resourceId) { - return com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(scopeKey, resourceId); - } - static String commandKeyHash(String operation, String commandId) { return sha256((operation + "\u0000" + commandId) @@ -3840,7 +3846,7 @@ public class ManagedSessionStore { } private static ManagedAgentException sessionNotFound() { - return new ManagedAgentException(HttpStatus.NOT_FOUND, + return new ManagedAgentException(404, "managed_session_not_found", "The Managed Session does not exist."); } @@ -3853,29 +3859,29 @@ public class ManagedSessionStore { // Package-private: ToolPublicationStore's evidence reads answer a // damaged journal with the same fault the session store raises. static ManagedAgentException journalCorrupt() { - return new ManagedAgentException(HttpStatus.INTERNAL_SERVER_ERROR, + return new ManagedAgentException(500, ManagedSessionStoreModels.ERROR_JOURNAL_CORRUPT, "The Managed Session transaction failed verification."); } private static ManagedAgentException headCorrupt() { - return new ManagedAgentException(HttpStatus.INTERNAL_SERVER_ERROR, + return new ManagedAgentException(500, ManagedSessionStoreModels.ERROR_HEAD_CORRUPT, "The Managed Session head failed verification."); } private static ManagedAgentException invalid(String message) { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, + return new ManagedAgentException(400, ManagedSessionStoreModels.ERROR_INVALID_REQUEST, message); } private static ManagedAgentException payloadTooLarge(String message) { - return new ManagedAgentException(HttpStatus.PAYLOAD_TOO_LARGE, + return new ManagedAgentException(413, ManagedSessionStoreModels.ERROR_PAYLOAD_TOO_LARGE, message); } private static ManagedAgentException conflict(String code, String message) { - return new ManagedAgentException(HttpStatus.CONFLICT, code, message); + return new ManagedAgentException(409, code, message); } private record HeadRow(String tenantId, String workspaceId, diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedTaskEventStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedTaskEventStore.java similarity index 83% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedTaskEventStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedTaskEventStore.java index 3c6e81ba567..84504368471 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedTaskEventStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedTaskEventStore.java @@ -1,13 +1,13 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Repository; @@ -17,55 +17,40 @@ import java.nio.charset.StandardCharsets; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; -import java.util.Base64; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.function.Supplier; /** - * The bounded per-task event journal behind - * {@code listSessionTaskEvents} and {@code queryWebShellTaskEvents} (H3 of - * #12827). Producers write an event in the transaction that commits what - * produces it, so a read never passes a concurrently committing event; the - * per-task sequence is allocated under its event-stream row lock, in - * commit order. One event is one logical cursor position, and a cursor names - * only durable state, so identity survives restarts, projection rebuilds and - * archival. + * The bounded per-task event journal behind {@code listSessionTaskEvents} and {@code + * queryWebShellTaskEvents} (H3 of #12827). Producers write an event in the transaction that commits + * what produces it, so a read never passes a concurrently committing event; the per-task sequence + * is allocated under its event-stream row lock, in commit order. One event is one logical cursor + * position, and a cursor names only durable state, so identity survives restarts, projection + * rebuilds and archival. * - *

The durable retention floor lives in the per-task event-stream row, so it - * survives an empty retained set. Only an oldest prefix expires, and the - * visibility barrier pins the floor behind every output event whose full - * text is not yet archived and discoverable; an archival failure therefore - * loses nothing and the backlog bound refuses the next accepted event - * instead of silently discarding one. Output events carry their segment - * identity in the per-stream ordinal space, and appends keep the ranges - * continuous and non-overlapping, so a {@code cursor_expired} recovery joins - * Artifacts and events without duplication. + *

The durable retention floor lives in the per-task event-stream row, so it survives an empty + * retained set. Only an oldest prefix expires, and the visibility barrier pins the floor behind + * every output event whose full text is not yet archived and discoverable; an archival failure + * therefore loses nothing and the backlog bound refuses the next accepted event instead of silently + * discarding one. Output events carry their segment identity in the per-stream ordinal space, and + * appends keep the ranges continuous and non-overlapping, so a {@code cursor_expired} recovery + * joins Artifacts and events without duplication. */ @Repository -public class ManagedTaskEventStore { - public static final int SCHEMA_VERSION = 1; - public static final int PROJECTION_VERSION = 1; - public static final int MAX_OUTPUT_CHARS = 16384; +public class ManagedTaskEventStore + implements com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore { /** The newest unique references a task view lists; the 101st refuses. */ - public static final int ARTIFACT_REF_BOUND = 100; + /** The expiry pass keeps this many retained rows behind every append. */ - public static final int RETAIN_TARGET = 256; + /** * Retained rows a task's journal may hold; an append past the bound is a * refusal, which is the producer backpressure the contract requires * while the barrier pins the floor. */ - public static final int BACKLOG_BOUND = 512; - public static final String CODE_ADMISSION_REFUSED = - "managed_task_output_admission_refused"; - public static final String CODE_FLOOR_PINNED = - "managed_task_event_floor_pinned"; - public static final String CODE_BACKLOG_FULL = - "managed_task_event_backlog_full"; - public static final String CODE_SEGMENT_CONFLICT = - "managed_task_event_segment_conflict"; + private static final ObjectMapper JSON = new ObjectMapper(); private final JdbcTemplate jdbc; private final TransactionTemplate transactions; @@ -77,61 +62,13 @@ public class ManagedTaskEventStore { } /** One committed task event, as the journal row holds it. */ - public record TaskEvent(long sequence, String type, long occurredAt, - int schemaVersion, int projectionVersion, String state, - String runtimeState, String text, Boolean truncated, - String artifactId, String captureId, String streamId, - Long firstOrdinal, Long endOrdinal, boolean archived) { - } - - public record EventPage(List events, boolean hasMore) { - } /** * The durable per-task positions: the committed tail, the retention * floor and the newest unique artifact references, oldest first. */ - public record CursorPositions(long lastSequence, long expiredThrough, - List artifactRefs) { - static final CursorPositions EMPTY = new CursorPositions(0, 0, - List.of()); - } /** Scoped durable task positions; raw identity delimiters never enter the encoding. */ - public static String encodeCursor(String tenantId, String sessionId, String taskId, long sequence) { - if (sequence < 0) { - throw new IllegalArgumentException("A task cursor requires a nonnegative sequence"); - } - return Base64.getUrlEncoder().withoutPadding().encodeToString( - ("tev2:" + ManagedSchemaKeys.key(tenantId, sessionId, taskId) + ":" + sequence) - .getBytes(StandardCharsets.US_ASCII)); - } - - public static long decodeCursor(String tenantId, String sessionId, String taskId, String cursor) { - if (cursor == null || cursor.length() > 128) { - throw invalidCursor(); - } - String decoded; - try { - decoded = new String(Base64.getUrlDecoder().decode(cursor), StandardCharsets.US_ASCII); - } catch (IllegalArgumentException error) { - throw invalidCursor(); - } - String[] parts = decoded.split(":", -1); - if (parts.length != 3 || !"tev2".equals(parts[0]) - || !ManagedSchemaKeys.key(tenantId, sessionId, taskId).equals(parts[1])) { - throw invalidCursor(); - } - try { - long position = Long.parseLong(parts[2]); - if (position < 0) { - throw new NumberFormatException(); - } - return position; - } catch (NumberFormatException error) { - throw invalidCursor(); - } - } /** Operational positions survive an empty event set and projection rebuilds. */ public CursorPositions positions(String tenantId, String sessionId, String taskId) { @@ -369,7 +306,7 @@ public class ManagedTaskEventStore { } catch (java.io.IOException exception) { throw new IllegalStateException("The task cursor resource is corrupt", exception); } - }, resourceKey, ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, "task-artifact-refs/1") + }, resourceKey, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, "task-artifact-refs/1") .stream().findFirst().orElseThrow(() -> new IllegalStateException("The task cursor resource is missing")); } @@ -381,7 +318,7 @@ public class ManagedTaskEventStore { body.putArray("artifactRefs"); } byte[] bytes = body.toString().getBytes(StandardCharsets.UTF_8); - String scope = ManagedSessionStore.sessionScopeKey(tenantId, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId); String digest = ToolPublicationContract.sha256(bytes); String resourceId = "task-cursor:" + ManagedSchemaKeys.key(taskId, digest); String resourceKey = ManagedSchemaKeys.key(scope, resourceId); @@ -434,7 +371,7 @@ public class ManagedTaskEventStore { } private static ManagedAgentException conflict(String code, String message) { - return new ManagedAgentException(HttpStatus.CONFLICT, code, message); + return new ManagedAgentException(409, code, message); } private static TaskEvent eventRow(ResultSet result, int row) throws SQLException { @@ -445,8 +382,4 @@ public class ManagedTaskEventStore { result.getString("capture_id"), result.getString("stream_id"), result.getObject("first_ordinal", Long.class), result.getObject("end_ordinal", Long.class), result.getBoolean("archived")); } - - private static ManagedAgentException invalidCursor() { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_event_cursor", "The task event cursor is invalid."); - } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolDetailsStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolDetailsStore.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolDetailsStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolDetailsStore.java index 5f85c3284ff..c405140f57c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolDetailsStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolDetailsStore.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @@ -17,7 +16,8 @@ import java.util.ArrayList; import java.util.List; @Repository -public class ManagedToolDetailsStore { +public class ManagedToolDetailsStore + implements com.alibaba.qwen.code.managedagent.repository.ManagedToolDetailsStore { private static final int READ_LIMIT = 8 * 1024 * 1024; private final JdbcTemplate jdbc; private final ManagedExtensionRecordStore resources; @@ -295,11 +295,11 @@ public class ManagedToolDetailsStore { private record Message(byte[] bytes, int readBytes) { } private static ManagedAgentException limit() { - return new ManagedAgentException(HttpStatus.PAYLOAD_TOO_LARGE, "tool_details_limit", + return new ManagedAgentException(413, "tool_details_limit", "Tool details exceed the bounded history read limit."); } private static ManagedAgentException unavailable() { - return new ManagedAgentException(HttpStatus.NOT_FOUND, "tool_details_not_found", "Tool details are unavailable."); + return new ManagedAgentException(404, "tool_details_not_found", "Tool details are unavailable."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjector.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjector.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjector.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjector.java index 3ffbbb5c5b1..93bd59924e4 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjector.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjector.java @@ -1,11 +1,13 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ManagedArtifactPolicy; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.persistence.ManagedToolResultStore.Artifact; -import com.alibaba.qwen.code.managedagent.persistence.ManagedToolResultStore.Claim; -import com.alibaba.qwen.code.managedagent.persistence.ManagedToolResultStore.Projection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedToolResultStore.Source; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ManagedArtifactPolicy; +import com.alibaba.qwen.code.managedagent.contracts.persistence.EventIdentity; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.Artifact; +import com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.Claim; +import com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.Projection; +import com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.Source; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -42,11 +44,11 @@ public class ManagedToolResultProjector { private final ObjectProvider publications; private final ManagedArtifactReader reader; private final ManagedArtifactPolicy policy; - private final ManagedAgentProperties properties; + private final ManagedAgentSettings properties; public ManagedToolResultProjector(ManagedToolResultStore store, @Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, ObjectProvider publications, ManagedArtifactReader reader, - ManagedArtifactPolicy policy, ManagedAgentProperties properties) { + ManagedArtifactPolicy policy, ManagedAgentSettings properties) { this.store = store; this.jdbc = jdbc; this.publications = publications; @@ -219,7 +221,7 @@ public class ManagedToolResultProjector { require("sealed".equals(stream.path("state").asText()) && List.of("stdout", "stderr").contains(streamId) && streamId.equals(stream.path("role").asText()), "Unsupported public stream"); - String id = ManagedToolResultStore.identity("artifact", "1", source.id(), + String id = com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.identity("artifact", "1", source.id(), manifestRef.path("digest").asText(), streamId, policyVersion); ObjectNode descriptor = JSON.createObjectNode().put("object", "agent.artifact") .put("id", id).put("session_id", source.sessionId()) @@ -301,7 +303,7 @@ public class ManagedToolResultProjector { + " FROM ma_resource WHERE session_scope_key = ? AND resource_id = ?" + " AND tenant_id = ? AND workspace_id = ? AND session_id = ? AND deletion_operation_key IS NULL", (schemaRow, schemaIndex) -> com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows.read(schemaRow), - ManagedSessionStore.sessionScopeKey(source.tenantId(), source.sessionId()), source.outcomeRef().path("resourceId").asText(), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(source.tenantId(), source.sessionId()), source.outcomeRef().path("resourceId").asText(), source.tenantId(), source.workspaceId(), source.sessionId()); require(rows.size() == 1, "Not-started outcome is missing"); var row = rows.getFirst(); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultStore.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultStore.java index 238e210fdff..c4cf8321813 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultStore.java @@ -1,11 +1,12 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -15,7 +16,6 @@ import org.springframework.stereotype.Repository; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionTemplate; -import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.sql.ResultSet; import java.sql.SQLException; @@ -29,8 +29,10 @@ import java.util.Objects; import java.util.Optional; @Repository -public class ManagedToolResultStore { +public class ManagedToolResultStore + implements com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore { private static final ObjectMapper JSON = new ObjectMapper(); + private final JdbcTemplate jdbc; private final TransactionTemplate transactions; private final AgentStateStore sessions; @@ -42,25 +44,6 @@ public class ManagedToolResultStore { this.sessions = sessions; } - public record Source(String id, String tenantId, String workspaceId, String sessionId, - String executionCallId, long journalRevision, long receiptSequence, - JsonNode outcomeRef, JsonNode resultRef, JsonNode resources, String sourceDigest) { - public JsonNode sessionKey() { - return JSON.createObjectNode().put("tenantId", tenantId).put("workspaceId", workspaceId) - .put("sessionId", sessionId); - } - } - - public record Claim(Source source, long generation) { } - - public record Artifact(JsonNode descriptor, Source source, String publicationId, - JsonNode binding, JsonNode manifestRef, String streamId, long creationSequence) { } - - public record ArtifactPage(List artifacts, long watermark, boolean hasMore) { } - - public record Projection(JsonNode descriptor, String publicationId, JsonNode binding, - JsonNode manifestRef, List artifacts, String policyVersion) { } - void captureEvents(String tenant, String workspace, String session, long revision, List events) { Map sources = new LinkedHashMap<>(); for (JsonNode event : events) { @@ -93,7 +76,7 @@ public class ManagedToolResultStore { List rows = new ArrayList<>(); for (Source source : sources.values()) { String executionKey = - identity( + com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.identity( "execution", source.executionCallId().isEmpty() ? "sequence:" + source.receiptSequence() @@ -594,18 +577,8 @@ public class ManagedToolResultStore { row.getLong("creation_sequence")); } - public static String identity(String prefix, String... values) { - var digest = new java.io.ByteArrayOutputStream(); - for (String value : values) { - byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - digest.writeBytes(ByteBuffer.allocate(4).putInt(bytes.length).array()); - digest.writeBytes(bytes); - } - return prefix + "_" + ToolPublicationContract.sha256(digest.toByteArray()); - } - static String scope(String tenant, String session) { - return identity("s", tenant, session).substring(2); + return com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.identity("s", tenant, session).substring(2); } private static Source source(String tenant, String workspace, String session, long revision, JsonNode event) { @@ -623,9 +596,9 @@ public class ManagedToolResultStore { JsonNode resources = canonical(payload.path("resources")); String identityKey = execution.isEmpty() ? "sequence:" + event.path("sequence").asText() : execution; - String id = identity("result", "1", tenant, workspace, session, identityKey); + String id = com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.identity("result", "1", tenant, workspace, session, identityKey); String digest = - identity( + com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.identity( "source", tenant, workspace, diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedUserMessageStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedUserMessageStore.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedUserMessageStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedUserMessageStore.java index 24404f03242..289a50d87c0 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedUserMessageStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedUserMessageStore.java @@ -1,15 +1,14 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.session.UserMessagePolicy; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; import com.alibaba.qwen.code.managedagent.model.RequestDigests; -import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -23,25 +22,25 @@ import java.util.UUID; @Service @Qualifier("managedAgentTransactionManager") -public class ManagedUserMessageStore { +public class ManagedUserMessageStore + implements com.alibaba.qwen.code.managedagent.repository.ManagedUserMessageStore { + private com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles worktreeFiles; + @org.springframework.beans.factory.annotation.Autowired + public void configureWorktreeFiles(com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles value) { worktreeFiles = value; } + private static final TypeReference>> INPUT = new TypeReference<>() { }; private final JdbcTemplate jdbc; private final ObjectMapper json; private final RequestDigests digests; - private final ManagedAgentService agents; + private final UserMessagePolicy agents; - public ManagedUserMessageStore(@Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, @Qualifier("managedAgentObjectMapper") ObjectMapper json, RequestDigests digests, ManagedAgentService agents) { + public ManagedUserMessageStore(@Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, @Qualifier("managedAgentObjectMapper") ObjectMapper json, RequestDigests digests, @Qualifier("managedUserMessagePolicy") UserMessagePolicy agents) { this.jdbc = jdbc; this.json = json; this.digests = digests; this.agents = agents; } - public record Message(String id, long sequence, long revision, String state, - List> input, String turnId) { } - public record Snapshot(boolean paused, List messages) { } - public record Command(String action, String messageId, Long expectedRevision, List input) { } - @Transactional public Snapshot queryMessages(String tenant, String actor, String session) { agents.authorizeUserMessages(tenant, actor, session); @@ -82,7 +81,7 @@ public class ManagedUserMessageStore { reconcile(tenant, session); switch (command.action()) { case "submit" -> { - SessionWorktreeStore.requireReady(jdbc, tenant, session); + SessionWorktreeStore.requireReady(worktreeFiles, jdbc, tenant, session); List> input = agents.userMessageInput(command.input()); Integer count = jdbc.queryForObject("SELECT COUNT(*) FROM ma_user_message WHERE tenant_id = ?" + " AND session_id = ? AND state IN ('queued', 'claimed', 'recovery_blocked')", Integer.class, tenant, session); @@ -118,7 +117,7 @@ public class ManagedUserMessageStore { } } case "resume" -> { - SessionWorktreeStore.requireReady(jdbc, tenant, session); + SessionWorktreeStore.requireReady(worktreeFiles, jdbc, tenant, session); if ("RECOVERY_BLOCKED".equals(latestTurn(tenant, session).get("status"))) { throw conflict("user_message_recovery_blocked"); } @@ -189,7 +188,7 @@ public class ManagedUserMessageStore { } @Transactional - public void startNext(String tenant, String session) { + public void startNext(String tenant, String session, QueuedTurnSubmitter submitter) { if (!agents.supportsUserMessageQueue()) { return; } @@ -225,7 +224,7 @@ public class ManagedUserMessageStore { return new InputBlock("input_file", (String) resource.get("text"), (String) resource.get("mimeType"), null, name); }).toList(); jdbc.update("UPDATE ma_user_message SET state = 'claimed' WHERE tenant_id = ? AND session_id = ? AND message_id = ? AND state = 'queued'", tenant, session, first.id()); - var admission = agents.submitTurn(tenant, actor, "user-queue:" + first.id(), session, input); + var admission = submitter.submit(tenant, actor, "user-queue:" + first.id(), session, input); String prompt = jdbc.queryForObject("SELECT prompt_id FROM ma_turn WHERE tenant_id = ? AND session_id = ? AND turn_id = ?", String.class, tenant, session, admission.turnId()); jdbc.update("UPDATE ma_user_message SET state = 'claimed', turn_id = ?, prompt_id = ? WHERE tenant_id = ?" @@ -243,7 +242,7 @@ public class ManagedUserMessageStore { var status = jdbc.query("SELECT status FROM ma_session WHERE tenant_id = ? AND session_id = ? FOR UPDATE", (row, index) -> row.getString(1), tenant, session); if (status.isEmpty()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", "Session not found."); + throw new ManagedAgentException(404, "session_not_found", "Session not found."); } return status.getFirst(); } @@ -350,9 +349,9 @@ public class ManagedUserMessageStore { } } private static ManagedAgentException conflict(String code) { - return new ManagedAgentException(HttpStatus.CONFLICT, code, "The user message queue changed or is unavailable."); + return new ManagedAgentException(409, code, "The user message queue changed or is unavailable."); } private static ManagedAgentException refusal(String code, String message) { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, code, message); + return new ManagedAgentException(400, code, message); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedWorkspaceRegistry.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedWorkspaceRegistry.java similarity index 87% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedWorkspaceRegistry.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedWorkspaceRegistry.java index fe1d14f6698..ab9c28f43f3 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedWorkspaceRegistry.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedWorkspaceRegistry.java @@ -1,16 +1,14 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceAccess; -import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceActor; import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceRecord; import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceState; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -23,13 +21,14 @@ import java.util.List; import java.util.Map; @Repository -public class ManagedWorkspaceRegistry { +public class ManagedWorkspaceRegistry + implements com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry { private static final ObjectMapper JSON = new ObjectMapper(); private final JdbcTemplate jdbc; - private com.alibaba.qwen.code.managedagent.config.SshRuntimeSettings ssh; + private com.alibaba.qwen.code.managedagent.settings.SshSettings ssh; @org.springframework.beans.factory.annotation.Autowired(required = false) - void configureSsh(com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties properties) { + void configureSsh(com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings properties) { this.ssh = properties.getRuntimeBroker().getSsh(); } @@ -43,7 +42,7 @@ public class ManagedWorkspaceRegistry { } byte[] key; try { - key = actorKey(tenantId, actorId); + key = com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId); } catch (IllegalArgumentException error) { return false; } @@ -59,7 +58,7 @@ public class ManagedWorkspaceRegistry { } byte[] key; try { - key = actorKey(tenantId, actorId); + key = com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId); } catch (IllegalArgumentException error) { return false; } @@ -81,7 +80,7 @@ public class ManagedWorkspaceRegistry { } byte[] key; try { - key = actorKey(tenantId, actorId); + key = com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId); } catch (IllegalArgumentException error) { return WorkspaceAccess.NONE; } @@ -124,8 +123,8 @@ public class ManagedWorkspaceRegistry { public List listReadable(String tenantId, String actorId, String afterId, int limit) { - byte[] key = actorKey(tenantId, actorId); - byte[] after = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.identity(afterId == null ? "" : afterId); + byte[] key = com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId); + byte[] after = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.identity(afterId == null ? "" : afterId); List rows = jdbc.query("SELECT r.workspace_id, r.display_name, r.state, r.workspace_kind, s.storage_kind, a.role FROM ma_workspace r" + " JOIN ma_workspace_access a ON a.tenant_id = r.tenant_id AND a.workspace_id = r.workspace_id" + " LEFT JOIN ma_storage s ON s.tenant_id = r.tenant_id AND s.storage_id = r.storage_id" @@ -137,7 +136,7 @@ public class ManagedWorkspaceRegistry { public WorkspaceSummary findReadable(String tenantId, String actorId, String workspaceId) { - byte[] key = actorKey(tenantId, actorId); + byte[] key = com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId); List rows = jdbc.query( "SELECT r.workspace_id, r.display_name, r.state," + " r.workspace_kind, s.storage_kind, a.role FROM ma_workspace r" @@ -164,7 +163,7 @@ public class ManagedWorkspaceRegistry { if (workspaceIds.isEmpty()) { return java.util.Map.of(); } - byte[] key = actorKey(tenantId, actorId); + byte[] key = com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId); String plain = String.join(", ", java.util.Collections.nCopies( workspaceIds.size(), "?")); List arguments = new java.util.ArrayList<>( @@ -267,21 +266,11 @@ public class ManagedWorkspaceRegistry { .isEmpty(); } - public record WorkspaceSummary(String workspaceId, String displayName, - String state, boolean canCreateSession, String defaultCwdRelative, boolean canSelectDirectory) { - public WorkspaceSummary(String workspaceId, String displayName, String state, boolean canCreateSession) { - this(workspaceId, displayName, state, canCreateSession, ".", false); - } - } - /** * A readable Workspace with the generation and storage the registry holds * now, so a batch caller can tell whether a Session's recorded binding is * still the current one. */ - public record ReadableGrant(String workspaceId, long workspaceGeneration, - String storageId, boolean canCreateSession) { - } public ResolvedBinding resolveForCreation(String tenantId, String actorId, WorkspaceSelection selection) { @@ -290,14 +279,14 @@ public class ManagedWorkspaceRegistry { "Workspace resolution requires a creation transaction"); } if (actorId == null || actorId.isEmpty()) { - throw new ManagedAgentException(HttpStatus.UNAUTHORIZED, + throw new ManagedAgentException(401, "actor_required", "A trusted actor is required."); } byte[] key; try { - key = actorKey(tenantId, actorId); + key = com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenantId, actorId); } catch (IllegalArgumentException error) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, + throw new ManagedAgentException(403, "actor_scope_mismatch", "Authenticated actor scope is invalid."); } String workspaceId; @@ -324,7 +313,7 @@ public class ManagedWorkspaceRegistry { if (selection == null) { throw workspaceRequired(); } - throw new ManagedAgentException(HttpStatus.NOT_FOUND, + throw new ManagedAgentException(404, "workspace_not_found", "Workspace not found."); } List access = jdbc.query( @@ -339,14 +328,14 @@ public class ManagedWorkspaceRegistry { if (selection == null) { throw workspaceRequired(); } - throw new ManagedAgentException(HttpStatus.NOT_FOUND, + throw new ManagedAgentException(404, "workspace_not_found", "Workspace not found."); } if (!access.getFirst().value().canCreate()) { if (selection == null) { throw workspaceRequired(); } - throw new ManagedAgentException(HttpStatus.FORBIDDEN, + throw new ManagedAgentException(403, "workspace_forbidden", "Workspace cannot be used."); } WorkspaceRecord record = records.getFirst(); @@ -355,7 +344,7 @@ public class ManagedWorkspaceRegistry { if (selection == null) { throw workspaceRequired(); } - throw new ManagedAgentException(HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "workspace_unavailable", "Workspace is unavailable."); } ContextBinding binding = new ContextBinding(tenantId, workspaceId, @@ -368,7 +357,7 @@ public class ManagedWorkspaceRegistry { void requireSshNodeAccess(String tenantId, String actorId, String storageId) { var remote = jdbc.queryForList("SELECT registration_json FROM ma_storage WHERE alias_key = ? AND storage_kind = 'SSH_LOCAL'", - String.class, com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenantId, storageId)); + String.class, com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenantId, storageId)); if (!remote.isEmpty()) { boolean allowed = false; if (ssh != null && ssh.isEnabled()) { @@ -376,11 +365,11 @@ public class ManagedWorkspaceRegistry { String nodeId = JSON.readTree(remote.getFirst()).path("nodeId").asText(); allowed = ssh.getNodes().stream().anyMatch(node -> node.nodeId().equals(nodeId) && node.admits(tenantId, actorId)); } catch (com.fasterxml.jackson.core.JsonProcessingException error) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "workspace_unavailable", "Workspace registration is invalid."); + throw new ManagedAgentException(409, "workspace_unavailable", "Workspace registration is invalid."); } } if (!allowed) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, "runtime_node_forbidden", "SSH runtime node is disabled or unauthorized."); + throw new ManagedAgentException(403, "runtime_node_forbidden", "SSH runtime node is disabled or unauthorized."); } } } @@ -390,16 +379,11 @@ public class ManagedWorkspaceRegistry { if (accessOf(tenant, actor, workspace) != WorkspaceAccess.OWNER || jdbc.queryForList("SELECT 1 FROM ma_workspace WHERE tenant_id = ? AND workspace_id = ?" + " AND workspace_kind = 'ROOT'", Integer.class, tenant, workspace).isEmpty()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "workspace_not_found", "Workspace not found."); + throw new ManagedAgentException(404, "workspace_not_found", "Workspace not found."); } return resolved; } - public static byte[] actorKey(String tenantId, String actorId) { - return new WorkspaceActor(tenantId, actorId).getActorId() - .getBytes(StandardCharsets.UTF_8); - } - static String descriptorRef(String configRef, String policyRef) { try { byte[] bytes = (configRef + "\u0000" + policyRef) @@ -427,14 +411,10 @@ public class ManagedWorkspaceRegistry { } private static ManagedAgentException workspaceRequired() { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, + return new ManagedAgentException(400, "workspace_required", "Select a Workspace."); } - public record ResolvedBinding(ContextBinding binding, String configRef, - String policyRef) { - } - private record AccessRow(WorkspaceAccess value) { } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStore.java similarity index 68% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStore.java index 87a6bc6bb18..1309c2bce28 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStore.java @@ -1,16 +1,15 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.ResidentWorkspace; -import com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceCsiResourceGuard; -import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentReclamation; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker.ResidentWorkspace; import com.alibaba.qwen.code.runtimebroker.CsiOwnershipRevision; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesHttpRuntimeClient; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.spi.KubernetesMaintenanceClient; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -22,7 +21,6 @@ import org.springframework.transaction.support.TransactionTemplate; import java.nio.charset.StandardCharsets; import java.time.Duration; -import java.time.Instant; import java.util.Base64; import java.util.LinkedHashMap; import java.util.List; @@ -36,7 +34,7 @@ import static com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity.ma import static com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity.same; /** Explicit idle maintenance with original platform cleanup and atomic ownership release. */ -public final class ResidentWorkspaceRetirementStore { +public final class ResidentWorkspaceRetirementStore implements com.alibaba.qwen.code.managedagent.repository.ResidentRetirementRepository { private static final String WORK = "POD_DELETE_ATTEMPT"; private static final String FINALIZER_WORK = "POD_FINALIZER_ATTEMPT"; private static final int PLAN_LIMIT = 256 * 1024; @@ -46,37 +44,12 @@ public final class ResidentWorkspaceRetirementStore { private final List mappings; private final ObjectMapper json; - public record Request(String tenantId, String workspaceId, long workspaceGeneration, - String actorId, String retirementId) { - public Request { - for (String value : new String[] {tenantId, workspaceId, actorId, retirementId}) { - require(value != null && !value.isBlank() && value.getBytes(StandardCharsets.UTF_8).length <= 512 - && value.codePoints().noneMatch(point -> point < 32 || point >= 127 && point <= 159 - || point >= 0xd800 && point <= 0xdfff)); - } - require(workspaceGeneration > 0); - } - } - @JsonInclude(JsonInclude.Include.NON_NULL) private record Plan(int version, Request request, String bindingId, long runtimeGeneration, - String resourceIdentity, String mappingDigest, ResidentWorkspace mapping) { } - - public record AttemptStatus(int ordinal, String resourceVersion, String state, Integer httpStatus, - String responseSha256) { } - - public record FinalizerAttemptStatus(int ordinal, String action, String resourceVersion, String state, - Integer httpStatus, String responseSha256) { } - - public record Status(String retirementId, String state, String bindingId, long runtimeGeneration, - List attempts, List finalizerAttempts, Completion completion) { } - - public record Completion(String receiptSha256, String dataPolicy, Long releasedRevision) { } - - private record Attempt(String workKey, String owner, long generation, byte[] body) { } + String resourceIdentity, String mappingDigest, com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentWorkspaceSnapshot mapping) { } public ResidentWorkspaceRetirementStore(JdbcTemplate jdbc, PlatformTransactionManager manager, - JdbcRuntimeBindingRepository bindings, List mappings, ObjectMapper json) { + JdbcRuntimeBindingRepository bindings, List mappings, ObjectMapper json) { require(jdbc.getDataSource() != null && bindings.usesDataSource(jdbc.getDataSource())); this.jdbc = jdbc; this.transaction = new TransactionTemplate(manager); @@ -159,7 +132,7 @@ public final class ResidentWorkspaceRetirementStore { + " AND target_key = ? AND target_revision = ? FOR UPDATE", String.class, WorkspaceOperations.hash(locked.getBindingId()), locked.getGeneration()).isEmpty()); Plan plan = new Plan(2, request, locked.getBindingId(), locked.getGeneration(), - (String) locked.getResourceHandle().getValue().get("identity"), digest(selected), selected); + (String) locked.getResourceHandle().getValue().get("identity"), digest(selected), new com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentWorkspaceSnapshot(selected.workspaceId(), selected.workspaceGeneration(), selected.registration(), selected.protection(), selected.image(), selected.command(), selected.artifacts())); String encoded = encode(plan); require(encoded.getBytes(StandardCharsets.UTF_8).length <= PLAN_LIMIT); operations.create(WORKSPACE_RETIREMENT, request.retirementId(), WorkspaceOperations.values( @@ -180,292 +153,21 @@ public final class ResidentWorkspaceRetirementStore { } } - public Status deletePod(Request request, String resourceVersion, KubernetesHttpRuntimeClient api) { - require(resourceVersion != null && resourceVersion.matches("[\\x21-\\x7e]{1,256}")); - var selected = selected(request); - var original = transaction.execute(status -> lockedPlan(request, selected, true)); - var placement = map(original.getResourceHandle().getValue().get("placement")); - String workKey = WorkspaceOperations.hash((String) placement.get("podUid"), resourceVersion); - if (operations(request).findWork(WORKSPACE_RETIREMENT, request.retirementId(), WORK, workKey, false) != null) { - return inspect(request); - } - new WorkspaceCsiResourceGuard(api, selected.registration().clusterDomain(), selected.registration(), - selected.protection()).verify().toCompletableFuture().join(); - String podName = (String) placement.get("podName"); - var pod = api.get("pods", selected.registration().namespace(), podName).toCompletableFuture().join(); - var saved = operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), false); - var baseline = saved.get("reclamationBaseline") == null ? null : baseline(saved); - verifyPod(original, pod, resourceVersion, baseline == null ? List.of() : List.of(baseline.finalizer())); - var node = api.getCluster("nodes", (String) placement.get("nodeName")).toCompletableFuture().join(); - require(node != null && placement.get("nodeUid").equals(map(node.get("metadata")).get("uid")) - && map(node.get("status")).get("conditions") instanceof List conditions - && conditions.stream().anyMatch(value -> "Ready".equals(map(value).get("type")) - && "True".equals(map(value).get("status")))); - byte[] body = WorkspaceCsiRuntimeIdentity.bytes(Map.of("apiVersion", "v1", "kind", "DeleteOptions", - "gracePeriodSeconds", 30, "propagationPolicy", "Background", "preconditions", - Map.of("uid", placement.get("podUid"), "resourceVersion", resourceVersion))); - Attempt attempt = transaction.execute(status -> { - lockedPlan(request, selected, true); - idle(request); - var operations = operations(request); - if (operations.findWork(WORKSPACE_RETIREMENT, request.retirementId(), WORK, workKey, true) != null) { - return null; - } - int count = attempts(request).size(); - require(count < 8); - String owner = UUID.randomUUID().toString(); - require(jdbc.update("UPDATE ma_operation SET claim_owner = ?, claim_generation = claim_generation + 1," - + " claim_until = " + ManagedSchemaTime.SQL_NOW + " + 60000 WHERE operation_key = ?" - + " AND (claim_until IS NULL OR claim_until <= " + ManagedSchemaTime.SQL_NOW + ")", - owner, operationKey(request)) == 1); - long generation = jdbc.queryForObject("SELECT claim_generation FROM ma_operation WHERE operation_key = ?", - Long.class, operationKey(request)); - operations.insertWork(WORKSPACE_RETIREMENT, request.retirementId(), WORK, workKey, workKey, null, - WorkspaceOperations.values("ordinal", count + 1, "resourceVersion", resourceVersion, - "requestBase64", Base64.getEncoder().encodeToString(body), "claimGeneration", generation), "UNKNOWN"); - return new Attempt(workKey, owner, generation, body); - }); - if (attempt == null) { - return inspect(request); - } - KubernetesHttpRuntimeClient.DeleteResponse reply = null; - try { - reply = api.deletePod(selected.registration().namespace(), podName, attempt.body()).toCompletableFuture().join(); - } catch (java.util.concurrent.CompletionException failure) { - // The durable UNKNOWN attempt must survive a lost reply without resending. - } - var response = reply; - transaction.executeWithoutResult(status -> { - lockedPlan(request, selected, false); - require(jdbc.queryForObject("SELECT COUNT(*) FROM ma_operation WHERE operation_key = ? AND claim_owner = ?" - + " AND claim_generation = ? AND claim_until > " + ManagedSchemaTime.SQL_NOW, - Long.class, operationKey(request), attempt.owner(), attempt.generation()) == 1); - if (response != null) { - require(response.body().length <= 64 * 1024); - operations(request).updateWork(WORKSPACE_RETIREMENT, request.retirementId(), WORK, attempt.workKey(), - WorkspaceOperations.values("state", response.complete() ? "RETURNED" : "UNKNOWN", "httpStatus", response.status(), - "responseBase64", Base64.getEncoder().encodeToString(response.body()), - "responseSha256", org.apache.commons.codec.digest.DigestUtils.sha256Hex(response.body()), - "complete", response.complete())); - } - jdbc.update("UPDATE ma_operation SET claim_owner = NULL, claim_until = NULL WHERE operation_key = ?" - + " AND claim_owner = ? AND claim_generation = ?", operationKey(request), attempt.owner(), attempt.generation()); - }); - return inspect(request); - } - - public Status prepareReclamation(Request request, String resourceVersion, KubernetesHttpRuntimeClient api) { - require(resourceVersion != null && resourceVersion.matches("[\\x21-\\x7e]{1,256}")); - var prior = inspect(request); - if (!"DRAINING".equals(prior.state())) { - require(!"NOT_STARTED".equals(prior.state())); - return prior; - } - var selected = selected(request); - var original = transaction.execute(status -> lockedPlan(request, selected, true)); - var saved = operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), false); - require(readPlan(request, saved).version() == 2); - verifyProtection(selected, api); - var placement = map(original.getResourceHandle().getValue().get("placement")); - var pod = api.get("pods", selected.registration().namespace(), (String) placement.get("podName")).toCompletableFuture().join(); - require(pod != null); - String finalizer = finalizer(request, original); - var retained = saved.get("reclamationBaseline") == null ? null : baseline(saved); - if (retained != null && List.of(finalizer).equals(map(pod.get("metadata")).get("finalizers"))) { - verifyPod(original, pod, resourceVersion, List.of(finalizer)); - return inspect(request); - } - verifyPod(original, pod, resourceVersion, List.of()); - var observed = ResidentWorkspaceReclamation.prepare(original, selected, finalizer, api, Instant.now()); - transaction.executeWithoutResult(status -> { - lockedPlan(request, selected, true); - idle(request); - var current = operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), true); - require("DRAINING".equals(current.get("state"))); - if (current.get("reclamationBaseline") == null) { - operations(request).update(WORKSPACE_RETIREMENT, request.retirementId(), WorkspaceOperations.values( - "reclamationBaseline", json.valueToTree(observed), "reclamationBaselineDigest", digest(observed))); - } else { - var first = baseline(current); - require(first.finalizer().equals(finalizer) && first.bootId().equals(observed.bootId()) - && first.leaseUid().equals(observed.leaseUid()) && first.attachmentUid().equals(observed.attachmentUid())); - } - }); - patchFinalizer(request, selected, original, pod, "ADD", api); - return inspect(request); - } - - public Status reclaim(Request request, KubernetesHttpRuntimeClient api) { - var prior = inspect(request); - if ("RELEASED".equals(prior.state())) { - return prior; - } - var selected = selected(request); - var original = transaction.execute(status -> lockedPlan(request, selected, true)); - var saved = operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), false); - require(readPlan(request, saved).version() == 2); - var baseline = baseline(saved); - require(baseline.finalizer().equals(finalizer(request, original))); - var placement = map(original.getResourceHandle().getValue().get("placement")); - String namespace = selected.registration().namespace(); - String name = (String) placement.get("podName"); - if ("DRAINING".equals(saved.get("state"))) { - require(!attempts(request).isEmpty()); - verifyProtection(selected, api); - var pod = api.get("pods", namespace, name).toCompletableFuture().join(); - var observed = ResidentWorkspaceReclamation.complete(original, selected, baseline, pod, api, Instant.now()); - transaction.executeWithoutResult(status -> { - lockedPlan(request, selected, true); - idle(request); - var current = operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), true); - if ("DRAINING".equals(current.get("state"))) { - require(digest(baseline).equals(current.get("reclamationBaselineDigest"))); - operations(request).update(WORKSPACE_RETIREMENT, request.retirementId(), WorkspaceOperations.values( - "state", "PLATFORM_CLEANED", "cleanupReceipt", json.valueToTree(observed), "cleanupReceiptDigest", digest(observed))); - } else { - receipt(current, original); - } - }); - } - saved = operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), false); - var proof = receipt(saved, original); - var pod = api.get("pods", namespace, name).toCompletableFuture().join(); - if (pod != null) { - var metadata = map(pod.get("metadata")); - require(placement.get("podUid").equals(metadata.get("uid")) - && placement.get("podName").equals(metadata.get("name")) - && placement.get("namespace").equals(metadata.get("namespace")) - && metadata.get("deletionTimestamp") instanceof String - && digest(pod.get("spec")).equals(original.getResourceHandle().getValue().get("podSpecDigest"))); - Instant.parse((String) metadata.get("deletionTimestamp")); - if (List.of(baseline.finalizer()).equals(metadata.get("finalizers"))) { - patchFinalizer(request, selected, original, pod, "REMOVE", api); - } else { - require(empty(metadata.get("finalizers"))); - } - if (api.get("pods", namespace, name).toCompletableFuture().join() != null) { - return inspect(request); - } - } - return transaction.execute(status -> { - var current = operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), false); - if ("RELEASED".equals(current.get("state"))) { - return inspect(request); - } - var locked = lockedPlan(request, selected, true); - idle(request); - current = operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), true); - require("PLATFORM_CLEANED".equals(current.get("state")) && digest(proof).equals(current.get("cleanupReceiptDigest"))); - receipt(current, locked); - require(jdbc.queryForObject("SELECT COUNT(*) FROM ma_operation WHERE operation_key = ?" - + " AND (claim_until IS NULL OR claim_until <= " + ManagedSchemaTime.SQL_NOW + ")", Long.class, operationKey(request)) == 1); - var storage = map(locked.getResourceHandle().getValue().get("storage")); - long revision = Math.addExact(Long.parseLong((String) storage.get("reservationRevision")), 1); - jdbc.execute((ConnectionCallback) connection -> { - bindings.releaseResidentForRetirement(connection, locked); - return null; - }); - require(jdbc.update("UPDATE ma_storage_owner SET csi_phase = 'RELEASED', csi_revision = ?, tenant_id = ?," - + " binding_id = NULL, runtime_generation = NULL, csi_reservation_id = NULL, csi_registration_key = NULL," - + " csi_registration_revision = NULL, csi_provision_request_id = NULL" - + " WHERE storage_key = ? AND storage_kind = 'CSI' AND csi_phase = 'RESERVED' AND csi_revision = ?" - + " AND binding_id = ? AND runtime_generation = ? AND csi_reservation_id = ?" - + " AND (tenant_id IS NULL OR tenant_id = ?)", revision, request.tenantId(), selected.registration().physicalKey(), - revision - 1, locked.getBindingId(), locked.getGeneration(), storage.get("reservationId"), request.tenantId()) == 1); - operations(request).update(WORKSPACE_RETIREMENT, request.retirementId(), WorkspaceOperations.values( - "state", "RELEASED", "releasedRevision", revision)); - require(jdbc.update("UPDATE ma_operation SET claim_owner = NULL, claim_until = NULL, claim_generation = claim_generation + 1" - + " WHERE operation_key = ?", operationKey(request)) == 1); - return status(request, readPlan(request, operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), false))); - }); - } - - private void patchFinalizer(Request request, ResidentWorkspace selected, RuntimeBindingRecord original, - Map pod, String action, KubernetesHttpRuntimeClient api) { - var metadata = map(pod.get("metadata")); - String resourceVersion = (String) metadata.get("resourceVersion"); - require(resourceVersion != null && resourceVersion.matches("[\\x21-\\x7e]{1,256}")); - String finalizer = finalizer(request, original); - var before = metadata.get("finalizers"); - require("ADD".equals(action) ? empty(before) : List.of(finalizer).equals(before)); - var patch = new java.util.ArrayList>(); - patch.add(Map.of("op", "test", "path", "/metadata/uid", "value", metadata.get("uid"))); - patch.add(Map.of("op", "test", "path", "/metadata/resourceVersion", "value", resourceVersion)); - if (before != null) { - patch.add(Map.of("op", "test", "path", "/metadata/finalizers", "value", before)); - } - patch.add(Map.of("op", "add", "path", "/metadata/finalizers", "value", "ADD".equals(action) ? List.of(finalizer) : List.of())); - byte[] body = WorkspaceCsiRuntimeIdentity.bytes(patch); - var attempt = transaction.execute(status -> { - lockedPlan(request, selected, true); - idle(request); - var saved = operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), true); - require(baseline(saved).finalizer().equals(finalizer) - && ("ADD".equals(action) ? "DRAINING".equals(saved.get("state")) : "PLATFORM_CLEANED".equals(saved.get("state")))); - if ("REMOVE".equals(action)) { - receipt(saved, original); - } - var previous = finalizerAttempts(request); - require(previous.size() < 16 && previous.stream().filter(value -> action.equals(value.get("action"))).count() < 8); - String owner = UUID.randomUUID().toString(); - require(jdbc.update("UPDATE ma_operation SET claim_owner = ?, claim_generation = claim_generation + 1," - + " claim_until = " + ManagedSchemaTime.SQL_NOW + " + 60000 WHERE operation_key = ?" - + " AND (claim_until IS NULL OR claim_until <= " + ManagedSchemaTime.SQL_NOW + ")", owner, operationKey(request)) == 1); - long generation = jdbc.queryForObject("SELECT claim_generation FROM ma_operation WHERE operation_key = ?", Long.class, operationKey(request)); - String workKey = WorkspaceOperations.hash(action, (String) metadata.get("uid"), resourceVersion, Long.toString(generation)); - operations(request).insertWork(WORKSPACE_RETIREMENT, request.retirementId(), FINALIZER_WORK, workKey, workKey, null, - WorkspaceOperations.values("ordinal", previous.size() + 1, "action", action, "resourceVersion", resourceVersion, - "requestBase64", Base64.getEncoder().encodeToString(body), "claimGeneration", generation), "UNKNOWN"); - return new Attempt(workKey, owner, generation, body); - }); - KubernetesHttpRuntimeClient.DeleteResponse response = null; - try { - response = api.patchPodFinalizers(selected.registration().namespace(), - (String) map(original.getResourceHandle().getValue().get("placement")).get("podName"), body).toCompletableFuture().join(); - } catch (java.util.concurrent.CompletionException failure) { - // Independent platform readback can recover the effect; this attempt stays UNKNOWN. - } - var reply = response; - transaction.executeWithoutResult(status -> { - lockedPlan(request, selected, false); - require(jdbc.queryForObject("SELECT COUNT(*) FROM ma_operation WHERE operation_key = ? AND claim_owner = ?" - + " AND claim_generation = ? AND claim_until > " + ManagedSchemaTime.SQL_NOW, Long.class, - operationKey(request), attempt.owner(), attempt.generation()) == 1); - if (reply != null) { - require(reply.body().length <= 64 * 1024); - operations(request).updateWork(WORKSPACE_RETIREMENT, request.retirementId(), FINALIZER_WORK, attempt.workKey(), - WorkspaceOperations.values("state", reply.complete() ? "RETURNED" : "UNKNOWN", "httpStatus", reply.status(), - "responseSha256", org.apache.commons.codec.digest.DigestUtils.sha256Hex(reply.body()), "complete", reply.complete())); - } - jdbc.update("UPDATE ma_operation SET claim_owner = NULL, claim_until = NULL WHERE operation_key = ?" - + " AND claim_owner = ? AND claim_generation = ?", operationKey(request), attempt.owner(), attempt.generation()); - }); - } - - private ResidentWorkspaceReclamation.Baseline baseline(Map saved) { + public ResidentReclamation.Baseline baseline(Map saved) { require(saved.get("reclamationBaseline") != null); - var baseline = json.convertValue(saved.get("reclamationBaseline"), ResidentWorkspaceReclamation.Baseline.class); + var baseline = json.convertValue(saved.get("reclamationBaseline"), ResidentReclamation.Baseline.class); require(baseline != null && digest(baseline).equals(saved.get("reclamationBaselineDigest"))); return baseline; } - private ResidentWorkspaceReclamation.Receipt receipt(Map saved, RuntimeBindingRecord original) { + public ResidentReclamation.Receipt receipt(Map saved, RuntimeBindingRecord original) { require(saved.get("cleanupReceipt") != null); - var receipt = json.convertValue(saved.get("cleanupReceipt"), ResidentWorkspaceReclamation.Receipt.class); + var receipt = json.convertValue(saved.get("cleanupReceipt"), ResidentReclamation.Receipt.class); require(receipt != null && digest(receipt).equals(saved.get("cleanupReceiptDigest"))); - ResidentWorkspaceReclamation.verifyReceipt(original, baseline(saved), receipt); + ResidentReclamation.verifyReceipt(original, baseline(saved), receipt); return receipt; } - private static String finalizer(Request request, RuntimeBindingRecord original) { - return "qwen.ai/retirement-" + WorkspaceOperations.hash(request.tenantId(), request.retirementId(), original.getBindingId()).substring(0, 40); - } - - private static void verifyProtection(ResidentWorkspace selected, KubernetesHttpRuntimeClient api) { - new WorkspaceCsiResourceGuard(api, selected.registration().clusterDomain(), selected.registration(), - selected.protection()).verify().toCompletableFuture().join(); - } - private ResidentWorkspace authorize(Request request, boolean lock) { return authorize(request, selected(request), lock); } @@ -483,11 +185,11 @@ public final class ResidentWorkspaceRetirementStore { require(rows.size() == 1 && List.of("ACTIVE", "DRAINING").contains(rows.getFirst())); require(jdbc.queryForList("SELECT role FROM ma_workspace_access WHERE tenant_id = ? AND workspace_id = ?" + " AND actor_id = ?" + (lock ? " FOR UPDATE" : ""), String.class, request.tenantId(), request.workspaceId(), - ManagedWorkspaceRegistry.actorKey(request.tenantId(), request.actorId())).equals(List.of("OWNER"))); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(request.tenantId(), request.actorId())).equals(List.of("OWNER"))); return selected; } - private ResidentWorkspace selected(Request request) { + public ResidentWorkspace selected(Request request) { return mappings.stream().filter(value -> value.registration().tenantId().equals(request.tenantId()) && value.workspaceId().equals(request.workspaceId()) && value.workspaceGeneration() == request.workspaceGeneration()) .findFirst().orElseThrow(ResidentWorkspaceRetirementStore::unavailable); @@ -604,27 +306,6 @@ public final class ResidentWorkspaceRetirementStore { require(plan.mapping() == null || original.getRequest().equals(runtimeRequest(plan.mapping()))); } - private static void verifyPod(RuntimeBindingRecord original, Map pod, String resourceVersion, - List finalizers) { - require(pod != null && "v1".equals(pod.get("apiVersion")) && "Pod".equals(pod.get("kind"))); - var placement = map(original.getResourceHandle().getValue().get("placement")); - var metadata = map(pod.get("metadata")); - require(placement.get("podName").equals(metadata.get("name")) && placement.get("namespace").equals(metadata.get("namespace")) - && placement.get("podUid").equals(metadata.get("uid")) && resourceVersion.equals(metadata.get("resourceVersion")) - && metadata.get("deletionTimestamp") == null && metadata.get("deletionGracePeriodSeconds") == null - && empty(metadata.get("ownerReferences")) - && (finalizers.isEmpty() ? empty(metadata.get("finalizers")) : finalizers.equals(metadata.get("finalizers"))) - && digest(pod.get("spec")).equals(original.getResourceHandle().getValue().get("podSpecDigest"))); - var status = map(pod.get("status")); - require("Running".equals(status.get("phase")) && placement.get("podIp").equals(status.get("podIP")) - && status.get("containerStatuses") instanceof List containers && containers.size() == 1); - var container = map(((List) status.get("containerStatuses")).getFirst()); - require(placement.get("containerName").equals(container.get("name")) - && placement.get("containerId").equals(container.get("containerID")) && placement.get("imageId").equals(container.get("imageID")) - && Long.valueOf(0).equals(BrokerValues.exactLong(container.get("restartCount"))) - && Boolean.TRUE.equals(container.get("ready")) && map(container.get("state")).containsKey("running")); - } - private List> attempts(Request request) { return jdbc.query("SELECT * FROM ma_operation_work WHERE operation_key = ? AND work_kind = ? ORDER BY target_key LIMIT 9", (row, index) -> WorkspaceOperations.work(row), operationKey(request), WORK); @@ -648,7 +329,7 @@ public final class ResidentWorkspaceRetirementStore { (String) value.get("action"), (String) value.get("resourceVersion"), (String) value.get("state"), value.get("httpStatus") instanceof Number code ? code.intValue() : null, (String) value.get("responseSha256"))) .sorted(java.util.Comparator.comparingInt(FinalizerAttemptStatus::ordinal)).toList(), - saved.get("cleanupReceiptDigest") instanceof String sha ? new Completion(sha, ResidentWorkspaceReclamation.DATA_POLICY, + saved.get("cleanupReceiptDigest") instanceof String sha ? new Completion(sha, ResidentReclamation.DATA_POLICY, saved.get("releasedRevision") instanceof Number revision ? revision.longValue() : null) : null); } @@ -673,4 +354,183 @@ public final class ResidentWorkspaceRetirementStore { private static RuntimeBrokerException unavailable() { return new RuntimeBrokerException(409, "resident_retirement_unavailable", "Original idle Workspace maintenance is unavailable.", false); } + + public void completeDeletion(Request request, ResidentWorkspace selected, Attempt attempt, KubernetesMaintenanceClient.DeleteResponse response) { + transaction.executeWithoutResult(status -> { + lockedPlan(request, selected, false); + require(jdbc.queryForObject("SELECT COUNT(*) FROM ma_operation WHERE operation_key = ? AND claim_owner = ?" + + " AND claim_generation = ? AND claim_until > " + ManagedSchemaTime.SQL_NOW, + Long.class, operationKey(request), attempt.owner(), attempt.generation()) == 1); + if (response != null) { + require(response.body().length <= 64 * 1024); + operations(request).updateWork(WORKSPACE_RETIREMENT, request.retirementId(), WORK, attempt.workKey(), + WorkspaceOperations.values("state", response.complete() ? "RETURNED" : "UNKNOWN", "httpStatus", response.status(), + "responseBase64", Base64.getEncoder().encodeToString(response.body()), + "responseSha256", org.apache.commons.codec.digest.DigestUtils.sha256Hex(response.body()), + "complete", response.complete())); + } + jdbc.update("UPDATE ma_operation SET claim_owner = NULL, claim_until = NULL WHERE operation_key = ?" + + " AND claim_owner = ? AND claim_generation = ?", operationKey(request), attempt.owner(), attempt.generation()); + }); + } + + public Attempt beginDeletion(Request request, ResidentWorkspace selected, String workKey, String resourceVersion, byte[] body) { + return transaction.execute(status -> { + lockedPlan(request, selected, true); + idle(request); + var operations = operations(request); + if (operations.findWork(WORKSPACE_RETIREMENT, request.retirementId(), WORK, workKey, true) != null) { + return null; + } + int count = attempts(request).size(); + require(count < 8); + String owner = UUID.randomUUID().toString(); + require(jdbc.update("UPDATE ma_operation SET claim_owner = ?, claim_generation = claim_generation + 1," + + " claim_until = " + ManagedSchemaTime.SQL_NOW + " + 60000 WHERE operation_key = ?" + + " AND (claim_until IS NULL OR claim_until <= " + ManagedSchemaTime.SQL_NOW + ")", + owner, operationKey(request)) == 1); + long generation = jdbc.queryForObject("SELECT claim_generation FROM ma_operation WHERE operation_key = ?", + Long.class, operationKey(request)); + operations.insertWork(WORKSPACE_RETIREMENT, request.retirementId(), WORK, workKey, workKey, null, + WorkspaceOperations.values("ordinal", count + 1, "resourceVersion", resourceVersion, + "requestBase64", Base64.getEncoder().encodeToString(body), "claimGeneration", generation), "UNKNOWN"); + return new Attempt(workKey, owner, generation, body); + }); + } + + public void recordBaseline(Request request, ResidentWorkspace selected, ResidentReclamation.Baseline observed, String finalizer) { + transaction.executeWithoutResult(status -> { + lockedPlan(request, selected, true); + idle(request); + var current = operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), true); + require("DRAINING".equals(current.get("state"))); + if (current.get("reclamationBaseline") == null) { + operations(request).update(WORKSPACE_RETIREMENT, request.retirementId(), WorkspaceOperations.values( + "reclamationBaseline", json.valueToTree(observed), "reclamationBaselineDigest", digest(observed))); + } else { + var first = baseline(current); + require(first.finalizer().equals(finalizer) && first.bootId().equals(observed.bootId()) + && first.leaseUid().equals(observed.leaseUid()) && first.attachmentUid().equals(observed.attachmentUid())); + } + }); + } + + public Status release(Request request, ResidentWorkspace selected, ResidentReclamation.Receipt proof) { + return transaction.execute(status -> { + var current = operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), false); + if ("RELEASED".equals(current.get("state"))) { + return inspect(request); + } + var locked = lockedPlan(request, selected, true); + idle(request); + current = operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), true); + require("PLATFORM_CLEANED".equals(current.get("state")) && digest(proof).equals(current.get("cleanupReceiptDigest"))); + receipt(current, locked); + require(jdbc.queryForObject("SELECT COUNT(*) FROM ma_operation WHERE operation_key = ?" + + " AND (claim_until IS NULL OR claim_until <= " + ManagedSchemaTime.SQL_NOW + ")", Long.class, operationKey(request)) == 1); + var storage = map(locked.getResourceHandle().getValue().get("storage")); + long revision = Math.addExact(Long.parseLong((String) storage.get("reservationRevision")), 1); + jdbc.execute((ConnectionCallback) connection -> { + bindings.releaseResidentForRetirement(connection, locked); + return null; + }); + require(jdbc.update("UPDATE ma_storage_owner SET csi_phase = 'RELEASED', csi_revision = ?, tenant_id = ?," + + " binding_id = NULL, runtime_generation = NULL, csi_reservation_id = NULL, csi_registration_key = NULL," + + " csi_registration_revision = NULL, csi_provision_request_id = NULL" + + " WHERE storage_key = ? AND storage_kind = 'CSI' AND csi_phase = 'RESERVED' AND csi_revision = ?" + + " AND binding_id = ? AND runtime_generation = ? AND csi_reservation_id = ?" + + " AND (tenant_id IS NULL OR tenant_id = ?)", revision, request.tenantId(), selected.registration().physicalKey(), + revision - 1, locked.getBindingId(), locked.getGeneration(), storage.get("reservationId"), request.tenantId()) == 1); + operations(request).update(WORKSPACE_RETIREMENT, request.retirementId(), WorkspaceOperations.values( + "state", "RELEASED", "releasedRevision", revision)); + require(jdbc.update("UPDATE ma_operation SET claim_owner = NULL, claim_until = NULL, claim_generation = claim_generation + 1" + + " WHERE operation_key = ?", operationKey(request)) == 1); + return status(request, readPlan(request, operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), false))); + }); + } + + public void recordCleanup(Request request, ResidentWorkspace selected, ResidentReclamation.Baseline baseline, ResidentReclamation.Receipt observed, RuntimeBindingRecord original) { + transaction.executeWithoutResult( + status -> { + lockedPlan(request, selected, true); + idle(request); + var current = + operations(request) + .find(WORKSPACE_RETIREMENT, request.retirementId(), true); + if ("DRAINING".equals(current.get("state"))) { + require(digest(baseline).equals(current.get("reclamationBaselineDigest"))); + operations(request) + .update( + WORKSPACE_RETIREMENT, + request.retirementId(), + WorkspaceOperations.values( + "state", + "PLATFORM_CLEANED", + "cleanupReceipt", + json.valueToTree(observed), + "cleanupReceiptDigest", + digest(observed))); + } else { + receipt(current, original); + } + }); + } + + public void completeFinalizer(Request request, ResidentWorkspace selected, Attempt attempt, KubernetesMaintenanceClient.DeleteResponse reply) { + transaction.executeWithoutResult(status -> { + lockedPlan(request, selected, false); + require(jdbc.queryForObject("SELECT COUNT(*) FROM ma_operation WHERE operation_key = ? AND claim_owner = ?" + + " AND claim_generation = ? AND claim_until > " + ManagedSchemaTime.SQL_NOW, Long.class, + operationKey(request), attempt.owner(), attempt.generation()) == 1); + if (reply != null) { + require(reply.body().length <= 64 * 1024); + operations(request).updateWork(WORKSPACE_RETIREMENT, request.retirementId(), FINALIZER_WORK, attempt.workKey(), + WorkspaceOperations.values("state", reply.complete() ? "RETURNED" : "UNKNOWN", "httpStatus", reply.status(), + "responseSha256", org.apache.commons.codec.digest.DigestUtils.sha256Hex(reply.body()), "complete", reply.complete())); + } + jdbc.update("UPDATE ma_operation SET claim_owner = NULL, claim_until = NULL WHERE operation_key = ?" + + " AND claim_owner = ? AND claim_generation = ?", operationKey(request), attempt.owner(), attempt.generation()); + }); + } + + public Attempt beginFinalizer(Request request, ResidentWorkspace selected, RuntimeBindingRecord original, String finalizer, String action, Map metadata, String resourceVersion, byte[] body) { + return transaction.execute(status -> { + lockedPlan(request, selected, true); + idle(request); + var saved = operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), true); + require(baseline(saved).finalizer().equals(finalizer) + && ("ADD".equals(action) ? "DRAINING".equals(saved.get("state")) : "PLATFORM_CLEANED".equals(saved.get("state")))); + if ("REMOVE".equals(action)) { + receipt(saved, original); + } + var previous = finalizerAttempts(request); + require(previous.size() < 16 && previous.stream().filter(value -> action.equals(value.get("action"))).count() < 8); + String owner = UUID.randomUUID().toString(); + require(jdbc.update("UPDATE ma_operation SET claim_owner = ?, claim_generation = claim_generation + 1," + + " claim_until = " + ManagedSchemaTime.SQL_NOW + " + 60000 WHERE operation_key = ?" + + " AND (claim_until IS NULL OR claim_until <= " + ManagedSchemaTime.SQL_NOW + ")", owner, operationKey(request)) == 1); + long generation = jdbc.queryForObject("SELECT claim_generation FROM ma_operation WHERE operation_key = ?", Long.class, operationKey(request)); + String workKey = WorkspaceOperations.hash(action, (String) metadata.get("uid"), resourceVersion, Long.toString(generation)); + operations(request).insertWork(WORKSPACE_RETIREMENT, request.retirementId(), FINALIZER_WORK, workKey, workKey, null, + WorkspaceOperations.values("ordinal", previous.size() + 1, "action", action, "resourceVersion", resourceVersion, + "requestBase64", Base64.getEncoder().encodeToString(body), "claimGeneration", generation), "UNKNOWN"); + return new Attempt(workKey, owner, generation, body); + }); + } + + public RuntimeBindingRecord original(Request request, ResidentWorkspace selected) { + return transaction.execute(status -> lockedPlan(request, selected, true)); + } + + public Map loadState(Request request) { + return operations(request).find(WORKSPACE_RETIREMENT, request.retirementId(), false); + } + + public boolean hasDeletionAttempt(Request request, String workKey) { + return operations(request).findWork(WORKSPACE_RETIREMENT, request.retirementId(), WORK, workKey, false) != null; + } + + public boolean hasDeletionAttempts(Request request) { return !attempts(request).isEmpty(); } + + public int planVersion(Request request, Map state) { return readPlan(request, state).version(); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaces.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaces.java similarity index 72% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaces.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaces.java index 8756d143ec4..7f3d4c4a5c7 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaces.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaces.java @@ -1,76 +1,22 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.ResidentWorkspace; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker.ResidentWorkspace; import com.alibaba.qwen.code.runtimebroker.ResidentWorkspaceProfile; -import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; -import com.alibaba.qwen.code.runtimebroker.RuntimeScope; -import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import org.springframework.jdbc.core.JdbcTemplate; -import java.util.HashSet; import java.util.List; /** Explicit administrator selection; a CSI registration alone never enables public execution. */ -public final class ResidentWorkspaces { - private final List configured; - - public ResidentWorkspaces(List configured) { - this.configured = List.copyOf(configured); - var workspaces = new HashSet>(); - var aliases = new HashSet(); - var physical = new HashSet(); - for (var entry : configured) { - if (!workspaces.add(List.of(entry.registration().tenantId(), entry.workspaceId())) - || !aliases.add(entry.registration().aliasKey()) || !physical.add(entry.registration().physicalKey())) { - throw new IllegalArgumentException("Resident Workspace ownership is duplicated"); - } - } - } - - public ResidentWorkspace find(ContextBinding binding) { - if (binding == null) { - return null; - } - return configured.stream().filter(value -> value.registration().tenantId().equals(binding.getTenantId()) - && value.workspaceId().equals(binding.getWorkspaceId()) - && value.workspaceGeneration() == binding.getWorkspaceGeneration() - && value.registration().storageId().equals(binding.getStorageId()) - && WorkspaceExecutionProfile.CONTEXT_CONFIG_REF.equals(binding.getContextConfigRef())) - .findFirst().orElse(null); - } - - public ResidentWorkspace require(RuntimeScope scope) { - if (!ResidentWorkspaceProfile.selects(scope)) { - throw WorkspaceExecutionStore.unavailable(); - } - return configured.stream().filter(value -> value.registration().tenantId().equals(scope.getTenantId()) - && value.workspaceId().equals(scope.getWorkspaceId()) - && Long.toString(value.workspaceGeneration()).equals(scope.getWorkspaceGeneration()) - && value.registration().mountRoot().equals(scope.getCanonicalCwd())) - .findFirst().orElseThrow(WorkspaceExecutionStore::unavailable); - } - - public RuntimeProvisionRequest request(ContextBinding binding) { - var selected = find(binding); - if (selected == null) { - throw WorkspaceExecutionStore.unavailable(); - } - return new RuntimeProvisionRequest(scope(selected), null, WorkspaceCsiReservationStore.PROVISIONER_KIND, - selected.registration().storageId()); - } - - public static RuntimeScope scope(ResidentWorkspace selected) { - return new RuntimeScope(selected.registration().tenantId(), selected.workspaceId(), - Long.toString(selected.workspaceGeneration()), selected.registration().mountRoot(), - ResidentWorkspaceProfile.CAPABILITY_DIGEST, "workspace"); - } +public final class ResidentWorkspaces extends com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentWorkspaceCatalog { + public ResidentWorkspaces(List configured) { super(configured); } public void requireCreation(JdbcTemplate jdbc, ContextBinding binding, String profile) { var selected = find(binding); if (selected == null || !WorkspaceToolProfiles.FILES.equals(profile)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } requireRegistration(jdbc, selected); // The caller already owns ma_workspace FOR UPDATE. Use locking reads under RR. @@ -98,10 +44,10 @@ public final class ResidentWorkspaces { .enable(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) .enable(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_TRAILING_TOKENS) .readValue(rows.getFirst(), WorkspaceCsiRegistration.class))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } catch (com.fasterxml.jackson.core.JsonProcessingException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -130,7 +76,7 @@ public final class ResidentWorkspaces { if (jdbc.queryForObject("SELECT COUNT(*) FROM ma_session WHERE tenant_id = ? AND session_id = ?" + " AND session_kind = 'PUBLIC' AND parent_session_id IS NULL AND tool_profile = ?" + " AND status IN ('CLOSING', 'DELETING')", Long.class, tenantId, sessionId, WorkspaceToolProfiles.FILES) != 1) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } requireRegistration(jdbc, selected); var originals = jdbc.query("SELECT b.binding_id, b.runtime_generation, b.resource_handle_json FROM ma_runtime_binding b" @@ -149,12 +95,12 @@ public final class ResidentWorkspaces { if (originals.isEmpty()) { if (!owners.isEmpty() || !jdbc.queryForList("SELECT binding_id FROM ma_runtime_binding WHERE request_key = ? FOR UPDATE", String.class, request(binding).requestKey()).isEmpty()) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return true; } if (originals.size() != 1) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } var original = originals.getFirst(); var storage = heldStorage(original.handle()); @@ -179,7 +125,7 @@ public final class ResidentWorkspaces { || !jdbc.queryForList("SELECT execution_call_id FROM ma_tool_execution WHERE binding_id = ?" + " AND runtime_generation = ? AND execution_state <> 'SETTLED' FOR UPDATE", String.class, original.id(), original.generation()).isEmpty()) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return true; } @@ -193,11 +139,11 @@ public final class ResidentWorkspaces { || !com.alibaba.qwen.code.runtimebroker.CsiOwnershipRevision.residentReserved(Long.parseLong(revision)) || !(storage.get("reservationId") instanceof String reservation) || !java.util.UUID.fromString(reservation).toString().equals(reservation)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return storage; } catch (IllegalArgumentException | ClassCastException failure) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionMessageRelayStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionMessageRelayStore.java similarity index 88% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionMessageRelayStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionMessageRelayStore.java index 047ffbfcf24..7a0f8d864d8 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionMessageRelayStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionMessageRelayStore.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.JdbcTemplate; @@ -10,17 +10,16 @@ import org.springframework.stereotype.Repository; import java.util.List; /** - * H4d-b: the JDBC side of the session message relay — its V64 ledger plus - * the reads its worker needs beyond the child result relay's: outbox - * entries over the delivery-pending index, one record's committed delivery - * state in either journal, and a child Session's recorded lineage. The - * ledger's classifications (`orphaned`, `unknown`) are terminal: they never - * represent delivery and never authorize a second one. + * H4d-b: the JDBC side of the session message relay — its V64 ledger plus the reads its worker + * needs beyond the child result relay's: outbox entries over the delivery-pending index, one + * record's committed delivery state in either journal, and a child Session's recorded lineage. The + * ledger's classifications (`orphaned`, `unknown`) are terminal: they never represent delivery and + * never authorize a second one. */ @Repository -public class SessionMessageRelayStore { +public class SessionMessageRelayStore + implements com.alibaba.qwen.code.managedagent.repository.SessionMessageRelayStore { /** The failed steps after which the relay gives an entry up. */ - public static final int MAX_ATTEMPTS = 64; /** Over a ledger row `l` joined to its entry: the row still owes work — * none opened yet, or one neither classified nor past the attempt @@ -29,38 +28,16 @@ public class SessionMessageRelayStore { * stops; the relay then owes it nothing but that give-up. The child * relay's hold on a child's own outbox and the send-order hold on a * younger message both end here. */ - public static final String LEDGER_OWES_WORK = "(l.delivery_id IS NULL" - + " OR (l.transport_state NOT IN ('done', 'orphaned', 'unknown')" - + " AND l.attempt_count < " + MAX_ATTEMPTS + "))"; /** One outbox entry the delivery-pending index surfaced. H4e-b3 adds * the domain: command entries share every field and never collide * with message ids, which would duplicate across the ledger. */ - public record PendingMessage(String tenantId, String senderSessionId, - String messageId, String domain, String deliveryState, - String recordResourceId) { - } /** One relay ledger row. */ - public record MessageRow(String tenantId, String senderSessionId, - String messageId, String targetSessionId, String state, - String claimedBy, long claimedUntil, int attempts, - long nextRetryAt, String lastError, long claimGeneration) { - public MessageRow(String tenantId, String senderSessionId, String messageId, String targetSessionId, - String state, String claimedBy, long claimedUntil, int attempts, long nextRetryAt, String lastError) { - this(tenantId, senderSessionId, messageId, targetSessionId, state, claimedBy, claimedUntil, - attempts, nextRetryAt, lastError, 0); - } - } /** A child Session's recorded lineage edge. */ - public record Lineage(String parentSessionId, String parentChildRunId) { - } /** One outbox entry as committed now: its delivery and its body. */ - public record CurrentRecord(String deliveryState, - String recordResourceId) { - } /** The domains this ledger drives: H4d-b's channel and H4e-b3's. */ private static final List DOMAINS = List.of("session_message", @@ -156,7 +133,7 @@ public class SessionMessageRelayStore { (result, row) -> new CurrentRecord( result.getString("delivery_state"), result.getString("record_resource_id")), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, domain, messageId); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, domain, messageId); return rows.isEmpty() ? null : rows.getFirst(); } @@ -168,7 +145,7 @@ public class SessionMessageRelayStore { + " WHERE session_scope_key = ? AND tenant_id = ? AND session_id = ?" + " AND domain = ? AND record_id = ?", (result, row) -> result.getString("delivery_state"), - ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, domain, messageId); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, sessionId), tenantId, sessionId, domain, messageId); return rows.isEmpty() ? null : rows.getFirst(); } @@ -186,7 +163,7 @@ public class SessionMessageRelayStore { } private static String deliveryKey(String tenant, String sender, String message) { - return ManagedSchemaKeys.key("delivery", "SESSION_MESSAGE", ManagedSessionStore.sessionScopeKey(tenant, sender), message); + return ManagedSchemaKeys.key("delivery", "SESSION_MESSAGE", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sender), message); } /** Whether an older command of the same sender is still unclassified: @@ -217,7 +194,7 @@ public class SessionMessageRelayStore { + " AND (l.transport_state IS NULL" + " OR l.transport_state NOT IN ('done', 'unknown', 'orphaned'))" + " LIMIT 1", - (result, row) -> result.getInt(1), commandId, ManagedSessionStore.sessionScopeKey(tenantId, senderSessionId), tenantId, + (result, row) -> result.getInt(1), commandId, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, senderSessionId), tenantId, senderSessionId).isEmpty(); } @@ -245,14 +222,14 @@ public class SessionMessageRelayStore { + " AND r.delivery_state IN ('planned', 'accepting', 'unknown')" + " AND " + LEDGER_OWES_WORK, (result, row) -> result.getString("record_resource_id"), messageId, - ManagedSessionStore.sessionScopeKey(tenantId, senderSessionId), tenantId, senderSessionId); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, senderSessionId), tenantId, senderSessionId); } /** Insert-or-claim: returns the row this worker now owns, or null when * another live claim holds it. */ public MessageRow claim(String tenantId, String senderSessionId, String messageId, String owner, long leaseUntil, long now) { - String scope = ManagedSessionStore.sessionScopeKey(tenantId, senderSessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenantId, senderSessionId); String key = deliveryKey(tenantId, senderSessionId, messageId); jdbc.update("INSERT IGNORE INTO ma_delivery (delivery_key, delivery_kind, tenant_id, source_scope_key," + " source_session_id, delivery_id, payload_json, transport_state, reception_state," diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollector.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollector.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollector.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollector.java index 154eeee01d1..52daafe8116 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollector.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollector.java @@ -1,11 +1,11 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; @@ -42,18 +42,18 @@ public final class SessionResourceCollectionCollector { private final JdbcTemplate jdbc; private final TransactionTemplate transactions; private final ManagedProjectionResourceCollector projections; - private final ManagedAgentProperties properties; + private final ManagedAgentSettings properties; private final LongSupplier clock; private final String owner = UUID.randomUUID().toString(); private long lastLedgerScanNanos; public SessionResourceCollectionCollector(JdbcTemplate jdbc, PlatformTransactionManager manager, - ManagedAgentProperties properties) { + ManagedAgentSettings properties) { this(jdbc, manager, properties, System::nanoTime); } SessionResourceCollectionCollector(JdbcTemplate jdbc, PlatformTransactionManager manager, - ManagedAgentProperties properties, LongSupplier clock) { + ManagedAgentSettings properties, LongSupplier clock) { Duration grace = properties.getToolPublication().getDeletionGrace(); if (grace == null || grace.isNegative()) { throw new IllegalStateException("Tool output deletion grace must be nonnegative"); @@ -155,7 +155,7 @@ public final class SessionResourceCollectionCollector { (String) row.get("session_id")); String tenant = (String) row.get("tenant_id"); String session = (String) row.get("session_id"); - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String request = JSON.createObjectNode() .put("version", 1) @@ -323,7 +323,7 @@ public final class SessionResourceCollectionCollector { if (!jdbc.query( "SELECT operation_key FROM ma_operation WHERE session_scope_key = ? AND" + " operation_kind = 'OBJECT_PUT' AND state <> 'RETURNED' LIMIT 1", - (row, index) -> row.getString(1), ManagedSessionStore.sessionScopeKey(tenant, session)).isEmpty()) { + (row, index) -> row.getString(1), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session)).isEmpty()) { return "put_unresolved"; } // The relay's classifications are all terminal (its discovery page and claim exclude @@ -333,7 +333,7 @@ public final class SessionResourceCollectionCollector { "SELECT operation_key FROM ma_operation WHERE session_scope_key = ? AND" + " operation_kind = 'CHILD_SESSION_START' AND state NOT IN ('done'," + " 'orphaned', 'unknown') LIMIT 1", - (row, index) -> row.getString(1), ManagedSessionStore.sessionScopeKey(tenant, session)).isEmpty()) { + (row, index) -> row.getString(1), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session)).isEmpty()) { return "child_start_unresolved"; } // A recovery capture or verification reads a retired Session's bytes through diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStore.java similarity index 77% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStore.java index c608f5293d7..c84264993b0 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStore.java @@ -1,13 +1,12 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry.ResolvedBinding; -import com.alibaba.qwen.code.managedagent.workspace.SessionWorktreeGit; +import com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.ResolvedBinding; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Repository; @@ -18,13 +17,16 @@ import java.nio.file.Path; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.UUID; @Repository -public class SessionWorktreeStore { - public static final String KIND = "SESSION_WORKTREE"; +public class SessionWorktreeStore + implements com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore { + private com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles worktreeFiles; + @org.springframework.beans.factory.annotation.Autowired + public void configureWorktreeFiles(com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles value) { worktreeFiles = value; } + private static final ObjectMapper JSON = new ObjectMapper(); private static final String SELECT = "SELECT w.*, o.claim_owner, o.claim_generation, o.claim_until, o.phase, o.attempt_count" + " FROM ma_session_worktree w JOIN ma_operation o ON o.operation_kind = 'SESSION_WORKTREE'" @@ -49,56 +51,7 @@ public class SessionWorktreeStore { this.guard = guard; } - public record Plan(String tenantId, String workspaceId, long workspaceGeneration, String storageId, - String sourceCwdRelative, String contextConfigRef, long contextRevision, String root, String rootIdentity, - String repositoryRelative, String sourceOffset, String commonDirectory, String commonIdentity, - String baseRef, String baseCommit, String worktreeId, String worktreeRoot, String cwdRelative, String branch, - String storageIdentity) { - public Plan(String tenantId, String workspaceId, long workspaceGeneration, String storageId, - String sourceCwdRelative, String contextConfigRef, long contextRevision, String root, String rootIdentity, - String repositoryRelative, String sourceOffset, String commonDirectory, String commonIdentity, - String baseRef, String baseCommit, String worktreeId, String worktreeRoot, String cwdRelative, String branch) { - this(tenantId, workspaceId, workspaceGeneration, storageId, sourceCwdRelative, contextConfigRef, contextRevision, - root, rootIdentity, repositoryRelative, sourceOffset, commonDirectory, commonIdentity, baseRef, - baseCommit, worktreeId, worktreeRoot, cwdRelative, branch, null); - } - public ContextBinding source() { - return new ContextBinding(tenantId, workspaceId, workspaceGeneration, storageId, sourceCwdRelative, - contextConfigRef, contextRevision); - } - - public ResolvedBinding target(ResolvedBinding source) { - if (!source().equals(source.binding())) { - throw error("worktree_source_changed"); - } - return new ResolvedBinding(new ContextBinding(tenantId, workspaceId, workspaceGeneration, storageId, - cwdRelative, source.binding().getContextConfigRef(), 1), source.configRef(), source.policyRef()); - } - } - - public record Descriptor(String id, String sessionId, String workspaceId, String state, String branch, - String baseCommit, String cwdRelative, String operationId, long revision, String errorCode) { - public String worktreeId() { - return id; - } - } - - public record Row(String tenantId, String sessionId, String workspaceId, String worktreeId, String state, - Plan plan, String identityJson, String operationId, String claimedBy, long generation, - Long claimUntil, String phase, int attempts, long revision, String errorCode, String actor) { - public Descriptor descriptor() { - return descriptor(plan.branch()); - } - - public Descriptor descriptor(String branch) { - return new Descriptor(worktreeId, sessionId, workspaceId, state.toLowerCase(Locale.ROOT), branch, - plan.baseCommit(), plan.cwdRelative(), operationId, revision, errorCode); - } - } - - public record SourceLocation(ResolvedBinding resolved, Path root, String storageIdentity) { } - - public SourceLocation source(String tenant, String actor, com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection selection) { + public SourceLocation source(String tenant, String actor, com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection selection) { return transaction.execute(status -> { WorkspaceMigrationAdmission.lockTenant(jdbc, tenant); ResolvedBinding source = registry.resolveForCreation(tenant, actor, selection); @@ -110,10 +63,10 @@ public class SessionWorktreeStore { public void authorizeOptions(String tenant, String actor, String workspace) { var access = registry.accessOf(tenant, actor, workspace); if (!access.canRead()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "workspace_not_found", "Workspace not found."); + throw new ManagedAgentException(404, "workspace_not_found", "Workspace not found."); } if (!access.canCreate()) { - throw new ManagedAgentException(HttpStatus.FORBIDDEN, "workspace_forbidden", "Workspace cannot be used."); + throw new ManagedAgentException(403, "workspace_forbidden", "Workspace cannot be used."); } } @@ -123,17 +76,17 @@ public class SessionWorktreeStore { } public Descriptor describe(String tenant, String session) { - return descriptor(jdbc, tenant, session); + return descriptor(worktreeFiles, jdbc, tenant, session); } public void admit(String tenant, String actor, String session, Plan plan, ResolvedBinding source) { plan.target(source); if (!tenant.equals(plan.tenantId()) || !guard.verifiedRoot(source.binding()).toString().equals(plan.root())) { - throw error("worktree_source_changed"); + throw com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.error("worktree_source_changed"); } - SessionWorktreeGit.requireRoot(plan); + worktreeFiles.requireRoot(plan); if (plan.storageIdentity() != null && !plan.storageIdentity().equals(storageIdentity(source.binding()))) { - throw error("worktree_source_changed"); + throw com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.error("worktree_source_changed"); } long now = System.currentTimeMillis(); String physical = WorkspaceExecutionStore.storageKey(jdbc, plan.source()); @@ -141,7 +94,7 @@ public class SessionWorktreeStore { + " worktree_id, owner_actor_key, state, revision, plan_json, operation_id, gmt_create, gmt_modified)" + " VALUES (?, ?, ?, ?, ?, ?, ?, 'PREPARING', 1, ?, ?, ?, ?)", tenant, session, plan.workspaceId(), plan.storageId(), physical, plan.worktreeId(), - ManagedWorkspaceRegistry.actorKey(tenant, actor), json(plan), plan.worktreeId(), now, now); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenant, actor), json(plan), plan.worktreeId(), now, now); createOperation(tenant, session, plan, plan.worktreeId(), "PREPARING", null); } @@ -151,15 +104,15 @@ public class SessionWorktreeStore { "phase", state, "delivery_state", "PENDING", "request_json", request == null ? json(plan) : request)); } - public static Descriptor descriptor(JdbcTemplate jdbc, String tenant, String session) { + public static Descriptor descriptor(WorktreeFiles worktreeFiles, JdbcTemplate jdbc, String tenant, String session) { Row row = find(jdbc, tenant, session); - return verifiedDescriptor(jdbc, row); + return verifiedDescriptor(worktreeFiles, jdbc, row); } - private static Descriptor verifiedDescriptor(JdbcTemplate jdbc, Row row) { + private static Descriptor verifiedDescriptor(WorktreeFiles worktreeFiles, JdbcTemplate jdbc, Row row) { if (row != null && "READY".equals(row.state())) { try { - return row.descriptor(SessionWorktreeGit.currentBranch(SessionWorktreeGit.verify(row.plan(), row.identityJson()))); + return row.descriptor(worktreeFiles.currentBranch(worktreeFiles.verify(row.plan(), row.identityJson()))); } catch (ManagedAgentException exception) { long revision = blockIdentity(jdbc, row, exception.getCode()); return new Descriptor(row.worktreeId(), row.sessionId(), row.workspaceId(), "blocked", row.plan().branch(), @@ -169,40 +122,40 @@ public class SessionWorktreeStore { return row == null ? null : row.descriptor(); } - public static boolean isReady(JdbcTemplate jdbc, String tenant, String session) { - Descriptor descriptor = descriptor(jdbc, tenant, session); + public static boolean isReady(WorktreeFiles worktreeFiles, JdbcTemplate jdbc, String tenant, String session) { + Descriptor descriptor = descriptor(worktreeFiles, jdbc, tenant, session); return descriptor == null || "ready".equals(descriptor.state()); } - public static Map descriptors(JdbcTemplate jdbc, String tenant, List sessions) { + public static Map descriptors(WorktreeFiles worktreeFiles, JdbcTemplate jdbc, String tenant, List sessions) { if (sessions.isEmpty()) { return Map.of(); } var args = new java.util.ArrayList(); args.add(tenant); args.addAll(sessions); - return descriptors(jdbc, jdbc.query(SELECT + " WHERE w.tenant_id = ? AND w.session_id IN (" + return descriptors(worktreeFiles, jdbc, jdbc.query(SELECT + " WHERE w.tenant_id = ? AND w.session_id IN (" + String.join(",", java.util.Collections.nCopies(sessions.size(), "?")) + ")", (record, index) -> row(record, index), args.toArray())); } - static Map descriptors(JdbcTemplate jdbc, List rows) { + static Map descriptors(WorktreeFiles worktreeFiles, JdbcTemplate jdbc, List rows) { var result = new java.util.LinkedHashMap(); - rows.forEach(row -> result.put(row.sessionId(), verifiedDescriptor(jdbc, row))); + rows.forEach(row -> result.put(row.sessionId(), verifiedDescriptor(worktreeFiles, jdbc, row))); return result; } - public static void requireReady(JdbcTemplate jdbc, String tenant, String session) { - Descriptor descriptor = descriptor(jdbc, tenant, session); + public static void requireReady(WorktreeFiles worktreeFiles, JdbcTemplate jdbc, String tenant, String session) { + Descriptor descriptor = descriptor(worktreeFiles, jdbc, tenant, session); if (descriptor != null && !"ready".equals(descriptor.state())) { - throw error(descriptor.errorCode() == null ? "worktree_" + descriptor.state() : descriptor.errorCode()); + throw com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.error(descriptor.errorCode() == null ? "worktree_" + descriptor.state() : descriptor.errorCode()); } } public static void requireCwd(JdbcTemplate jdbc, String tenant, String session, String cwd) { Row row = find(jdbc, tenant, session); if (row != null && !Path.of(cwd).normalize().startsWith(Path.of(row.plan().worktreeRoot()))) { - throw error("worktree_cwd_outside"); + throw com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.error("worktree_cwd_outside"); } } @@ -223,7 +176,7 @@ public class SessionWorktreeStore { throw notFound(); } if (write && jdbc.queryForList("SELECT 1 FROM ma_session_worktree WHERE tenant_id = ? AND worktree_id = ?" - + " AND owner_actor_key = ?", Integer.class, tenant, id, ManagedWorkspaceRegistry.actorKey(tenant, actor)).isEmpty()) { + + " AND owner_actor_key = ?", Integer.class, tenant, id, com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenant, actor)).isEmpty()) { throw notFound(); } return rows.getFirst(); @@ -235,7 +188,7 @@ public class SessionWorktreeStore { } List rows = jdbc.query(SELECT + " WHERE w.tenant_id = ? AND w.workspace_id = ? AND w.state <> 'REMOVED' ORDER BY w.gmt_create DESC", SessionWorktreeStore::row, tenant, workspace); - var descriptions = descriptors(jdbc, tenant, rows.stream().map(Row::sessionId).toList()); + var descriptions = descriptors(worktreeFiles, jdbc, tenant, rows.stream().map(Row::sessionId).toList()); return rows.stream().map(row -> descriptions.get(row.sessionId())).toList(); } @@ -261,7 +214,7 @@ public class SessionWorktreeStore { + " AND (claim_until IS NULL OR claim_until <= ?) FOR UPDATE", Integer.class, key(row), now).isEmpty()) { return null; } - String physical = WorkspaceExecutionStore.storageKey(jdbc, storageBinding(row.plan())); + String physical = WorkspaceExecutionStore.storageKey(jdbc, com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.storageBinding(row.plan())); if (!jdbc.queryForList("SELECT 1 FROM ma_storage_owner WHERE storage_key = ? AND maintenance_id = ?" + " AND holder_key IS NOT NULL FOR UPDATE", Integer.class, physical, row.operationId()).isEmpty()) { jdbc.update("UPDATE ma_session_worktree SET state = 'BLOCKED', failure_code = 'maintenance_stop_unverified'," @@ -331,7 +284,7 @@ public class SessionWorktreeStore { || current.generation() != row.generation()) { return; } - var binding = storageBinding(row.plan()); + var binding = com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.storageBinding(row.plan()); if (maintenanceHeld(row) || !WorkspaceMaintenanceRecovery.stopped(jdbc, binding, row.operationId(), row.generation())) { return; } @@ -346,13 +299,13 @@ public class SessionWorktreeStore { public boolean maintenanceHeld(Row row) { return !jdbc.queryForList("SELECT 1 FROM ma_storage_owner WHERE storage_key = ? AND maintenance_id = ?" - + " AND holder_key IS NOT NULL", Integer.class, WorkspaceExecutionStore.storageKey(jdbc, storageBinding(row.plan())), + + " AND holder_key IS NOT NULL", Integer.class, WorkspaceExecutionStore.storageKey(jdbc, com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.storageBinding(row.plan())), row.operationId()).isEmpty(); } public Row cleanup(String tenant, String actor, String workspace, String id, String idempotencyKey) { if (idempotencyKey == null || idempotencyKey.isBlank() || idempotencyKey.length() > 512) { - throw error("invalid_idempotency_key"); + throw com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.error("invalid_idempotency_key"); } return transaction.execute(status -> { WorkspaceStorageKindGuard.lockDomain(jdbc, tenant); @@ -368,7 +321,7 @@ public class SessionWorktreeStore { } String blocker = cleanupBlocker(row); if (!List.of("READY", "FAILED").contains(row.state()) || blocker != null) { - throw error(blocker == null ? "worktree_not_ready" : blocker); + throw com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.error(blocker == null ? "worktree_not_ready" : blocker); } createOperation(tenant, row.sessionId(), row.plan(), operation, "REMOVING", json(Map.of("worktreeId", id, "actor", actor, "idempotencyKey", idempotencyKey))); @@ -428,7 +381,7 @@ public class SessionWorktreeStore { private String storageIdentity(ContextBinding binding) { var alias = jdbc.queryForList("SELECT physical_key, registration_revision, mount_revision, fence_operation_id" + " FROM ma_storage WHERE alias_key = ? FOR UPDATE", - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(binding.getTenantId(), binding.getStorageId())); + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(binding.getTenantId(), binding.getStorageId())); var owner = jdbc.queryForList("SELECT mount_revision, mount_registration_id, mount_state, mount_operation_id" + " FROM ma_storage_owner WHERE storage_key = ? FOR UPDATE", WorkspaceExecutionStore.storageKey(jdbc, binding)); return org.apache.commons.codec.digest.DigestUtils.sha256Hex(json(List.of(alias, owner))); @@ -438,11 +391,6 @@ public class SessionWorktreeStore { return WorkspaceOperations.key(WorkspaceOperations.Kind.SESSION_WORKTREE, row.tenantId(), row.operationId()); } - public static ContextBinding storageBinding(Plan plan) { - return new ContextBinding(plan.tenantId(), plan.workspaceId(), plan.workspaceGeneration(), plan.storageId(), - ".", plan.contextConfigRef(), plan.contextRevision()); - } - private static Row row(ResultSet result, int index) throws SQLException { return readRow(result, ""); } @@ -467,11 +415,7 @@ public class SessionWorktreeStore { } } - private static ManagedAgentException error(String code) { - return SessionWorktreeGit.error(code, "The Session worktree cannot perform this operation (" + code + ")."); - } - private static ManagedAgentException notFound() { - return new ManagedAgentException(HttpStatus.NOT_FOUND, "worktree_not_found", "Session worktree not found."); + return new ManagedAgentException(404, "worktree_not_found", "Session worktree not found."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStore.java similarity index 81% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStore.java index 856a1b7f3e1..077f7a884df 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStore.java @@ -1,18 +1,17 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeStorage; -import com.fasterxml.jackson.core.type.TypeReference; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeStorage; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.PlatformTransactionManager; @@ -22,21 +21,20 @@ import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.UUID; @Repository -public class SshWorkspaceStore { - public static final String KIND = "SSH_LOCAL"; - public static final String OPERATION = "WORKSPACE_CREATE"; +public class SshWorkspaceStore + implements com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore { + private static final ObjectMapper JSON = new ObjectMapper(); + /** * D7-2: how long a registration that may have reached its node parks * before its node is read again. A register-storage still running on the * node when its caller gave up finishes well within it. */ static final long PARK_DELAY_MILLIS = 300_000; - private static final ObjectMapper JSON = new ObjectMapper(); - private static final TypeReference> MAP = new TypeReference<>() { }; + private final JdbcTemplate jdbc; private final TransactionTemplate transaction; @@ -45,25 +43,11 @@ public class SshWorkspaceStore { this.transaction = new TransactionTemplate(manager); } - public record Operation(String operationId, String type, String status, - String workspaceId, String failureCode, boolean replayed) { - } - - public record Intent(String operationId, String tenantId, String actorId, - String nodeId, String path, String displayName, String workspaceId, - String storageId, String registrationId, Map registration) { - } - /** * D7-2: a registration parked recovery_blocked. Only a reserved one can * have reached its node; its registration names the identity and the * marker the node would hold. */ - public record Parked(Intent intent, String failureCode) { - public boolean reserved() { - return !intent.registration().isEmpty(); - } - } public Operation begin(String tenant, String actor, String key, String node, String path, String name) { @@ -75,7 +59,7 @@ public class SshWorkspaceStore { var rows = operationRows(tenant, actor, id, true); if (!rows.isEmpty()) { if (!digest.equals(rows.getFirst().get("request_digest"))) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "idempotency_conflict", "Registration key was used for another request."); + throw new ManagedAgentException(409, "idempotency_conflict", "Registration key was used for another request."); } return operation(rows.getFirst(), true); } @@ -90,7 +74,7 @@ public class SshWorkspaceStore { + " VALUES (?, ?, ?, ?, ?, 'WORKSPACE', ?, ?, ?, ?, ?, ?, ?, ?, 'PENDING', 'PENDING', " + ManagedSchemaTime.SQL_NOW + ", " + ManagedSchemaTime.SQL_NOW + ")", operationKey(tenant, id), ManagedSchemaKeys.key(tenant), OPERATION, bytes(id), bytes(tenant), - ManagedSchemaKeys.key(workspace), bytes(workspace), ManagedWorkspaceRegistry.actorKey(tenant, actor), + ManagedSchemaKeys.key(workspace), bytes(workspace), com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenant, actor), bytes(actor), bytes(key), ManagedSchemaKeys.key(key), digest, encode(request)); return new Operation(id, "workspace_create", "pending", null, null, false); }); @@ -99,7 +83,7 @@ public class SshWorkspaceStore { public Operation get(String tenant, String actor, String id) { var rows = operationRows(tenant, actor, id, false); if (rows.isEmpty()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "operation_not_found", "Workspace registration not found."); + throw new ManagedAgentException(404, "operation_not_found", "Workspace registration not found."); } return operation(rows.getFirst(), true); } @@ -112,7 +96,7 @@ public class SshWorkspaceStore { return null; } if (!ManagedSchemaKeys.key(node, path, name).equals(rows.getFirst().get("request_digest"))) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "idempotency_conflict", "Registration key was used for another request."); + throw new ManagedAgentException(409, "idempotency_conflict", "Registration key was used for another request."); } return operation(rows.getFirst(), true); } @@ -122,7 +106,7 @@ public class SshWorkspaceStore { || node == null || node.isBlank() || node.length() > 128 || path == null || path.isBlank() || path.length() > 512 || name == null || name.isBlank() || name.length() > 512) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", "Workspace registration request is invalid."); + throw new ManagedAgentException(400, "invalid_request", "Workspace registration request is invalid."); } } @@ -147,28 +131,28 @@ public class SshWorkspaceStore { private static Intent intent(String id, String tenant, Map row) { Map value = decode((String) row.get("request_json")); Map registration = decode((String) row.get("result_json")); - return new Intent(id, tenant, string(value, "actorId"), string(value, "nodeId"), string(value, "path"), - string(value, "displayName"), string(value, "workspaceId"), string(value, "storageId"), - string(value, "registrationId"), registration); + return new Intent(id, tenant, com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(value, "actorId"), com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(value, "nodeId"), com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(value, "path"), + com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(value, "displayName"), com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(value, "workspaceId"), com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(value, "storageId"), + com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(value, "registrationId"), registration); } public void reserve(Intent intent, String owner, Map registration) { transaction.executeWithoutResult(status -> { WorkspaceStorageKindGuard.lockDomain(jdbc, intent.tenantId()); requireClaim(intent, owner); - Map identity = object(registration, "identity"); + Map identity = com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.object(registration, "identity"); LocalStorageOwners.requireNonOverlapping(jdbc, physicalIdentity(identity)); String physical = physicalKey(identity); - String root = string(identity, "root"); + String root = com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(identity, "root"); jdbc.update("INSERT INTO ma_storage_owner (storage_key, physical_key, storage_kind, tenant_id, storage_id," + " mount_root, mount_host_id, mount_device, mount_inode, mount_birth_time, mount_registration_id)" + " VALUES (?, ?, 'SSH_LOCAL', ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE storage_key = storage_key", - physical, physical, intent.tenantId(), intent.storageId(), root, string(identity, "hostId"), - string(identity, "device"), string(identity, "inode"), string(identity, "birthTime"), intent.registrationId()); + physical, physical, intent.tenantId(), intent.storageId(), root, com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(identity, "hostId"), + com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(identity, "device"), com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(identity, "inode"), com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(identity, "birthTime"), intent.registrationId()); var rows = jdbc.queryForList("SELECT storage_kind, mount_registration_id FROM ma_storage_owner WHERE storage_key = ? FOR UPDATE", physical); if (rows.size() != 1 || !KIND.equals(rows.getFirst().get("storage_kind")) || !intent.registrationId().equals(rows.getFirst().get("mount_registration_id"))) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "storage_already_registered", "Physical directory already has an owner."); + throw new ManagedAgentException(409, "storage_already_registered", "Physical directory already has an owner."); } String alias = JdbcRuntimeBindingRepository.storageAliasKey(intent.tenantId(), intent.storageId()); jdbc.update("INSERT INTO ma_storage (alias_key, tenant_key, storage_key, tenant_id, storage_id, physical_key," @@ -188,7 +172,7 @@ public class SshWorkspaceStore { requireClaim(intent, owner); publish(intent); if (finish(intent, owner, "COMPLETED", null) != 1) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "operation_claim_lost", "Registration claim expired."); + throw new ManagedAgentException(409, "operation_claim_lost", "Registration claim expired."); } }); } @@ -198,7 +182,7 @@ public class SshWorkspaceStore { String alias = JdbcRuntimeBindingRepository.storageAliasKey(intent.tenantId(), intent.storageId()); var rows = jdbc.queryForList("SELECT registration_json FROM ma_storage WHERE alias_key = ? AND storage_kind = 'SSH_LOCAL' FOR UPDATE", alias); if (rows.size() != 1) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } jdbc.update("UPDATE ma_storage SET registration_state = 'REGISTERED', mount_revision = 1 WHERE alias_key = ?", alias); jdbc.update("UPDATE ma_storage_owner SET mount_state = 'READY', mount_revision = 1," @@ -208,7 +192,7 @@ public class SshWorkspaceStore { + " VALUES (?, ?, 1, ?, ?, ?, ?, 'ACTIVE')", intent.tenantId(), intent.workspaceId(), intent.storageId(), intent.displayName(), WorkspaceExecutionProfile.CONFIG_REF, WorkspaceExecutionProfile.POLICY_REF); jdbc.update("INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role) VALUES (?, ?, ?, 'OWNER')", - intent.tenantId(), intent.workspaceId(), ManagedWorkspaceRegistry.actorKey(intent.tenantId(), intent.actorId())); + intent.tenantId(), intent.workspaceId(), com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(intent.tenantId(), intent.actorId())); } /** @@ -290,7 +274,7 @@ public class SshWorkspaceStore { JdbcRuntimeBindingRepository.storageAliasKey(tenant, intent.storageId())); jdbc.update("DELETE FROM ma_storage_owner WHERE storage_key = ? AND storage_kind = 'SSH_LOCAL'" + " AND mount_registration_id = ? AND mount_state = 'UNVERIFIED'", - physicalKey(object(intent.registration(), "identity")), intent.registrationId()); + physicalKey(com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.object(intent.registration(), "identity")), intent.registrationId()); } return settleParked(tenant, operationId, "FAILED", code) == 1; })); @@ -330,7 +314,7 @@ public class SshWorkspaceStore { } var storage = runtimeStorage(scope.getTenantId(), text(rows.getFirst().get("storage_id"))); if (storage != null && !scope.getWorkspaceGeneration().equals(rows.getFirst().get("workspace_generation").toString())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return storage; } @@ -342,11 +326,11 @@ public class SshWorkspaceStore { return null; } if (!"REGISTERED".equals(rows.getFirst().get("registration_state"))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Map registration = decode((String) rows.getFirst().get("registration_json")); - return new SshRuntimeStorage(string(registration, "nodeId"), storage, - string(object(registration, "identity"), "root"), registration); + return new SshRuntimeStorage(com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(registration, "nodeId"), storage, + com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.object(registration, "identity"), "root"), registration); } public boolean verify(ContextBinding binding) { @@ -356,14 +340,14 @@ public class SshWorkspaceStore { } if (jdbc.queryForObject("SELECT COUNT(*) FROM ma_storage WHERE alias_key = ? AND fence_operation_id IS NULL", Long.class, JdbcRuntimeBindingRepository.storageAliasKey(binding.getTenantId(), binding.getStorageId())) != 1) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } - Map identity = object(storage.registration(), "identity"); + Map identity = com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.object(storage.registration(), "identity"); var rows = jdbc.queryForList("SELECT mount_state, mount_registration_id FROM ma_storage_owner WHERE storage_key = ? AND storage_kind = 'SSH_LOCAL'", physicalKey(identity)); if (rows.size() != 1 || !"READY".equals(rows.getFirst().get("mount_state")) - || !string(object(storage.registration(), "marker"), "registrationId").equals(rows.getFirst().get("mount_registration_id"))) { - throw WorkspaceExecutionStore.unavailable(); + || !com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.object(storage.registration(), "marker"), "registrationId").equals(rows.getFirst().get("mount_registration_id"))) { + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return true; } @@ -372,7 +356,7 @@ public class SshWorkspaceStore { if (jdbc.queryForList("SELECT operation_key FROM ma_operation WHERE operation_key = ? AND claim_owner = ?" + " AND state = 'RUNNING' AND claim_until > " + ManagedSchemaTime.SQL_NOW + " FOR UPDATE", String.class, operationKey(intent.tenantId(), intent.operationId()), owner).size() != 1) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "operation_claim_lost", "Registration claim expired."); + throw new ManagedAgentException(409, "operation_claim_lost", "Registration claim expired."); } } @@ -388,34 +372,23 @@ public class SshWorkspaceStore { private List> operationRows(String tenant, String actor, String id, boolean lock) { return WorkspaceOperations.rows(jdbc, "SELECT * FROM ma_operation WHERE operation_key = ? AND operation_kind = 'WORKSPACE_CREATE'" + " AND tenant_id = ? AND actor_key = ?" + (lock ? " FOR UPDATE" : ""), - operationKey(tenant, id), tenant, ManagedWorkspaceRegistry.actorKey(tenant, actor)); + operationKey(tenant, id), tenant, com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenant, actor)); } private static Operation operation(Map row, boolean replayed) { var request = decode((String) row.get("request_json")); String state = (String) row.get("state"); return new Operation((String) row.get("operation_id"), "workspace_create", state.toLowerCase(java.util.Locale.ROOT), - "COMPLETED".equals(state) ? string(request, "workspaceId") : null, (String) row.get("failure_code"), replayed); - } - - public static Map object(Map value, String key) { - return JSON.convertValue(Objects.requireNonNull(value.get(key), key), MAP); - } - - public static String string(Map value, String key) { - if (!(value.get(key) instanceof String text) || text.isBlank()) { - throw WorkspaceExecutionStore.unavailable(); - } - return text; + "COMPLETED".equals(state) ? com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(request, "workspaceId") : null, (String) row.get("failure_code"), replayed); } public static String physicalKey(Map identity) { return LocalStorageOwners.physicalKey(physicalIdentity(identity)); } - private static WorkspaceStorageGuard.Identity physicalIdentity(Map identity) { - return new WorkspaceStorageGuard.Identity(string(identity, "root"), string(identity, "hostId"), - string(identity, "device"), string(identity, "inode"), string(identity, "birthTime")); + private static StorageIdentityReader.Identity physicalIdentity(Map identity) { + return new StorageIdentityReader.Identity(com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(identity, "root"), com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(identity, "hostId"), + com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(identity, "device"), com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(identity, "inode"), com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.string(identity, "birthTime")); } static String physicalKey(JdbcTemplate jdbc, String tenant, String storage) { @@ -430,14 +403,14 @@ public class SshWorkspaceStore { if (!tenant.equals(text(row.get("tenant_id"))) || !storage.equals(text(row.get("storage_id"))) || !"REGISTERED".equals(row.get("registration_state")) || row.get("fence_operation_id") != null || !(row.get("physical_key") instanceof String key)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } var owners = jdbc.queryForList("SELECT mount_state, tenant_id, storage_id FROM ma_storage_owner WHERE storage_key = ?" + " AND storage_kind = 'SSH_LOCAL'" + (lock ? " FOR UPDATE" : ""), key); if (owners.size() != 1 || !"READY".equals(owners.getFirst().get("mount_state")) || !tenant.equals(text(owners.getFirst().get("tenant_id"))) || !storage.equals(text(owners.getFirst().get("storage_id")))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return key; } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAdmissionStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAdmissionStore.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAdmissionStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAdmissionStore.java index 5eb096c3d7d..85137e3e38c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAdmissionStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAdmissionStore.java @@ -1,11 +1,13 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -26,8 +28,8 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import static com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract.require; -import static com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract.text; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract.require; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract.text; /** Joins one frozen publication root and its original receipt in the Session transaction. */ public final class ToolPublicationAdmissionStore { @@ -142,7 +144,7 @@ public final class ToolPublicationAdmissionStore { var head = jdbc.query("SELECT journal_revision, committed_sequence, storage_version FROM ma_session_head" + " WHERE head_kind = 'MANAGED_SESSION' AND tenant_key = ? AND harness_key = ? AND tenant_id = ? AND session_id = ?" + suffix, (schemaRow, schemaIndex) -> ManagedSchemaRows.read(schemaRow), ManagedSchemaKeys.key(text(key, "tenantId")), ManagedSchemaKeys.key(text(key, "sessionId")), text(key, "tenantId"), text(key, "sessionId")).getFirst(); require(((Number) head.get("storage_version")).intValue() >= 1 - && ((Number) head.get("storage_version")).intValue() <= ManagedSessionReaderVersion.SUPPORTED, + && ((Number) head.get("storage_version")).intValue() <= com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedStorageVersions.SUPPORTED, "Managed Session storage version is unsupported"); require(positiveCounter(head.get("journal_revision")) >= revision && positiveCounter(head.get("committed_sequence")) >= sequence, @@ -213,7 +215,7 @@ public final class ToolPublicationAdmissionStore { var resources = result.putArray("resources"); for (JsonNode ref : List.of(outcomeRef, manifest)) { String resourceId = text(ref, "resourceId"); - String sessionScope = ManagedSessionStore.sessionScopeKey(text(key, "tenantId"), text(key, "sessionId")); + String sessionScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(text(key, "tenantId"), text(key, "sessionId")); var resource = jdbc.query("SELECT tenant_id, workspace_id, session_id, resource_id, kind, schema_version," + " byte_length, sha256, storage_kind, inline_bytes, object_key, object_version_id, encryption_key_id," + " publish_command_id, state FROM ma_resource" @@ -474,7 +476,7 @@ public final class ToolPublicationAdmissionStore { private void referenceCapturedResource(JsonNode key, CommitTransactionRequest request, JsonNode ref, byte[] bytes, String objectKey, String kind) { - String scopeKey = ManagedSessionStore.sessionScopeKey(text(key, "tenantId"), text(key, "sessionId")); + String scopeKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(text(key, "tenantId"), text(key, "sessionId")); String resourceId = text(ref, "resourceId"); var rows = jdbc.query("SELECT state, kind, byte_length, sha256, capture_state, publish_command_id" + " FROM ma_resource WHERE session_scope_key = ? AND resource_id = ?" diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollector.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollector.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollector.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollector.java index bf0e003dec5..2e93a1b8642 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollector.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollector.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; @@ -32,12 +32,12 @@ public final class ToolPublicationCollector { private final TransactionTemplate transactions; private final ToolPublicationRetentionStore retention; private final ToolPublicationObjectStore objects; - private final ManagedAgentProperties properties; + private final ManagedAgentSettings properties; private final String owner = UUID.randomUUID().toString(); public ToolPublicationCollector(JdbcTemplate jdbc, PlatformTransactionManager manager, ToolPublicationRetentionStore retention, ToolPublicationObjectStore objects, - ManagedAgentProperties properties) { + ManagedAgentSettings properties) { this.jdbc = jdbc; this.transactions = new TransactionTemplate(manager); this.transactions.setIsolationLevel(org.springframework.transaction.TransactionDefinition.ISOLATION_REPEATABLE_READ); @@ -149,7 +149,7 @@ public final class ToolPublicationCollector { jdbc.update( "INSERT IGNORE INTO ma_operation (operation_key, session_scope_key, scope_key, operation_kind, operation_id, publication_id, tenant_id, workspace_id, session_id, target_kind, target_key, request_digest, request_json, phase, state, gmt_create, gmt_modified) VALUES (?, ?, ?, 'CAPTURE_GC', ?, ?, ?, ?, ?, 'OUTPUT_CAPTURE', ?, ?, ?, 'COLLECTING', 'PENDING', ?, ?)", operationKey(scope, publication), - ManagedSessionStore.sessionScopeKey( + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey( (String) capture.get("tenant_id"), (String) capture.get("session_id")), scope, diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationDataStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationDataStore.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationDataStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationDataStore.java index 4c9d3d0801b..90dce091e64 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationDataStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationDataStore.java @@ -1,13 +1,14 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationStorageException; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationStorageException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; @@ -16,7 +17,6 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.codec.digest.DigestUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -42,8 +42,8 @@ import java.util.Objects; import java.util.UUID; import java.util.concurrent.TimeUnit; -import static com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract.require; -import static com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract.text; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract.require; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract.text; /** O2b's publication catalog. Object I/O always runs outside SQL transactions. */ public final class ToolPublicationDataStore { @@ -401,7 +401,7 @@ public final class ToolPublicationDataStore { } String active = jdbc.queryForObject("SELECT active_operation_id FROM ma_output_capture" + " WHERE scope_key = ? AND publication_id = ?", String.class, scope, publicationId); - requireContract("terminal".equals(row.slot()) || operationId.equals(active), HttpStatus.CONFLICT, + requireContract("terminal".equals(row.slot()) || operationId.equals(active), 409, "managed_tool_publication_claim_lost", "Publication operation lost its slot"); long epoch = row.epoch() + 1; Timestamp until = new Timestamp(Math.min(row.deadline().getTime(), @@ -539,15 +539,15 @@ public final class ToolPublicationDataStore { .contains(api.getCode())) { return null; } - return api.getStatus().is4xxClientError() || api.getStatus() == HttpStatus.INSUFFICIENT_STORAGE ? api : null; + return (api.getStatus() >= 400 && api.getStatus() < 500) || api.getStatus() == 507 ? api : null; } if (error instanceof CorruptObjectException || error instanceof ToolPublicationContract.ContractViolationException) { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", "Publication verification failed"); + return new ManagedAgentException(400, "invalid_request", "Publication verification failed"); } if (error instanceof ToolPublicationStorageException storage && storage.getReason() == ToolPublicationStorageException.Reason.ACCESS_DENIED) { - return new ManagedAgentException(HttpStatus.FORBIDDEN, "managed_tool_publication_storage_denied", + return new ManagedAgentException(403, "managed_tool_publication_storage_denied", "Publication storage rejected verification"); } return null; @@ -622,7 +622,7 @@ public final class ToolPublicationDataStore { private String recordVerificationFailure(String scope, String publicationId, String operationId, ManagedAgentException failure) { jdbc.update("UPDATE ma_operation SET gmt_modified = " + ManagedSchemaTime.SQL_NOW + ", state = 'FAILED', failure_status = ?, failure_code = ?, claim_owner = NULL, claim_until = NULL, verification_next_at = NULL WHERE operation_kind IN ('OUTPUT_UPLOAD', 'OUTPUT_VERIFY', 'OUTPUT_FINISH') AND scope_key = ? AND publication_id = ? AND operation_id = ?", - failure.getStatus().value(), failure.getCode(), scope, publicationId, operationId); + failure.getStatus(), failure.getCode(), scope, publicationId, operationId); jdbc.update("UPDATE ma_output_capture SET gmt_modified = " + ManagedSchemaTime.SQL_NOW + ", active_operation_id = NULL WHERE scope_key = ? AND publication_id = ? AND active_operation_id = ?", scope, publicationId, operationId); String objectKey = releaseFailedFinish(scope, publicationId, operationId); LOG.info("Tool publication verification failed operation={} code={}", operationId, failure.getCode()); @@ -750,14 +750,14 @@ public final class ToolPublicationDataStore { requireContract(((Number) row.get("segment_count")).intValue() == segmentCount && ((Number) row.get("byte_length")).longValue() == byteLength && digest.equals(row.get("sha256")), - HttpStatus.CONFLICT, "managed_tool_result_conflict", "Seal conflicts"); + 409, "managed_tool_result_conflict", "Seal conflicts"); } StreamScan scan = scan(scope, publicationId, streamId, segmentCount, heartbeat(key, scope, publicationId, token, operationId, claim.epoch())); requireContract(scan.segmentCount() == segmentCount, - HttpStatus.CONFLICT, "managed_tool_result_conflict", "Seal has missing or extra segments"); + 409, "managed_tool_result_conflict", "Seal has missing or extra segments"); requireContract(scan.byteLength() == byteLength && scan.digest().equals(digest), - HttpStatus.BAD_REQUEST, "managed_tool_result_digest_mismatch", "Seal digest mismatch"); + 400, "managed_tool_result_digest_mismatch", "Seal digest mismatch"); return transactions.execute(status -> { authorize(key, scope, publicationId, token); checkScanClaim(scope, publicationId, operationId, claim.epoch()); @@ -768,7 +768,7 @@ public final class ToolPublicationDataStore { requireContract(((Number) old.get(0).get("segment_count")).intValue() == segmentCount && ((Number) old.get(0).get("byte_length")).longValue() == byteLength && digest.equals(old.get(0).get("sha256")), - HttpStatus.CONFLICT, "managed_tool_result_conflict", "Seal conflicts"); + 409, "managed_tool_result_conflict", "Seal conflicts"); } ObjectNode receipt = JSON.createObjectNode().put("segmentCount", segmentCount) .put("byteLength", byteLength).put("digest", digest); @@ -856,7 +856,7 @@ public final class ToolPublicationDataStore { }, scope, publicationId, operationId); if (rows.isEmpty()) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, + throw new ManagedAgentException(404, "managed_tool_publication_operation_unknown", "Publication operation is unknown"); } Map row = rows.get(0); @@ -900,7 +900,7 @@ public final class ToolPublicationDataStore { List rows = operation(scope, publicationId, operationId, true); require(rows.size() == 1, "Publication operation is missing"); Operation row = rows.get(0); - requireContract(!"FAILED".equals(row.state()), HttpStatus.CONFLICT, + requireContract(!"FAILED".equals(row.state()), 409, "managed_tool_publication_failed", "Failed operation cannot be recovered"); if (authorization.execution().getState() == ToolExecutionRecord.State.SETTLED) { @@ -929,7 +929,7 @@ public final class ToolPublicationDataStore { // A recovery starts a new bounded verification attempt. It does // not extend the original deadline, change bytes or release quota. String active = availableActive(scope, publicationId, operationId, current); - requireContract(active == null || operationId.equals(active), HttpStatus.CONFLICT, + requireContract(active == null || operationId.equals(active), 409, "managed_tool_publication_busy", "Publication is busy"); jdbc.update("UPDATE ma_operation SET gmt_modified = " + ManagedSchemaTime.SQL_NOW + ", recovery_deadline = ?, claim_generation = claim_generation + 1, claim_owner = NULL, claim_until = NULL WHERE operation_kind IN ('OUTPUT_UPLOAD', 'OUTPUT_VERIFY', 'OUTPUT_FINISH') AND scope_key = ? AND publication_id = ? AND operation_id = ?", new Timestamp(current.getTime() + verificationWindow(scope, publicationId, @@ -1068,7 +1068,7 @@ public final class ToolPublicationDataStore { + " WHERE scope_key = ? AND publication_id = ? AND tenant_id = ?" + " AND workspace_id = ? AND session_id = ?", (schemaRow, schemaIndex) -> ManagedSchemaRows.read(schemaRow), scope, publicationId, text(key, "tenantId"), text(key, "workspaceId"), text(key, "sessionId")); - requireContract(!publications.isEmpty(), HttpStatus.NOT_FOUND, + requireContract(!publications.isEmpty(), 404, "managed_tool_publication_unknown", "Publication is unknown"); Map publication = publications.getFirst(); require("FINISHED".equals(publication.get("producer_phase")) @@ -1242,9 +1242,9 @@ public final class ToolPublicationDataStore { if (claim.predecessor() != null) { List previous = operation(scope, publicationId, claim.predecessor(), false); requireContract(previous.isEmpty() || !"FAILED".equals(previous.get(0).state()), - HttpStatus.CONFLICT, "managed_tool_publication_predecessor_failed", "Finish predecessor failed"); + 409, "managed_tool_publication_predecessor_failed", "Finish predecessor failed"); requireContract(previous.size() == 1 && "SUCCEEDED".equals(previous.get(0).state()), - HttpStatus.CONFLICT, "managed_tool_publication_busy", "Finish predecessor has not completed"); + 409, "managed_tool_publication_busy", "Finish predecessor has not completed"); } validateFinished(key, publicationId, claim.binding(), result, heartbeat(key, scope, publicationId, token, operationId, claim.epoch())); @@ -1300,10 +1300,10 @@ public final class ToolPublicationDataStore { String requestDigest = requestDigest("terminal", length, digest); List prior = operation(scope, publicationId, operationId, true); if (!prior.isEmpty()) { - requireContract(!"FAILED".equals(prior.get(0).state()), HttpStatus.CONFLICT, + requireContract(!"FAILED".equals(prior.get(0).state()), 409, "managed_tool_publication_failed", "Publication operation has failed"); requireContract(preparing || verificationTask.get() != null || !prior.get(0).asynchronous(), - HttpStatus.CONFLICT, "managed_tool_publication_busy", "Publication operation is asynchronous"); + 409, "managed_tool_publication_busy", "Publication operation is asynchronous"); } if ("FINISHED".equals(phase)) { @@ -1335,7 +1335,7 @@ public final class ToolPublicationDataStore { && requestDigest.equals(prior.get(0).digest()), "Finish replay conflicts"); requireUnexpired(prior.get(0).deadline(), now); requireContract(prior.get(0).claimUntil() == null || !prior.get(0).claimUntil().after(now), - HttpStatus.CONFLICT, "managed_tool_publication_busy", "Finish operation is busy"); + 409, "managed_tool_publication_busy", "Finish operation is busy"); } else { require(prior.isEmpty(), "Finish operation ID conflicts"); long used = ((Number) row.get("producer_used_bytes")).longValue(); @@ -1380,7 +1380,7 @@ public final class ToolPublicationDataStore { if (claim.predecessor() != null) { List previous = operation(scope, publicationId, claim.predecessor(), true); requireContract(previous.size() == 1 && "SUCCEEDED".equals(previous.get(0).state()), - HttpStatus.CONFLICT, "managed_tool_publication_busy", "Finish predecessor has not completed"); + 409, "managed_tool_publication_busy", "Finish predecessor has not completed"); } Stored terminal = stored(scope, publicationId, "terminal", true).get(0); require("CANDIDATE".equals(terminal.state()) && terminal.length() == bytes.length @@ -1513,10 +1513,10 @@ public final class ToolPublicationDataStore { "Publication is finishing"); List prior = operation(scope, publicationId, operationId, true); if (!prior.isEmpty()) { - requireContract(!"FAILED".equals(prior.get(0).state()), HttpStatus.CONFLICT, + requireContract(!"FAILED".equals(prior.get(0).state()), 409, "managed_tool_publication_failed", "Publication operation has failed"); requireContract(preparing || verificationTask.get() != null || !prior.get(0).asynchronous(), - HttpStatus.CONFLICT, "managed_tool_publication_busy", "Publication operation is asynchronous"); + 409, "managed_tool_publication_busy", "Publication operation is asynchronous"); } if (verificationTask.get() != null) { @@ -1540,11 +1540,11 @@ public final class ToolPublicationDataStore { requireUnexpired(row.deadline(), now); } String active = availableActive(scope, publicationId, operationId, now); - requireContract(active == null || active.equals(operationId), HttpStatus.CONFLICT, + requireContract(active == null || active.equals(operationId), 409, "managed_tool_publication_busy", "Publication is busy"); if (active != null) { requireContract(!prior.isEmpty() && (prior.get(0).claimUntil() == null - || !prior.get(0).claimUntil().after(now)), HttpStatus.CONFLICT, + || !prior.get(0).claimUntil().after(now)), 409, "managed_tool_publication_busy", "Publication operation is busy"); } long epoch = prior.isEmpty() ? 1 : prior.get(0).epoch() + 1; @@ -1566,7 +1566,7 @@ public final class ToolPublicationDataStore { + " WHERE scope_key = ? AND publication_id = ?", String.class, scope, publicationId); List rows = operation(scope, publicationId, operationId, true); requireOperationClaim(rows, epoch, now()); - requireContract(operationId.equals(active), HttpStatus.CONFLICT, + requireContract(operationId.equals(active), 409, "managed_tool_publication_claim_lost", "Publication operation lost its claim"); } @@ -1622,7 +1622,7 @@ public final class ToolPublicationDataStore { } if (expectedCount >= 0) { requireContract(rows.size() == expectedCount, - HttpStatus.CONFLICT, "managed_tool_result_conflict", "Seal has missing or extra segments"); + 409, "managed_tool_result_conflict", "Seal has missing or extra segments"); } MessageDigest aggregate = sha256(); int count = 0; @@ -1735,7 +1735,7 @@ public final class ToolPublicationDataStore { byte[] bytes = input.clone(); String digest = ToolPublicationContract.sha256(bytes); requireContract(expectedDigest == null || expectedDigest.equals(digest), - HttpStatus.BAD_REQUEST, "managed_tool_result_digest_mismatch", "Publication digest mismatch"); + 400, "managed_tool_result_digest_mismatch", "Publication digest mismatch"); String scope = scope(key); JsonNode accepted = submitVerification(key, publicationId, token, operationId, slot, requestDigest(slot, bytes.length, digest), JSON.createObjectNode(), kind, bytes, digest, asynchronous); @@ -1810,10 +1810,10 @@ public final class ToolPublicationDataStore { String requestDigest = requestDigest(slot, length, digest); List prior = operation(scope, publicationId, operationId, true); if (!prior.isEmpty()) { - requireContract(!"FAILED".equals(prior.get(0).state()), HttpStatus.CONFLICT, + requireContract(!"FAILED".equals(prior.get(0).state()), 409, "managed_tool_publication_failed", "Publication operation has failed"); requireContract(preparing || verificationTask.get() != null || !prior.get(0).asynchronous(), - HttpStatus.CONFLICT, "managed_tool_publication_busy", "Publication operation is asynchronous"); + 409, "managed_tool_publication_busy", "Publication operation is asynchronous"); } if (!prior.isEmpty()) { @@ -1825,12 +1825,12 @@ public final class ToolPublicationDataStore { String streamId = slot.split(":")[1]; int sealed = sealRows(scope, publicationId, streamId).size(); requireContract(sealed == 0 || !saved.isEmpty(), - HttpStatus.CONFLICT, "managed_tool_result_conflict", "Publication stream is sealed"); + 409, "managed_tool_result_conflict", "Publication stream is sealed"); } if (!saved.isEmpty()) { Stored row = saved.get(0); requireContract(row.length() == length && digest.equals(row.digest()), - HttpStatus.CONFLICT, "managed_tool_result_conflict", "Publication slot conflicts"); + 409, "managed_tool_result_conflict", "Publication slot conflicts"); if ("VERIFIED".equals(row.state())) { return new Candidate(row.objectKey(), row.resourceId(), 0, receipt(binding, slot, row.resourceId(), kind, length, digest)); @@ -1844,7 +1844,7 @@ public final class ToolPublicationDataStore { requireUnexpired(row.deadline(), now); } String active = availableActive(scope, publicationId, operationId, now); - requireContract(active == null || active.equals(operationId), HttpStatus.CONFLICT, + requireContract(active == null || active.equals(operationId), 409, "managed_tool_publication_busy", "Publication is busy"); String resourceId = kind == null ? null : "result-" + hash(scope + ":" + publicationId + ":" + slot).substring(0, 32); String objectKey = (kind != null && length <= 64 * 1024) ? null @@ -1869,7 +1869,7 @@ public final class ToolPublicationDataStore { claimUntil, new Timestamp(now.getTime() + operationTimeout.toMillis()), now); } else { requireContract(prior.get(0).claimUntil() == null || !prior.get(0).claimUntil().after(now), - HttpStatus.CONFLICT, "managed_tool_publication_busy", "Publication operation is busy"); + 409, "managed_tool_publication_busy", "Publication operation is busy"); jdbc.update("UPDATE ma_operation SET gmt_modified = " + ManagedSchemaTime.SQL_NOW + ", claim_owner = ?, claim_generation = ?, claim_until = ? WHERE operation_kind IN ('OUTPUT_UPLOAD', 'OUTPUT_VERIFY', 'OUTPUT_FINISH') AND scope_key = ? AND publication_id = ? AND operation_id = ?", owner, epoch, claimUntil, scope, publicationId, operationId); } @@ -1884,7 +1884,7 @@ public final class ToolPublicationDataStore { + " WHERE scope_key = ? AND publication_id = ?", String.class, scope, publicationId); List rows = operation(scope, publicationId, operationId, true); requireOperationClaim(rows, candidate.epoch(), now()); - requireContract(operationId.equals(active), HttpStatus.CONFLICT, + requireContract(operationId.equals(active), 409, "managed_tool_publication_claim_lost", "Publication operation lost its claim"); Stored row = stored(scope, publicationId, rows.get(0).slot(), true).get(0); require("CANDIDATE".equals(row.state()) && digest.equals(row.digest()) @@ -2077,7 +2077,7 @@ public final class ToolPublicationDataStore { + " WHERE scope_key = ? AND publication_id = ?", (schemaRow, schemaIndex) -> ManagedSchemaRows.read(schemaRow), scope, publicationId).getFirst(); String tenant = (String) capture.get("tenant_id"); String session = (String) capture.get("session_id"); - String sessionScope = ManagedSessionStore.sessionScopeKey(tenant, session); + String sessionScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); if (resourceId == null) { resourceId = "capture-segment-" + ManagedSchemaKeys.key(scope, publicationId, slot); } @@ -2122,7 +2122,7 @@ public final class ToolPublicationDataStore { + " deadline, request_json, gmt_create, gmt_modified) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'OUTPUT_CAPTURE', ?, ?, ?, ?," + " 'VERIFYING', 'PENDING', ?, ?, ?, ?, ?, ?, ?)", ManagedSchemaKeys.operationKey(OperationKind.valueOf(kind), scope, ManagedSchemaKeys.key(publicationId), operationId), - ManagedSessionStore.sessionScopeKey((String) capture.get("tenant_id"), (String) capture.get("session_id")), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey((String) capture.get("tenant_id"), (String) capture.get("session_id")), scope, kind, operationId, capture.get("tenant_id"), capture.get("workspace_id"), capture.get("session_id"), ManagedSchemaKeys.key(publicationId), publicationId, requestDigest, slot, owner, generation, until, deadline, JSON.createObjectNode().put("version", 1).put("slot", slot).toString(), @@ -2180,7 +2180,7 @@ public final class ToolPublicationDataStore { } byte[] readResource(JsonNode key, String publicationId, String resourceId, - ToolPublicationRetentionStore.ReadLease lease, Runnable guard) { + com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease, Runnable guard) { guard.run(); byte[] bytes = readResourceInternal(key, publicationId, resourceId, lease, guard); lease.check(); @@ -2189,7 +2189,7 @@ public final class ToolPublicationDataStore { } private byte[] readResourceInternal(JsonNode key, String publicationId, String resourceId, - ToolPublicationRetentionStore.ReadLease lease, Runnable guard) { + com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease, Runnable guard) { lease.requireScope(key); lease.check(); String scope = scope(key); @@ -2246,7 +2246,7 @@ public final class ToolPublicationDataStore { private byte[] readRangeInternal(JsonNode key, String publicationId, JsonNode manifestRef, JsonNode expectedIdentity, String streamId, long offset, int length, boolean requireFinished, - ToolPublicationRetentionStore.ReadLease lease, Runnable heartbeat) { + com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease, Runnable heartbeat) { require(offset >= 0 && length >= 0 && length <= MAX_SEGMENT, "Invalid publication range"); lease.requireScope(key); Runnable guard = () -> { @@ -2259,7 +2259,7 @@ public final class ToolPublicationDataStore { VerifiedStream openReferencedStream(JsonNode key, String publicationId, JsonNode outcomeRef, JsonNode manifestRef, JsonNode expectedIdentity, - String streamId, long revision, long sequence, ToolPublicationRetentionStore.ReadLease lease, Runnable guard) { + String streamId, long revision, long sequence, com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease, Runnable guard) { lease.requireScope(key); guard.run(); requireReferenced(key, publicationId, outcomeRef, revision, sequence); @@ -2314,7 +2314,7 @@ public final class ToolPublicationDataStore { } private VerifiedStream verifiedStream(JsonNode key, String publicationId, - JsonNode manifestRef, JsonNode expectedIdentity, String streamId, boolean requireFinished, ToolPublicationRetentionStore.ReadLease lease, Runnable guard) { + JsonNode manifestRef, JsonNode expectedIdentity, String streamId, boolean requireFinished, com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease, Runnable guard) { guard.run(); String scope = scope(key); var publications = jdbc.query("SELECT binding_json, producer_phase," @@ -2322,7 +2322,7 @@ public final class ToolPublicationDataStore { + " WHERE scope_key = ? AND publication_id = ? AND tenant_id = ?" + " AND workspace_id = ? AND session_id = ?", (schemaRow, schemaIndex) -> ManagedSchemaRows.read(schemaRow), scope, publicationId, text(key, "tenantId"), text(key, "workspaceId"), text(key, "sessionId")); - requireContract(!publications.isEmpty(), HttpStatus.NOT_FOUND, + requireContract(!publications.isEmpty(), 404, "managed_tool_publication_unknown", "Publication is unknown"); var publication = publications.getFirst(); require(!requireFinished || "FINISHED".equals(publication.get("producer_phase")) @@ -2427,10 +2427,10 @@ public final class ToolPublicationDataStore { private final String publicationId; private final long size; private final List parts; - private final ToolPublicationRetentionStore.ReadLease lease; + private final com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease; private VerifiedStream(String scope, String publicationId, long size, - List parts, ToolPublicationRetentionStore.ReadLease lease) { + List parts, com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease) { this.scope = scope; this.publicationId = publicationId; this.size = size; @@ -2521,7 +2521,7 @@ public final class ToolPublicationDataStore { } private byte[] referencedResource(JsonNode key, String publicationId, JsonNode ref, - String kind, int maximum, ToolPublicationRetentionStore.ReadLease lease, Runnable guard) { + String kind, int maximum, com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease, Runnable guard) { guard.run(); Resource resource = catalogResource(key, publicationId, text(ref, "resourceId")); require(refMatches(resource, ref, kind) && resource.length() <= maximum, @@ -2557,7 +2557,7 @@ public final class ToolPublicationDataStore { private void copyVerified(Resource resource, String scope, String publicationId, long offset, byte[] target, int targetOffset, int length, - ToolPublicationRetentionStore.ReadLease lease, Runnable heartbeat) { + com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease, Runnable heartbeat) { require(offset >= 0 && length >= 0 && offset <= resource.length() && length <= resource.length() - offset, "Publication copy range is invalid"); MessageDigest hash = sha256(); @@ -2618,24 +2618,24 @@ public final class ToolPublicationDataStore { private static void requireCapacity(long requested, long available) { if (requested > available) { - throw new ManagedAgentException(HttpStatus.INSUFFICIENT_STORAGE, + throw new ManagedAgentException(507, "managed_tool_publication_quota_exhausted", "Tool publication capacity is exhausted"); } } private static void requireUnexpired(Timestamp deadline, Timestamp current) { - requireContract(deadline.after(current), HttpStatus.CONFLICT, + requireContract(deadline.after(current), 409, "managed_tool_publication_operation_expired", "Publication operation expired"); } private static void requireOperationClaim(List rows, long epoch, Timestamp current) { requireContract(rows.size() == 1 && "PENDING".equals(rows.get(0).state()) - && rows.get(0).epoch() == epoch, HttpStatus.CONFLICT, + && rows.get(0).epoch() == epoch, 409, "managed_tool_publication_claim_lost", "Publication operation lost its claim"); Operation row = rows.get(0); requireUnexpired(row.deadline(), current); - requireContract(row.claimUntil() != null && row.claimUntil().after(current), HttpStatus.CONFLICT, + requireContract(row.claimUntil() != null && row.claimUntil().after(current), 409, "managed_tool_publication_claim_expired", "Publication operation claim expired"); } @@ -2657,7 +2657,7 @@ public final class ToolPublicationDataStore { return verificationBudget.timeout(operationTimeout, bytes); } - private static void requireContract(boolean valid, HttpStatus status, + private static void requireContract(boolean valid, int status, String code, String message) { if (!valid) { throw new ManagedAgentException(status, code, message); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStore.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStore.java index 41d2ae3c15c..a4fc76d04d8 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStore.java @@ -1,14 +1,14 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.ArgumentPreparedStatementSetter; import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.JdbcTemplate; @@ -25,7 +25,7 @@ import java.util.Map; import java.util.UUID; /** Durable Session-root protection; elapsed time never closes a physical PUT. */ -public final class ToolPublicationRetentionStore { +public final class ToolPublicationRetentionStore implements com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationRetention { private static final ObjectMapper JSON = new ObjectMapper(); static final long READ_BUDGET_MILLIS = 120_000; private final JdbcTemplate jdbc; @@ -113,7 +113,7 @@ public final class ToolPublicationRetentionStore { jdbc.update( "INSERT INTO ma_session (tenant_id, session_id, session_kind, tenant_key, session_key, session_scope_key, gmt_create, gmt_modified) VALUES (?, ?, 'CONTROL', ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE tenant_key = VALUES(tenant_key), session_key = VALUES(session_key), session_scope_key = VALUES(session_scope_key)", boundedParameters(jdbc, timeoutSeconds, tenant, session, hash(tenant), hash(session), - ManagedSessionStore.sessionScopeKey(tenant, session), time, time)); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), time, time)); return jdbc.query( "SELECT tenant_id, session_id, csi_guard FROM ma_session WHERE tenant_id = ? AND" + " session_id = ? FOR UPDATE", @@ -218,7 +218,7 @@ public final class ToolPublicationRetentionStore { var head = heads.getFirst(); if ("ACTIVE".equals(head.get("state")) && ((Number) head.get("writer_active")).intValue() != 0) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "managed_session_writer_active", + throw new ManagedAgentException(409, "managed_session_writer_active", "Session deletion is waiting for its writer to stop."); } protectedRecovery = !"READY".equals(head.get("recovery_status")); @@ -347,7 +347,7 @@ public final class ToolPublicationRetentionStore { } java.io.InputStream open(String objectKey, ToolPublicationObjectStore objects, - ReadLease lease, Runnable guard) { + com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease lease, Runnable guard) { lease.check(); guard.run(); var input = objects.open(objectKey, () -> { @@ -401,7 +401,7 @@ public final class ToolPublicationRetentionStore { }); } - public final class ReadLease implements AutoCloseable { + public final class ReadLease implements com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease { private final String id; private final String tenant; private final String session; @@ -415,11 +415,11 @@ public final class ToolPublicationRetentionStore { this.generation = generation; } - void requireScope(JsonNode key) { + public void requireScope(JsonNode key) { requireScope(key.path("tenantId").asText(), key.path("sessionId").asText()); } - void requireScope(String requestedTenant, String requestedSession) { + public void requireScope(String requestedTenant, String requestedSession) { ToolPublicationContract.require(tenant.equals(requestedTenant) && session.equals(requestedSession), "Read lease scope conflicts"); } @@ -428,7 +428,7 @@ public final class ToolPublicationRetentionStore { check(true); } - synchronized void checkReadView() { + public synchronized void checkReadView() { check(false); } @@ -487,7 +487,7 @@ public final class ToolPublicationRetentionStore { } private ManagedAgentException expired() { - return new ManagedAgentException(HttpStatus.CONFLICT, "tool_output_read_expired", + return new ManagedAgentException(409, "tool_output_read_expired", "The output read lease expired or its Session was retired."); } @@ -581,7 +581,7 @@ public final class ToolPublicationRetentionStore { jdbc.update( "INSERT INTO ma_operation (operation_key, session_scope_key, scope_key, operation_kind, operation_id, tenant_id, session_id, workspace_id, target_kind, target_key, publication_id, request_digest, request_json, state, phase, claim_generation, gmt_create, gmt_modified) VALUES (?, ?, ?, 'OBJECT_PUT', ?, ?, ?, ?, 'RESOURCE', ?, ?, ?, ?, 'IN_FLIGHT', 'PUT', 1, ?, ?)", originalKey, - ManagedSessionStore.sessionScopeKey(tenant, session), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), scope, id, tenant, @@ -656,13 +656,10 @@ public final class ToolPublicationRetentionStore { } private static ManagedAgentException unresolvedPut() { - return new ManagedAgentException(HttpStatus.CONFLICT, "tool_output_put_unresolved", + return new ManagedAgentException(409, "tool_output_put_unresolved", "The original object write is unresolved; a new write is not admitted."); } - public record Candidate(String scope, String publication, String tenant, String session, - long bytes, String blocker) {} - public List observe(Duration grace) { if (grace.isNegative()) { throw new IllegalArgumentException("Negative deletion grace"); @@ -773,7 +770,7 @@ public final class ToolPublicationRetentionStore { return Boolean.TRUE.equals(value) || value instanceof Number number && number.intValue() != 0; } private static ManagedAgentException retired() { - return new ManagedAgentException(HttpStatus.CONFLICT, "tool_output_session_retired", + return new ManagedAgentException(409, "tool_output_session_retired", "The Session output is permanently retired."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationStore.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationStore.java index 40abe72fdce..12b3e5a4ac5 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationStore.java @@ -1,25 +1,26 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -38,8 +39,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import static com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract.require; -import static com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract.text; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract.require; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract.text; /** Reserves publication capacity and fences producer grants against the Session owner. */ public final class ToolPublicationStore { @@ -176,7 +177,7 @@ public final class ToolPublicationStore { return producerBindingWithHashLocked(scope, publicationId, tokenHash, bindingDigest, Access.CLAIM, true); } catch (IllegalArgumentException error) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "managed_tool_publication_fenced", + throw new ManagedAgentException(409, "managed_tool_publication_fenced", "Original publication authority is fenced", error); } } @@ -207,7 +208,7 @@ public final class ToolPublicationStore { boolean fencedCsi = error instanceof RuntimeBrokerException broker && "workspace_csi_unavailable".equals(broker.getCode()) && !broker.isRetryable(); if (expectedBindingDigest != null && (missingRuntime || fencedCsi)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "managed_tool_publication_runtime_unavailable", + throw new ManagedAgentException(409, "managed_tool_publication_runtime_unavailable", "Original publication runtime is unavailable"); } throw error; @@ -236,7 +237,7 @@ public final class ToolPublicationStore { "SELECT workspace_id, state, storage_version, writer_id, writer_generation, CASE WHEN writer_lease_until > " + ManagedSchemaTime.SQL_NOW + " THEN 1 ELSE 0 END AS writer_live, recovery_status, activation_epoch, journal_revision, activation_id, activation_phase, activation_event_epoch, activation_expires_at, activation_head_revision FROM ma_session_head WHERE head_kind = 'MANAGED_SESSION' AND tenant_key = ? AND harness_key = ? AND tenant_id = ? AND session_id = ? FOR UPDATE", (schemaRow, schemaIndex) -> ManagedSchemaRows.read(schemaRow), ManagedSchemaKeys.key(row.tenant()), ManagedSchemaKeys.key(row.session()), row.tenant(), row.session()).getFirst(); require(((Number) head.get("storage_version")).intValue() >= 1 - && ((Number) head.get("storage_version")).intValue() <= ManagedSessionReaderVersion.SUPPORTED, + && ((Number) head.get("storage_version")).intValue() <= com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedStorageVersions.SUPPORTED, "Managed Session storage version is unsupported."); var current = jdbc.query( "SELECT token_hash, state, expires_at, binding_digest," @@ -971,9 +972,9 @@ public final class ToolPublicationStore { String message = "Tool publication reservation refused at the " + limit + " limit"; Map details = Map.of("limit", limit, "admits", admits); return retained - ? new ManagedAgentException(HttpStatus.CONFLICT, + ? new ManagedAgentException(409, "managed_tool_publication_retention_exhausted", message, details) - : new ManagedAgentException(HttpStatus.TOO_MANY_REQUESTS, + : new ManagedAgentException(429, "managed_tool_publication_capacity_exhausted", message, details); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationVerifier.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationVerifier.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationVerifier.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationVerifier.java index ff0ade23648..1e4bda3ccaf 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationVerifier.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationVerifier.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/TransportConsumerStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/TransportConsumerStore.java similarity index 89% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/TransportConsumerStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/TransportConsumerStore.java index 7a7116bec44..83c3759db4b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/TransportConsumerStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/TransportConsumerStore.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowCallbackHandler; @@ -19,14 +19,14 @@ import java.util.Set; * phase MQ2c). Every decision a consumer takes is read here from SQL truth, never from the broker: * an MQ offset or a delivered envelope is never evidence of anything. * - *

The wake watermark is the SESSION stream's independent - * wake_covered_sequence, the highest journal - * sequence a wake activation was attempted for. It only dedups — a - * redelivered or reordered wake at or below it is a no-op — and is never - * recovery evidence: the independent SQL wake recovery scan finds committed - * unconsumed inputs after writer loss even when a notice was already claimed. + *

The wake watermark is the SESSION stream's independent wake_covered_sequence, the highest + * journal sequence a wake activation was attempted for. It only dedups — a redelivered or reordered + * wake at or below it is a no-op — and is never recovery evidence: the independent SQL wake + * recovery scan finds committed unconsumed inputs after writer loss even when a notice was already + * claimed. */ -public class TransportConsumerStore { +public class TransportConsumerStore + implements com.alibaba.qwen.code.managedagent.repository.TransportConsumerStore { private static final int HELD_CHUNK = 200; private final JdbcTemplate jdbc; @@ -40,14 +40,6 @@ public class TransportConsumerStore { * (workspace, state, committed sequence) and whether a writer holds the journal under a lease * that has not expired by database time. Empty when the Session or its journal does not exist. */ - public record WakeFence(String sessionStatus, String workspaceId, - String headState, long committedSequence, boolean liveWriter) { - /** Whether a wake may act: an ACTIVE Session with an ACTIVE or SEALED journal head. */ - public boolean open() { - return "ACTIVE".equals(sessionStatus) - && ("ACTIVE".equals(headState) || "SEALED".equals(headState)); - } - } public Optional wakeFence(String tenantId, String sessionId) { record Row(String status, String workspaceId, String state, diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStore.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStore.java index d81db6d9479..0b31ab418a4 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStore.java @@ -1,7 +1,8 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Qualifier; @@ -21,7 +22,8 @@ import java.util.Set; /** Discovers committed wake inputs independently of their source relay or MQ notice. */ @Repository -public class WakeRecoveryStore { +public class WakeRecoveryStore + implements com.alibaba.qwen.code.managedagent.repository.WakeRecoveryStore { private static final Set SOURCES = Set.of("monitor", "channel", "child_agent", "workflow", "automation", "session_message"); private static final int JOURNAL_PAGE = 1; @@ -38,11 +40,6 @@ public class WakeRecoveryStore { transactions = new TransactionTemplate(new DataSourceTransactionManager(Objects.requireNonNull(jdbc.getDataSource()))); } - public record Candidate(String tenantId, String workspaceId, String sessionId, - long journalRevision, long committedSequence, long compactedThroughRevision, - String parentSessionId, String parentChildRunId) { - } - private record Chunk(String workspaceId, long revision, long firstSequence, long lastSequence, int eventCount, String encoding, byte[] bytes, long byteLength, String digest) { @@ -93,7 +90,7 @@ public class WakeRecoveryStore { + " AND retired_at IS NULL AND csi_guard = FALSE" + " AND (workspace_id IS NULL OR workspace_id = ?) FOR UPDATE", String.class, candidate.tenantId(), candidate.sessionId(), candidate.workspaceId()); - if (session.isEmpty() || ManagedConfigInstall.blocked(jdbc, candidate.tenantId(), candidate.sessionId())) { + if (session.isEmpty() || ManagedConfigStore.blocked(jdbc, candidate.tenantId(), candidate.sessionId())) { return false; } return jdbc.update("UPDATE ma_session_head h SET wake_retry_at = " + ManagedSchemaTime.SQL_NOW + " + " + RETRY_MS + " WHERE" @@ -160,7 +157,7 @@ public class WakeRecoveryStore { throw corrupt(); } for (int index = 0; index < chunk.eventCount(); index++) { - JsonNode record = ManagedExtensionRecordStore.parse(lines[index]); + JsonNode record = com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.parse(lines[index]); JsonNode event = record == null ? null : record.get("managedSession"); if (event == null || !"managed_session_event_v1".equals(record.path("subtype").asText()) || !event.path("v").isIntegralNumber() || event.path("v").intValue() != 1 diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationRoots.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationRoots.java similarity index 86% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationRoots.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationRoots.java index b984f0f14a6..2d4245b6695 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationRoots.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationRoots.java @@ -1,11 +1,12 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; @@ -33,7 +34,7 @@ final class WorkspaceCreationRoots { private final WorkspaceLocalDirectories localDirectories; private static final ObjectMapper JSON = JsonMapper.builder().build(); - WorkspaceCreationRoots(JdbcTemplate jdbc, ManagedAgentProperties properties) { + WorkspaceCreationRoots(JdbcTemplate jdbc, ManagedAgentSettings properties) { this.jdbc = jdbc; localDirectories = new WorkspaceLocalDirectories(properties); var broker = properties.getRuntimeBroker(); @@ -98,12 +99,12 @@ final class WorkspaceCreationRoots { || !(row.get("idempotency_key") instanceof String idempotency) || !key.equals(ManagedSchemaKeys.receiptKey(ReceiptNamespace.WORKSPACE_CREATE, tenant, ReceiptScope.ACTOR, scope, idempotency))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Details details = details((String) row.get("result_json"), key); if (!storage.equals(details.storageId()) || !("workspace-" + key).equals(details.workspaceId())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } try { if (details.localRoot() != null) { @@ -116,7 +117,7 @@ final class WorkspaceCreationRoots { return null; } if (!Objects.equals(baseKey, directoryKey(base))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Path tenantRoot = base.resolve(ManagedSchemaKeys.key(tenant)); Path root = tenantRoot.resolve(key); @@ -124,17 +125,17 @@ final class WorkspaceCreationRoots { directoryKey(root); return new Root(root, null); } catch (IOException | ManagedAgentException error) { - throw WorkspaceExecutionStore.unavailable(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(error); } } Path prepare(String tenant, String receiptKey) { if (!enabled() || !receiptKey.matches("[a-f0-9]{64}")) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } try { if (!Objects.equals(baseKey, directoryKey(base))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Path tenantRoot = base.resolve(ManagedSchemaKeys.key(tenant)); createDirectory(tenantRoot); @@ -146,7 +147,7 @@ final class WorkspaceCreationRoots { } return root; } catch (IOException error) { - throw WorkspaceExecutionStore.unavailable(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(error); } } @@ -156,7 +157,7 @@ final class WorkspaceCreationRoots { Path importedMetadataDirectory(String storage, boolean create) { if (!sharedStorage(storage) || stateDirectory == null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } try { directoryKey(stateDirectory); @@ -171,7 +172,7 @@ final class WorkspaceCreationRoots { } return metadata; } catch (IOException error) { - throw WorkspaceExecutionStore.unavailable(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(error); } } @@ -207,7 +208,7 @@ final class WorkspaceCreationRoots { } return value; } catch (IOException | RuntimeException error) { - throw WorkspaceExecutionStore.unavailable(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(error); } } @@ -236,11 +237,11 @@ final class WorkspaceCreationRoots { } } - record Root(Path path, WorkspaceStorageGuard.Identity expectedIdentity) {} + record Root(Path path, StorageIdentityReader.Identity expectedIdentity) {} record Details(String workspaceId, String storageId, String displayName, String registrationId, String sourceWorkspaceId, Long sourceGeneration, String cwdRelative, - @JsonInclude(JsonInclude.Include.NON_NULL) WorkspaceStorageGuard.Identity localRoot) { + @JsonInclude(JsonInclude.Include.NON_NULL) StorageIdentityReader.Identity localRoot) { Details(String workspaceId, String storageId, String displayName, String registrationId) { this(workspaceId, storageId, displayName, registrationId, null, null, null, null); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStore.java similarity index 88% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStore.java index 0bdfff6eab5..edb31429601 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStore.java @@ -1,21 +1,20 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry.WorkspaceSummary; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCreationRoots.Details; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.WorkspaceSummary; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.dao.DuplicateKeyException; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.PlatformTransactionManager; @@ -46,7 +45,7 @@ public class WorkspaceCreationStore { public WorkspaceCreationStore(@Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, @Qualifier("managedAgentTransactionManager") PlatformTransactionManager manager, WorkspaceStorageGuard guard, ManagedWorkspaceRegistry registry, - ManagedAgentProperties properties, @Qualifier("managedAgentObjectMapper") ObjectMapper json, @Qualifier("managedAgentClock") Clock clock) { + ManagedAgentSettings properties, @Qualifier("managedAgentObjectMapper") ObjectMapper json, @Qualifier("managedAgentClock") Clock clock) { this.jdbc = jdbc; transaction = new TransactionTemplate(manager); this.guard = guard; @@ -82,33 +81,33 @@ public class WorkspaceCreationStore { public WorkspaceSummary create(String tenant, String actor, String displayName, String key, String sourceWorkspaceId, String cwdRelative, String localPath) { if (localPath != null && (sourceWorkspaceId != null || cwdRelative != null)) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", + throw new ManagedAgentException(400, "invalid_request", "Local path and source Workspace must not be combined."); } if (sourceWorkspaceId == null && cwdRelative != null || sourceWorkspaceId != null && cwdRelative == null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", + throw new ManagedAgentException(400, "invalid_request", "Source Workspace and directory must be supplied together."); } WorkspaceSelection selected = sourceWorkspaceId == null ? null : new WorkspaceSelection(sourceWorkspaceId, cwdRelative); if (localPath != null ? !localDirectoryImportEnabled() : selected == null ? !enabled() : !directorySelectionEnabled()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "workspace_creation_unavailable", + throw new ManagedAgentException(409, "workspace_creation_unavailable", "Workspace creation is not enabled."); } String name = displayName == null ? "" : displayName.strip(); if (name.isEmpty() || name.length() > 128 || name.codePoints().anyMatch(Character::isISOControl)) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_workspace_name", + throw new ManagedAgentException(400, "invalid_workspace_name", "Workspace name must contain 1-128 characters without control characters."); } if (key == null || !key.matches("[\\x21-\\x7e]{1,128}")) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_idempotency_key", + throw new ManagedAgentException(400, "invalid_idempotency_key", "Idempotency-Key must contain 1-128 visible characters."); } WorkspaceStorageKindGuard.requireFreshTransaction(); - byte[] actorKey = ManagedWorkspaceRegistry.actorKey(tenant, actor); + byte[] actorKey = com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenant, actor); String scope = ManagedReceipts.actorScope(actorKey); String receiptKey = ManagedSchemaKeys.receiptKey(ReceiptNamespace.WORKSPACE_CREATE, tenant, ReceiptScope.ACTOR, scope, key); @@ -158,7 +157,7 @@ public class WorkspaceCreationStore { return replay(tenant, actor, details); } if (!"PENDING".equals(receipt.state())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if (details.localRoot() != null) { details = reuseImportedRoot(tenant, actor, scope, key, details, digest); @@ -191,7 +190,7 @@ public class WorkspaceCreationStore { return; } if (!"PENDING".equals(current.state())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } long now = clock.millis(); Details completed = WorkspaceCreationRoots.details(current.resultJson(), receiptKey); @@ -199,7 +198,7 @@ public class WorkspaceCreationStore { if (completed.localRoot() != null) { Path root = Path.of(completed.localRoot().root()); if (!completed.localRoot().equals(guard.identity(root))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } localDirectories.project(root.resolve(completed.cwdRelative()).toString()); } @@ -219,7 +218,7 @@ public class WorkspaceCreationStore { public DirectoryPage localDirectories(String localPath) { if (!localDirectoryImportEnabled()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "workspace_creation_unavailable", + throw new ManagedAgentException(409, "workspace_creation_unavailable", "Local directory import is not enabled."); } return localDirectories.directories(localPath); @@ -227,7 +226,7 @@ public class WorkspaceCreationStore { public DirectoryPage directories(String tenant, String actor, String workspace, String cwd) { if (!directorySelectionEnabled()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "workspace_creation_unavailable", + throw new ManagedAgentException(409, "workspace_creation_unavailable", "Directory selection is not enabled."); } WorkspaceSelection selected = new WorkspaceSelection(workspace, cwd); @@ -256,7 +255,7 @@ public class WorkspaceCreationStore { } } } catch (IOException error) { - throw WorkspaceExecutionStore.unavailable(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(error); } guard.verify(binding); children.sort(Comparator.comparing(Directory::name)); @@ -276,7 +275,7 @@ public class WorkspaceCreationStore { + " JOIN ma_storage s ON s.tenant_id = w.tenant_id AND s.storage_id = w.storage_id" + " WHERE w.tenant_id = ? AND a.actor_id = ? AND a.role = 'OWNER'" + " AND w.state = 'ACTIVE' AND w.workspace_kind = 'ROOT' AND s.storage_kind = 'LOCAL'" - + " ORDER BY w.workspace_id", String.class, tenant, ManagedWorkspaceRegistry.actorKey(tenant, actor)); + + " ORDER BY w.workspace_id", String.class, tenant, com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenant, actor)); for (String workspace : candidates) { String storage = jdbc.queryForObject("SELECT storage_id FROM ma_workspace WHERE tenant_id = ? AND workspace_id = ?", String.class, tenant, workspace); @@ -300,7 +299,7 @@ public class WorkspaceCreationStore { Path root = Path.of(details.localRoot().root()); localDirectories.requireStorageRoot(root); if (!details.localRoot().equals(guard.identity(root))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return transaction.execute(status -> { var receipt = find(tenant, scope, key, true); @@ -323,14 +322,14 @@ public class WorkspaceCreationStore { WorkspaceSelection selected) { for (String segment : selected.cwdRelative().split("/")) { if (reserved(segment)) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_cwd", "Select a project directory."); + throw new ManagedAgentException(400, "invalid_cwd", "Select a project directory."); } } var source = registry.resolveOwnedDirectory(tenant, actor, selected.workspaceId(), selected.cwdRelative()); Path root = guard.verifiedRoot(source.binding()); Path directory = root.resolve(selected.cwdRelative()).normalize(); if (!Files.isReadable(directory) || !Files.isExecutable(directory)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return source; } @@ -340,7 +339,7 @@ public class WorkspaceCreationStore { new WorkspaceSelection(details.sourceWorkspaceId(), details.cwdRelative())); if (source.binding().getWorkspaceGeneration() != details.sourceGeneration() || !source.binding().getStorageId().equals(details.storageId())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return source; } @@ -364,7 +363,7 @@ public class WorkspaceCreationStore { private WorkspaceSummary replay(String tenant, String actor, Details details) { WorkspaceSummary found = registry.findReadable(tenant, actor, details.workspaceId()); if (found == null) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, "workspace_not_found", "Workspace not found."); + throw new ManagedAgentException(404, "workspace_not_found", "Workspace not found."); } return found; } @@ -376,7 +375,7 @@ public class WorkspaceCreationStore { private static void requireDigest(ManagedReceipts.Receipt receipt, String digest) { if (!digest.equals(receipt.requestDigest())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "idempotency_conflict", + throw new ManagedAgentException(409, "idempotency_conflict", "Idempotency key was already used for another Workspace request."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistory.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistory.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistory.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistory.java index 24219a0c298..2f47e3d50e9 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistory.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistory.java @@ -1,19 +1,22 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationInventory.Membership; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationInventory.Row; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationInventory.Snapshot; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiActivationAdmission; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement.Stage; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiExecutionAdmission; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiFilesRetirementGuard; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.ManagedCheckpointHistory; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiActivationAdmission; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement.Stage; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiExecutionAdmission; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiFilesRetirementGuard; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.transport.CsiFileFinalizeProtocol; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -38,7 +41,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationJson.require; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson.require; /** Read-only application semantics while the original DRAINING holder remains reserved. */ final class WorkspaceCsiApplicationHistory { @@ -102,7 +105,7 @@ final class WorkspaceCsiApplicationHistory { static CsiNativeActivationProof.TerminalProof preparedTerminal(Snapshot baseline, RuntimeProvisionRequest original, byte[] body) { require(baseline != null && baseline.blockers().isEmpty() && original != null - && JdbcCsiFilesRetirementGuard.isProfile(original.getScope())); + && CsiFilesRetirementProfile.isProfile(original.getScope())); baseline = WorkspaceCsiApplicationInventory.restoreCanonical(baseline.bytes()); JsonNode metadata = WorkspaceCsiApplicationJson.object(body, 128 * 1024); long expectedRevision = number(metadata.get("expectedJournalRevision")); @@ -207,7 +210,7 @@ final class WorkspaceCsiApplicationHistory { require(committed.add(row.number("journal_revision"))); } } - String sessionScope = ManagedSessionStore.sessionScopeKey(original.getScope().getTenantId(), original.getIsolationKey()); + String sessionScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(original.getScope().getTenantId(), original.getIsolationKey()); Row modelContext = WorkspaceCsiApplicationInventory.modelContextReference(baseline.table("ma_session_journal_tx").rows(), baseline.table("ma_session_record").rows(), baseline.table("ma_resource").rows(), baseline.table("ma_resource_ref").rows(), original, WorkspaceCsiApplicationImage.sole(baseline, "ma_operation").number("settlement_deadline_ms")); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryImage.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryImage.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryImage.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryImage.java index 1281befe7ca..d3b4aec214f 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryImage.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryImage.java @@ -4,7 +4,7 @@ import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationInv import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationInventory.Row; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationInventory.Snapshot; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationInventory.Table; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -12,7 +12,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationJson.require; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson.require; /** Original application image with stable shared workspace context. */ final class WorkspaceCsiApplicationHistoryImage { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationImage.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationImage.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationImage.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationImage.java index 6da3d790c93..8e283639cee 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationImage.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationImage.java @@ -1,11 +1,12 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationInventory.Membership; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationInventory.Row; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationInventory.Snapshot; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationInventory.Table; import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement.Stage; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement.Stage; import com.fasterxml.jackson.databind.JsonNode; import java.nio.charset.StandardCharsets; @@ -18,7 +19,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationJson.require; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson.require; /** Exact declared stage deltas over the once-saved SETTLING image. */ final class WorkspaceCsiApplicationImage { @@ -90,10 +91,10 @@ final class WorkspaceCsiApplicationImage { byte[] resourceBytes = WorkspaceCsiApplicationJson.base64(resource.path("bytesBase64"), 16 * 1024); Map values = empty(baseline, "ma_resource"); scope(values, head); - put(values, "session_scope_key", ManagedSessionStore.sessionScopeKey(head.text("tenant_id"), head.text("session_id"))); + put(values, "session_scope_key", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(head.text("tenant_id"), head.text("session_id"))); put(values, "resource_id", text(resource, "resourceId")); - String resourceScope = ManagedSessionStore.sessionScopeKey(head.text("tenant_id"), head.text("session_id")); - String resourceKey = ManagedSessionStore.resourceKey(resourceScope, text(resource, "resourceId")); + String resourceScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(head.text("tenant_id"), head.text("session_id")); + String resourceKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(resourceScope, text(resource, "resourceId")); put(values, "resource_key", resourceKey); put(values, "kind", text(resource, "kind")); put(values, "schema_version", resource.path("schemaVersion").intValue()); @@ -112,7 +113,7 @@ final class WorkspaceCsiApplicationImage { put(values, "owner_key", resourceScope); put(values, "owner_revision", revision); put(values, "resource_key", resourceKey); - put(values, "session_scope_key", ManagedSessionStore.sessionScopeKey(head.text("tenant_id"), head.text("session_id"))); + put(values, "session_scope_key", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(head.text("tenant_id"), head.text("session_id"))); put(values, "journal_revision", revision); put(values, "resource_id", text(resource, "resourceId")); put(values, "gmt_create", writtenAtMillis); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventory.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventory.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventory.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventory.java index eff666b1fca..243b4c55ac0 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventory.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventory.java @@ -1,13 +1,19 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.catalog.AgentDefinitionExtensions; +import com.alibaba.qwen.code.managedagent.contracts.catalog.AgentDefinitionExtensions; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; import com.alibaba.qwen.code.runtimebroker.CsiOwnershipRevision; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiFilesRetirementGuard; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiFilesRetirementGuard; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.StreamReadConstraints; @@ -638,7 +644,7 @@ final class WorkspaceCsiApplicationInventory { requireEncoded(saved.number("record_version") != null && saved.number("record_version") >= intent.sealedBindingVersion() && Objects.equals(saved.number("first_activation_journal_revision"), 2L) && actual.version() == saved.number("record_version")); - WorkspaceCsiApplicationImage.expected(baseline, com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement.Stage.SETTLING, + WorkspaceCsiApplicationImage.expected(baseline, com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement.Stage.SETTLING, null, null, null); // Only the saved-row validators run here; no discover, membership lookup or SQL is invoked. final Inspection validation = new Inspection(null, actual, intent); @@ -687,9 +693,9 @@ final class WorkspaceCsiApplicationInventory { String session = actual.request().getIsolationKey(); String tenant = actual.request().getScope().getTenantId(); Set scopes = new LinkedHashSet<>(); - scopes.add(ManagedSessionStore.sessionScopeKey(tenant, session)); + scopes.add(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session)); scopes.add(ManagedToolResultStore.scope(tenant, session)); - scopes.add(com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("csi-read", tenant, session)); + scopes.add(com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("csi-read", tenant, session)); try { scopes.add(hash(JSON.writeValueAsBytes(List.of(tenant, actual.request().getScope().getWorkspaceId(), session)))); } catch (IOException encoding) { @@ -761,9 +767,9 @@ final class WorkspaceCsiApplicationInventory { for (String field : List.of("tenant_id", "workspace_id", "session_id", "session_scope_key")) { requireEncoded(Objects.equals(manifest.text(field), part.text(field))); } - requireEncoded(ManagedSessionStore.sessionScopeKey(part.text("tenant_id"), part.text("session_id")) + requireEncoded(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(part.text("tenant_id"), part.text("session_id")) .equals(part.text("session_scope_key")) - && ManagedSessionStore.resourceKey(part.text("session_scope_key"), part.text("resource_id")).equals(part.text("resource_key")) + && com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(part.text("session_scope_key"), part.text("resource_id")).equals(part.text("resource_key")) && Objects.equals(part.text("kind"), ref.path("kind").textValue()) && Objects.equals(part.number("schema_version"), ref.path("schemaVersion").longValue()) && Objects.equals(part.number("byte_length"), ref.path("byteLength").longValue()) @@ -798,7 +804,7 @@ final class WorkspaceCsiApplicationInventory { String tenant = original.getScope().getTenantId(); String workspace = original.getScope().getWorkspaceId(); String session = original.getIsolationKey(); - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String key = ManagedExtensionProjection.recordKey(session, "hosted_context_usage", "latest"); for (Row transaction : transactions.stream().filter(row -> row.membership() == Membership.ORIGINAL) .sorted(java.util.Comparator.comparingLong(row -> row.number("journal_revision"))).toList()) { @@ -867,7 +873,7 @@ final class WorkspaceCsiApplicationInventory { CATALOGUE.stream().filter(table -> table.name().equals("ma_resource_ref")).findFirst().orElseThrow() .columns().forEach(column -> values.put(column.name(), null)); Map.ofEntries(Map.entry("session_scope_key", scope), Map.entry("owner_kind", "SESSION_RECORD"), Map.entry("owner_key", key), - Map.entry("owner_revision", Long.toString(revision)), Map.entry("resource_key", ManagedSessionStore.resourceKey(scope, resourceId)), + Map.entry("owner_revision", Long.toString(revision)), Map.entry("resource_key", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, resourceId)), Map.entry("tenant_id", tenant), Map.entry("workspace_id", workspace), Map.entry("session_id", session), Map.entry("resource_id", resourceId), Map.entry("gmt_create", ref.text("gmt_create"))) .forEach((field, value) -> values.put(field, utf8(value))); @@ -922,14 +928,14 @@ final class WorkspaceCsiApplicationInventory { && read.number("delivery_expires_at") > read.number("started_at")); var initial = evidence.deepCopy(); ((com.fasterxml.jackson.databind.node.ObjectNode) initial).remove(List.of("ended_at", "outcome_code")); - requireEncoded(com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("csi-read", id).equals(row.text("receipt_key")) - && com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(read.text("tenant_id")).equals(row.text("tenant_key")) + requireEncoded(com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("csi-read", id).equals(row.text("receipt_key")) + && com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(read.text("tenant_id")).equals(row.text("tenant_key")) && read.text("tenant_id").equals(row.text("tenant_id")) && "SESSION".equals(row.text("scope_kind")) - && com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("csi-read", read.text("tenant_id"), + && com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("csi-read", read.text("tenant_id"), read.text("session_id")).equals(row.text("scope_key")) && read.text("session_id").equals(row.text("scope_id")) && read.text("session_id").equals(row.text("session_id")) && id.equals(row.text("idempotency_key")) - && com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(id).equals(row.text("idempotency_digest")) + && com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(id).equals(row.text("idempotency_digest")) && hash(utf8(initial.toString())).equals(row.text("request_digest")) && Objects.equals(read.number("started_at"), row.number("gmt_create")) && row.number("gmt_modified") != null && row.number("gmt_modified") >= read.number("started_at")); @@ -1306,7 +1312,7 @@ final class WorkspaceCsiApplicationInventory { private List executions(JdbcCsiFilesRetirementGuard.Original original) throws SQLException { requireEncoded(original != null && original.request() != null - && JdbcCsiFilesRetirementGuard.isProfile(original.request().getScope())); + && CsiFilesRetirementProfile.isProfile(original.request().getScope())); var declaration = CATALOGUE.stream().filter(table -> table.name().equals("ma_tool_execution")).findFirst().orElseThrow(); var current = query(declaration, "binding_id = ? OR harness_session_id = ? OR runtime_session_id = ?", List.of(new Cell(Kind.VARBINARY, utf8(original.bindingId())), @@ -1367,7 +1373,7 @@ final class WorkspaceCsiApplicationInventory { } if (decodeMembers && !nativeMembers.containsKey(key(row))) { try { - nativeMembers.put(key(row), com.alibaba.qwen.code.runtimebroker.JdbcCsiExecutionAdmission.readHistoricalMember(rows)); + nativeMembers.put(key(row), com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiExecutionAdmission.readHistoricalMember(rows)); } catch (RuntimeException malformed) { block(declaration.name(), row, "invalid_historical_execution"); } @@ -1492,9 +1498,9 @@ final class WorkspaceCsiApplicationInventory { } private static void resourceMetadata(Row row) { - requireEncoded(ManagedSessionStore.sessionScopeKey(row.text("tenant_id"), row.text("session_id")) + requireEncoded(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(row.text("tenant_id"), row.text("session_id")) .equals(row.text("session_scope_key")) - && ManagedSessionStore.resourceKey(row.text("session_scope_key"), row.text("resource_id")).equals(row.text("resource_key")) + && com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(row.text("session_scope_key"), row.text("resource_id")).equals(row.text("resource_key")) && Objects.equals(row.number("schema_version"), 1L) && "MYSQL_INLINE".equals(row.text("storage_kind")) && row.text("object_key") == null && row.text("object_version_id") == null && row.text("encryption_key_id") == null @@ -1616,7 +1622,7 @@ final class WorkspaceCsiApplicationInventory { private static long bodyRevision(Row body, String item, long revision) { String[] id = body.text("resource_id").split(":", -1); requireEncoded(id.length == 4 && "message".equals(id[0]) - && com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(body.text("session_scope_key"), item).equals(id[1]) + && com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(body.text("session_scope_key"), item).equals(id[1]) && id[2].matches("[1-9][0-9]{0,18}") && body.text("sha256").equals(id[3])); long original = Long.parseLong(id[2]); requireEncoded(original > 0 && original <= revision); @@ -1641,7 +1647,7 @@ final class WorkspaceCsiApplicationInventory { requireEncoded(Objects.equals(row.text("tenant_id"), message.text("tenant_id")) && Objects.equals(row.text("session_id"), message.text("session_id")) && "[]".equals(message.text("parts_json"))); long revision = bodyRevision(row, message.text("item_id"), message.number("revision")); - String owner = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(row.text("session_scope_key"), message.text("item_id")); + String owner = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(row.text("session_scope_key"), message.text("item_id")); partCoverage(parts, message.number("first_sequence"), message.number("last_sequence"), message.number("gmt_create"), message.number("gmt_modified")); int current = 0; @@ -1683,9 +1689,9 @@ final class WorkspaceCsiApplicationInventory { requireEncoded(Objects.equals(owner.text("tenant_id"), row.text("tenant_id")) && Objects.equals(owner.text("workspace_id"), row.text("workspace_id")) && Objects.equals(owner.text("session_id"), row.text("session_id")) - && ManagedSessionStore.sessionScopeKey(row.text("tenant_id"), row.text("session_id")) + && com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(row.text("tenant_id"), row.text("session_id")) .equals(row.text("session_scope_key")) - && ManagedSessionStore.resourceKey(row.text("session_scope_key"), row.text("resource_id")) + && com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(row.text("session_scope_key"), row.text("resource_id")) .equals(row.text("resource_key")) && Objects.equals(1L, row.number("schema_version")) && row.cell("object_key").isNull() && row.cell("object_version_id").isNull() @@ -1703,7 +1709,7 @@ final class WorkspaceCsiApplicationInventory { && row.number("byte_length") <= 16 * 1024 * 1024 && row.text("sha256") != null && row.text("sha256").matches("[0-9a-f]{64}") && Objects.equals(row.text("resource_id"), "managed-tool-segment".equals(kind) - ? "capture-segment-" + com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + ? "capture-segment-" + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( row.text("scope_key"), row.text("publication_id"), slot) : "result-" + hash(utf8(row.text("scope_key") + ":" + row.text("publication_id") + ":" + slot)).substring(0, 32))); if (row.text("capture_object_key") != null) { @@ -2043,7 +2049,7 @@ final class WorkspaceCsiApplicationInventory { seed("runtime_session_id", intent.runtimeInstanceId()); id("A", intent.reservation().registrationKey()); id("F", intent.physicalKey()); - id("J", ManagedSessionStore.sessionScopeKey(tenant, session)); + id("J", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session)); id("M", digest(tenant, session)); try { id("U", hash(JSON.writeValueAsBytes(List.of(tenant, workspace, session)))); @@ -2302,11 +2308,11 @@ final class WorkspaceCsiApplicationInventory { "ma_session_record", "ma_session_journal_tx", "ma_channel_binding", "ma_schedule", "ma_schedule_occurrence" -> otherSession(row.text("tenant_id"), row.text("workspace_id"), row.text("session_id")) && (row.cell("session_scope_key") == null || Objects.equals(row.text("session_scope_key"), - ManagedSessionStore.sessionScopeKey(row.text("tenant_id"), row.text("session_id")))); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(row.text("tenant_id"), row.text("session_id")))); case "ma_resource" -> otherSession(row.text("tenant_id"), row.text("workspace_id"), row.text("session_id")) - && ManagedSessionStore.sessionScopeKey(row.text("tenant_id"), row.text("session_id")) + && com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(row.text("tenant_id"), row.text("session_id")) .equals(row.text("session_scope_key")) - && ManagedSessionStore.resourceKey(row.text("session_scope_key"), row.text("resource_id")) + && com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(row.text("session_scope_key"), row.text("resource_id")) .equals(row.text("resource_key")); case "ma_request_receipt" -> isNativeRead(row) ? otherNativeRead(row) : otherCreationReceipt(row); case "ma_output_capture" -> otherCapture(row); @@ -2351,34 +2357,34 @@ final class WorkspaceCsiApplicationInventory { } private boolean otherCreationReceipt(Row row) throws SQLException { - var namespace = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace.valueOf(row.text("namespace")); - var scopeKind = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope.valueOf(row.text("scope_kind")); + var namespace = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace.valueOf(row.text("namespace")); + var scopeKind = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope.valueOf(row.text("scope_kind")); String ownerTenant = row.text("tenant_id"); String scopeId = row.text("scope_id"); String idempotency = row.text("idempotency_key"); if (ownerTenant == null || scopeId == null || idempotency == null || row.text("request_digest") == null - || !com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.receiptKey( + || !com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.receiptKey( namespace, ownerTenant, scopeKind, scopeId, idempotency).equals(row.text("receipt_key")) - || !com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(ownerTenant).equals(row.text("tenant_key")) + || !com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(ownerTenant).equals(row.text("tenant_key")) || !ManagedReceipts.scope(ownerTenant, scopeId).equals(row.text("scope_key")) - || !com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(idempotency).equals(row.text("idempotency_digest"))) { + || !com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(idempotency).equals(row.text("idempotency_digest"))) { return false; } - if (namespace == com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace.WORKSPACE_CREATE) { + if (namespace == com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace.WORKSPACE_CREATE) { return otherWorkspaceCreationReceipt(row); } if (!row.cell("operation").isNull() || !row.cell("session_status_before").isNull()) { return false; } - if (namespace == com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_MODE) { + if (namespace == com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_MODE) { Boolean bound = row.bool("workspace_bound"); - return scopeKind == com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope.TENANT + return scopeKind == com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope.TENANT && ownerTenant.equals(scopeId) && bound != null && "RESERVED".equals(row.text("state")) && row.cell("session_id").isNull() && row.cell("turn_id").isNull() && row.cell("actor_id").isNull() - && com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(bound.toString()).equals(row.text("request_digest")); + && com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(bound.toString()).equals(row.text("request_digest")); } - if (namespace != com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_ACTOR - || scopeKind != com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope.ACTOR + if (namespace != com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_ACTOR + || scopeKind != com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope.ACTOR || !Boolean.TRUE.equals(row.bool("workspace_bound")) || !"COMPLETED".equals(row.text("state")) || row.binary("actor_id") == null || !ManagedReceipts.actorScope(row.binary("actor_id")).equals(scopeId) || !otherSession(ownerTenant, null, row.text("session_id"))) { @@ -2433,7 +2439,7 @@ final class WorkspaceCsiApplicationInventory { || !("created-" + row.text("receipt_key")).equals(otherStorage) || name.isEmpty() || name.length() > 128 || !name.equals(name.strip()) || name.codePoints().anyMatch(Character::isISOControl) - || !com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(name).equals(row.text("request_digest")) + || !com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(name).equals(row.text("request_digest")) || !java.util.UUID.fromString(registration).toString().equals(registration) || tenant.equals(row.text("tenant_id")) && (workspace.equals(otherWorkspace) || storage.equals(otherStorage))) { return false; @@ -2498,11 +2504,11 @@ final class WorkspaceCsiApplicationInventory { && Objects.equals(binding.number("runtime_generation"), row.number("target_revision")); } if (otherSession == null || !otherSession(otherTenant, row.text("workspace_id"), otherSession) - || !ManagedSessionStore.sessionScopeKey(otherTenant, otherSession).equals(row.text("session_scope_key"))) { + || !com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(otherTenant, otherSession).equals(row.text("session_scope_key"))) { return false; } - var kind = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind.valueOf(row.text("operation_kind")); - return com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.operationKey(kind, + var kind = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind.valueOf(row.text("operation_kind")); + return com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.operationKey(kind, row.text("scope_key"), row.text("target_key"), otherId).equals(row.text("operation_key")); } @@ -2541,8 +2547,8 @@ final class WorkspaceCsiApplicationInventory { references.put("provision", Set.of(intent.reservation().provisionRequestId())); references.put("execution", values("executions")); references.put("executionHash", values("executionHashes")); - references.put("scopedSession", Set.copyOf(List.of(ManagedSessionStore.sessionScopeKey(tenant, session), ManagedToolResultStore.scope(tenant, session), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("csi-read", tenant, session)))); + references.put("scopedSession", Set.copyOf(List.of(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), ManagedToolResultStore.scope(tenant, session), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("csi-read", tenant, session)))); references.put("sessionKey", Set.of(ToolPublicationRetentionStore.hash(session))); references.put("resourceRead", values("reads")); references.put("operationKey", values("operationKeys")); @@ -2972,7 +2978,7 @@ final class WorkspaceCsiApplicationInventory { && Objects.equals((long) row.binary("record_bytes").length, row.number("byte_length")) && hash(row.binary("record_bytes")).equals(row.text("record_digest"))); case "ma_resource" -> expect(table, row, rawNative(row) - && ManagedSessionStore.resourceKey(ManagedSessionStore.sessionScopeKey(tenant, session), row.text("resource_id")).equals(row.text("resource_key")) + && com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), row.text("resource_id")).equals(row.text("resource_key")) && row.cell("deletion_operation_key").isNull() && row.cell("scope_key").isNull() && row.cell("publication_id").isNull() && row.cell("capture_state").isNull() && has("J", row.text("session_scope_key")) @@ -2988,7 +2994,7 @@ final class WorkspaceCsiApplicationInventory { && ("JOURNAL".equals(row.text("owner_kind")) && has("J", row.text("owner_key")) && Objects.equals(row.number("owner_revision"), row.number("journal_revision")) || "SESSION_RECORD".equals(row.text("owner_kind")) && row.cell("journal_revision").isNull()) - && ManagedSessionStore.resourceKey(ManagedSessionStore.sessionScopeKey(tenant, session), row.text("resource_id")).equals(row.text("resource_key"))); + && com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), row.text("resource_id")).equals(row.text("resource_key"))); case "ma_tool_execution" -> expect(table, row, original.bindingId().equals(row.text("binding_id")) && Objects.equals(original.generation(), row.number("runtime_generation")) && session.equals(row.text("harness_session_id")) && session.equals(row.text("runtime_session_id")) @@ -3025,25 +3031,25 @@ final class WorkspaceCsiApplicationInventory { && read.number("ended_at") < read.number("delivery_expires_at")); break; } - var namespace = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace.valueOf(row.text("namespace")); - var scopeKind = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope.valueOf(row.text("scope_kind")); + var namespace = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace.valueOf(row.text("namespace")); + var scopeKind = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope.valueOf(row.text("scope_kind")); String scopeId = row.text("scope_id"); String idempotency = row.text("idempotency_key"); expect(table, row, tenant.equals(row.text("tenant_id")) && has("RT", row.text("tenant_key")) - && com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.receiptKey(namespace, tenant, scopeKind, scopeId, idempotency).equals(row.text("receipt_key")) + && com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.receiptKey(namespace, tenant, scopeKind, scopeId, idempotency).equals(row.text("receipt_key")) && ManagedReceipts.scope(tenant, scopeId).equals(row.text("scope_key")) - && com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(idempotency).equals(row.text("idempotency_digest")) + && com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(idempotency).equals(row.text("idempotency_digest")) && row.cell("turn_id").isNull() && row.cell("operation").isNull() && row.cell("session_status_before").isNull() && Boolean.TRUE.equals(row.bool("workspace_bound")) && row.text("request_digest") != null); - if (namespace == com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_MODE) { - expect(table, row, scopeKind == com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope.TENANT + if (namespace == com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_MODE) { + expect(table, row, scopeKind == com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope.TENANT && tenant.equals(scopeId) && has("creation", idempotency) && row.cell("session_id").isNull() && row.cell("actor_id").isNull() && "RESERVED".equals(row.text("state")) - && com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("true").equals(row.text("request_digest"))); - } else if (namespace == com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_ACTOR) { + && com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("true").equals(row.text("request_digest"))); + } else if (namespace == com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_ACTOR) { Row parent = originalRow("ma_session"); expect(table, row, parent != null && rawSession(row) && "COMPLETED".equals(row.text("state")) - && scopeKind == com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope.ACTOR + && scopeKind == com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope.ACTOR && Arrays.equals(parent.binary("creator_actor_key"), row.binary("actor_id")) && ManagedReceipts.actorScope(row.binary("actor_id")).equals(scopeId) && Objects.equals(parent.number("gmt_create"), row.number("gmt_create"))); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationStore.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationStore.java index 453b8f0a79e..14b09b61659 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationStore.java @@ -1,19 +1,19 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationInventory.Snapshot; import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement.Authority; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement.Stage; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement.Authority; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement.Stage; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.transport.CsiFileFinalizeProtocol; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.jdbc.core.ConnectionCallback; @@ -38,7 +38,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationJson.require; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson.require; /** Original Session application retirement; RPCs never hold SQL locks. */ public final class WorkspaceCsiApplicationStore { @@ -59,11 +59,6 @@ public final class WorkspaceCsiApplicationStore { private final WorkspaceCsiApplicationHistory history; public WorkspaceCsiApplicationStore(JdbcTemplate jdbc, PlatformTransactionManager manager, - ManagedSessionStore nativeStore, JdbcRuntimeBindingRepository bindings, JdbcRuntimeSessionRepository sessions) { - this(jdbc, manager, nativeStore, bindings, sessions, new HttpRuntimeTransport()); - } - - WorkspaceCsiApplicationStore(JdbcTemplate jdbc, PlatformTransactionManager manager, ManagedSessionStore nativeStore, JdbcRuntimeBindingRepository bindings, JdbcRuntimeSessionRepository sessions, RuntimeTransport transport) { require(jdbc != null && jdbc.getDataSource() != null && nativeStore != null && bindings != null && sessions != null diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationWorkerMatch.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationWorkerMatch.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationWorkerMatch.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationWorkerMatch.java index 260126b4340..a26c3f75c37 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationWorkerMatch.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationWorkerMatch.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson; import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; import com.alibaba.qwen.code.runtimebroker.transport.CsiFileFinalizeProtocol; import com.fasterxml.jackson.databind.JsonNode; @@ -11,8 +12,8 @@ import java.util.List; import java.util.Map; import java.util.Set; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationJson.closed; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationJson.require; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson.closed; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson.require; final class WorkspaceCsiApplicationWorkerMatch { private WorkspaceCsiApplicationWorkerMatch() { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStore.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStore.java index d4b64849391..2e06f9eab19 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStore.java @@ -1,10 +1,13 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; @@ -144,7 +147,7 @@ public final class WorkspaceCsiCheckpointSnapshotStore { && number(head, "compacted_through_revision") == 0 && number(head, "activation_epoch") == binding.path("activationEpoch").longValue()); ArrayNode transactions = transactions(connection, key, head); - String sessionScope = ManagedSessionStore.sessionScopeKey(key.path("tenantId").asText(), key.path("sessionId").asText()); + String sessionScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(key.path("tenantId").asText(), key.path("sessionId").asText()); ArrayNode resources = resources(connection, key, sessionScope, number(head, "journal_revision")); long revision = number(publication, "receipt_revision"); long sequence = number(publication, "receipt_sequence"); @@ -467,7 +470,7 @@ public final class WorkspaceCsiCheckpointSnapshotStore { int type = h2 ? statement.getParameterMetaData().getParameterType(index + 1) : Types.OTHER; if (parameters[index] instanceof String value && Set.of(Types.BINARY, Types.VARBINARY, Types.LONGVARBINARY).contains(type)) { - statement.setBytes(index + 1, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.identity(value)); + statement.setBytes(index + 1, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.identity(value)); } else { statement.setObject(index + 1, parameters[index]); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStore.java similarity index 80% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStore.java index 9c07217a6c8..7132c85d8d1 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStore.java @@ -1,14 +1,16 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.runtimebroker.CsiOwnershipRevision; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationProgress; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiFilesRetirementGuard; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.ResidentWorkspaceProfile; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationProgress; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiFilesRetirementGuard; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -29,17 +31,12 @@ import java.util.Objects; import java.util.UUID; /** Private persistence only; a reservation does not authorize a mount. */ -public final class WorkspaceCsiReservationStore { +public final class WorkspaceCsiReservationStore implements com.alibaba.qwen.code.managedagent.repository.WorkspaceCsiReservations { public static final String PROVISIONER_KIND = "kubernetes-workspace"; private final JdbcTemplate jdbc; private final TransactionTemplate transaction; private final ObjectMapper json; - public record Reservation(String phase, long revision, String reservationId, - String registrationKey, Long registrationRevision, String bindingId, - Long runtimeGeneration, String provisionRequestId) { - } - public record Retirement(String operationId, String physicalKey, String phase, Reservation reservation, long sealedBindingVersion, String resourceHandleKind, Integer resourceHandleVersion, String resourceHandleJson, String runtimeInstanceId, @@ -94,76 +91,122 @@ public final class WorkspaceCsiReservationStore { } public Reservation inspect(WorkspaceCsiRegistration registration) { - return transaction.execute(status -> { - verifyRegistration(registration); - var reservation = readReservation(registration.physicalKey()); - requireDataTenant(registration); - return reservation; - }); + try { + return transaction.execute( + status -> { + verifyRegistration(registration); + var reservation = readReservation(registration.physicalKey()); + requireDataTenant(registration); + return reservation; + }); + } catch (org.springframework.dao.DataAccessException + | org.springframework.transaction.TransactionException error) { + throw new com.alibaba.qwen.code.runtimebroker.StoreFailureException("CSI reservation storage is unavailable", error); + } } public Reservation reserve(WorkspaceCsiRegistration registration, - JdbcRuntimeBindingRepository bindings, RuntimeBindingRecord original, String reservationId) { - if (bindings == null || original == null || original.getState() != RuntimeBindingRecord.State.PROVISIONING - || !original.getRequest().isManagedContext() || original.getProvisionSeed() == null - || !PROVISIONER_KIND.equals(original.getRequest().getProvisionerKind()) - || original.getOperationOwner() == null || original.isDrainRequested() - || !uuid(reservationId)) { - throw unavailable(); + com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository repository, RuntimeBindingRecord original, String reservationId) { + try { + if (!(repository instanceof JdbcRuntimeBindingRepository bindings) + || !bindings.usesDataSource(jdbc.getDataSource())) { + throw unavailable(); + } + + if (bindings == null + || original == null + || original.getState() != RuntimeBindingRecord.State.PROVISIONING + || !original.getRequest().isManagedContext() + || original.getProvisionSeed() == null + || !PROVISIONER_KIND.equals(original.getRequest().getProvisionerKind()) + || original.getOperationOwner() == null + || original.isDrainRequested() + || !uuid(reservationId)) { + throw unavailable(); + } + return transaction.execute( + status -> { + RuntimeBindingRecord authoritative; + try { + authoritative = + jdbc.execute( + (ConnectionCallback) + connection -> + bindings.findByIdForUpdate( + connection, + original.getBindingId())); + } catch (IllegalArgumentException | IllegalStateException error) { + throw unavailable(); + } + Instant operationUntil = + verifyBinding(registration, original, authoritative); + verifyRegistration(registration); + Reservation current = readReservation(registration.physicalKey()); + Instant now = + jdbc.queryForObject( + "SELECT UNIX_TIMESTAMP()," + + " EXTRACT(MICROSECOND FROM CURRENT_TIMESTAMP(6))", + (row, index) -> + Instant.ofEpochSecond( + row.getLong(1), row.getLong(2) * 1000)); + if (now == null || !operationUntil.isAfter(now)) { + throw unavailable(); + } + String requestId = original.getProvisionSeed().getProvisionRequestId(); + if ("RESERVED".equals(current.phase())) { + if (!reservationId.equals(current.reservationId()) + || !registration.aliasKey().equals(current.registrationKey()) + || !Objects.equals( + registration.revision(), current.registrationRevision()) + || !original.getBindingId().equals(current.bindingId()) + || !Objects.equals( + original.getGeneration(), current.runtimeGeneration()) + || !requestId.equals(current.provisionRequestId())) { + throw busy(); + } + return current; + } + if (!"RELEASED".equals(current.phase())) { + throw busy(); + } + requireDataTenant(registration); + long revision; + try { + revision = + ResidentWorkspaceProfile.selects( + original.getRequest().getScope()) + ? Math.addExact(current.revision(), 1) + : nextRevision(current.revision()); + } catch (ArithmeticException error) { + throw unavailable(); + } + int changed = + jdbc.update( + "UPDATE ma_storage_owner" + + " SET csi_phase = 'RESERVED', csi_revision = ?," + + " csi_reservation_id = ?, csi_registration_key = ?, csi_registration_revision = ?," + + " binding_id = ?, runtime_generation = ?, csi_provision_request_id = ?, tenant_id = ?" + + " WHERE storage_key = ? AND storage_kind = 'CSI' AND csi_phase = 'RELEASED'" + + " AND csi_revision = ? AND holder_key IS NULL AND runtime_session_id IS NULL", + revision, + reservationId, + registration.aliasKey(), + registration.revision(), + original.getBindingId(), + original.getGeneration(), + requestId, + registration.tenantId(), + registration.physicalKey(), + current.revision()); + if (changed != 1) { + throw unavailable(); + } + return readReservation(registration.physicalKey()); + }); + } catch (org.springframework.dao.DataAccessException + | org.springframework.transaction.TransactionException error) { + throw new com.alibaba.qwen.code.runtimebroker.StoreFailureException("CSI reservation storage is unavailable", error); } - return transaction.execute(status -> { - RuntimeBindingRecord authoritative; - try { - authoritative = jdbc.execute((ConnectionCallback) connection -> - bindings.findByIdForUpdate(connection, original.getBindingId())); - } catch (IllegalArgumentException | IllegalStateException error) { - throw unavailable(); - } - Instant operationUntil = verifyBinding(registration, original, authoritative); - verifyRegistration(registration); - Reservation current = readReservation(registration.physicalKey()); - Instant now = jdbc.queryForObject("SELECT UNIX_TIMESTAMP()," - + " EXTRACT(MICROSECOND FROM CURRENT_TIMESTAMP(6))", (row, index) -> - Instant.ofEpochSecond(row.getLong(1), row.getLong(2) * 1000)); - if (now == null || !operationUntil.isAfter(now)) { - throw unavailable(); - } - String requestId = original.getProvisionSeed().getProvisionRequestId(); - if ("RESERVED".equals(current.phase())) { - if (!reservationId.equals(current.reservationId()) - || !registration.aliasKey().equals(current.registrationKey()) - || !Objects.equals(registration.revision(), current.registrationRevision()) - || !original.getBindingId().equals(current.bindingId()) - || !Objects.equals(original.getGeneration(), current.runtimeGeneration()) - || !requestId.equals(current.provisionRequestId())) { - throw busy(); - } - return current; - } - if (!"RELEASED".equals(current.phase())) { - throw busy(); - } - requireDataTenant(registration); - long revision; - try { - revision = ResidentWorkspaceProfile.selects(original.getRequest().getScope()) - ? Math.addExact(current.revision(), 1) : nextRevision(current.revision()); - } catch (ArithmeticException error) { - throw unavailable(); - } - int changed = jdbc.update("UPDATE ma_storage_owner" - + " SET csi_phase = 'RESERVED', csi_revision = ?," - + " csi_reservation_id = ?, csi_registration_key = ?, csi_registration_revision = ?," - + " binding_id = ?, runtime_generation = ?, csi_provision_request_id = ?, tenant_id = ?" - + " WHERE storage_key = ? AND storage_kind = 'CSI' AND csi_phase = 'RELEASED'" - + " AND csi_revision = ? AND holder_key IS NULL AND runtime_session_id IS NULL", - revision, reservationId, registration.aliasKey(), registration.revision(), original.getBindingId(), - original.getGeneration(), requestId, registration.tenantId(), registration.physicalKey(), current.revision()); - if (changed != 1) { - throw unavailable(); - } - return readReservation(registration.physicalKey()); - }); } public Retirement beginRetirement(WorkspaceCsiRegistration registration, @@ -632,4 +675,15 @@ public final class WorkspaceCsiReservationStore { private static RuntimeBrokerException busy() { return new RuntimeBrokerException(409, "workspace_csi_busy", "Workspace storage is reserved.", true); } + public boolean usesSameDataSource(com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository repository) { + return repository instanceof JdbcRuntimeBindingRepository bindings && bindings.usesDataSource(jdbc.getDataSource()); + } + public void requireRegistration(com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.RuntimeBroker.ResidentWorkspace selected) { + try { + ResidentWorkspaces.requireRegistration(jdbc, selected); + } catch (org.springframework.dao.DataAccessException + | org.springframework.transaction.TransactionException error) { + throw new com.alibaba.qwen.code.runtimebroker.StoreFailureException("CSI reservation storage is unavailable", error); + } + } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeAccess.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeAccess.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeAccess.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeAccess.java index 2fa7ac7b39f..e4e827efe9e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeAccess.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeAccess.java @@ -1,13 +1,8 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.runtimebroker.HarnessSessionResolver; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiActivationAdmission; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationProgress; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiExecutionAdmission; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiFilesRetirementGuard; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeAttestation; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; @@ -20,6 +15,12 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiActivationAdmission; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationProgress; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiExecutionAdmission; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiFilesRetirementGuard; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.transport.CsiFileHistoryProtocol; import com.alibaba.qwen.code.runtimebroker.transport.CsiNativeReadbackProtocol; import com.fasterxml.jackson.core.JsonProcessingException; @@ -57,9 +58,9 @@ public final class WorkspaceCsiRuntimeAccess implements HarnessSessionResolver, private final RuntimeTransport delegate; public WorkspaceCsiRuntimeAccess(JdbcTemplate jdbc, DataSourceTransactionManager manager, - ObjectMapper json, ManagedAgentProperties properties, WorkspaceCsiRegistration registration, + ObjectMapper json, ManagedAgentSettings properties, WorkspaceCsiRegistration registration, String sessionId, String requestKey, JdbcRuntimeBindingRepository bindings, - JdbcRuntimeSessionRepository sessions, RuntimeTransport delegate) { + JdbcRuntimeSessionRepository sessions, RuntimeTransport delegate, com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRules harnessRules) { if (jdbc == null || jdbc.getDataSource() == null || manager == null || manager.getDataSource() != jdbc.getDataSource() || bindings == null || sessions == null || !bindings.usesDataSource(jdbc.getDataSource()) || !sessions.usesDataSource(jdbc.getDataSource()) @@ -71,7 +72,7 @@ public final class WorkspaceCsiRuntimeAccess implements HarnessSessionResolver, this.transaction = new TransactionTemplate(manager); this.transaction.setTimeout(10); this.managed = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + new ManagedWorkspaceRegistry(jdbc), properties, harnessRules); this.storage = new WorkspaceCsiReservationStore(jdbc, manager, json); this.registration = Objects.requireNonNull(registration); this.sessionId = sessionId; diff --git a/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionCommands.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionCommands.java new file mode 100644 index 00000000000..ad6105ea644 --- /dev/null +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionCommands.java @@ -0,0 +1,48 @@ +package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Created; +import com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request; +import com.alibaba.qwen.code.managedagent.model.RequestDigests; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.jdbc.core.ConnectionCallback; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceUtils; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.transaction.support.TransactionTemplate; + +import java.time.Clock; +import java.util.Map; +public final class WorkspaceCsiSessionCommands { + private WorkspaceCsiSessionCommands() { } + public static Created create(JdbcTemplate jdbc, PlatformTransactionManager manager, + ObjectMapper json, com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings properties, Request request, com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRules harnessRules) { + if (TransactionSynchronizationManager.isActualTransactionActive()) { + throw new IllegalStateException("Private CSI CREATE requires a fresh transaction"); + } + var transaction = new TransactionTemplate(manager); + transaction.setTimeout(10); + var reservations = new WorkspaceCsiReservationStore(jdbc, manager, json); + var sessions = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, harnessRules); + String digest = new RequestDigests().digest(Map.of("profile", CsiFilesRetirementProfile.PROFILE, + "request", request)); + return transaction.execute(status -> { + jdbc.execute((ConnectionCallback) connection -> { + if (connection.getAutoCommit() || !DataSourceUtils.isConnectionTransactional( + DataSourceUtils.getTargetConnection(connection), jdbc.getDataSource())) { + throw new IllegalStateException("Private CSI CREATE requires its original transaction connection"); + } + JdbcRuntimeBindingRepository.lockPlacementDomain(connection, request.registration().tenantId(), 10); + return null; + }); + ToolPublicationRetentionStore.lockTenant(jdbc, request.registration().tenantId()); + reservations.verifyRegistration(request.registration()); + var admission = sessions.insertCsiSessionCommand(request.registration(), request.actorId(), + request.idempotencyKey(), digest, request.requestedRevision(), request.title(), request.workspace()); + var original = sessions.requireCsiRequest(request.registration(), admission.sessionId()); + return new Created(admission.sessionId(), original.requestKey(), admission.replayed()); + }); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStore.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStore.java index 7aedaad4bda..422bc7660de 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStore.java @@ -1,13 +1,14 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiProtocol; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; @@ -36,7 +37,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; -import static com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract.require; +import static com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract.require; /** Confirms one original receipt; it cannot authorize physical retirement. */ public final class WorkspaceCsiWorkerAckStore { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceEvidenceResources.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceEvidenceResources.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceEvidenceResources.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceEvidenceResources.java index 45dc5ede566..e4d9bba65ce 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceEvidenceResources.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceEvidenceResources.java @@ -14,7 +14,7 @@ final class WorkspaceEvidenceResources { static String publish(JdbcTemplate jdbc, String tenant, String workspace, String session, String ownerKey, byte[] bytes) { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String digest = digest(bytes); String resourceId = "workspace-proof:" + ownerKey + ":" + digest; String resourceKey = WorkspaceOperations.hash(scope, resourceId); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceExecutionStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceExecutionStore.java similarity index 90% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceExecutionStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceExecutionStore.java index 8266b3789fa..27b4bda410d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceExecutionStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceExecutionStore.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.runtimebroker.HarnessOwnership; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; @@ -11,7 +11,6 @@ import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceAccess; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -24,7 +23,12 @@ import java.nio.charset.StandardCharsets; import java.util.List; @Repository -public class WorkspaceExecutionStore { +public class WorkspaceExecutionStore + implements com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore { + private com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles worktreeFiles; + @org.springframework.beans.factory.annotation.Autowired + public void configureWorktreeFiles(com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles value) { worktreeFiles = value; } + private final JdbcTemplate jdbc; private final TransactionTemplate transaction; private final WorkspaceStorageGuard storageGuard; @@ -33,7 +37,7 @@ public class WorkspaceExecutionStore { @Autowired public WorkspaceExecutionStore(@Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, @Qualifier("managedAgentTransactionManager") PlatformTransactionManager transactionManager, WorkspaceStorageGuard storageGuard, - com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties properties) { + com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings properties) { this.jdbc = jdbc; this.transaction = new TransactionTemplate(transactionManager); this.storageGuard = storageGuard; @@ -42,8 +46,10 @@ public class WorkspaceExecutionStore { public WorkspaceExecutionStore(JdbcTemplate jdbc, PlatformTransactionManager transactionManager, WorkspaceStorageGuard storageGuard) { - this(jdbc, transactionManager, storageGuard, - new com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties()); + this.jdbc = jdbc; + this.transaction = new TransactionTemplate(transactionManager); + this.storageGuard = storageGuard; + this.residents = new ResidentWorkspaces(List.of()); } public WorkspaceExecutionStore(JdbcTemplate jdbc, @@ -67,7 +73,7 @@ public class WorkspaceExecutionStore { var selected = residents.find(binding); if (selected == null || !com.alibaba.qwen.code.runtimebroker.ResidentWorkspaceProfile.selects(session.getSession().getScope()) || !ResidentWorkspaces.scope(selected).equals(session.getSession().getScope())) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } ResidentWorkspaces.requireRegistration(jdbc, selected); var rows = jdbc.query("SELECT rs.runtime_session_id, b.resource_handle_json, o.csi_revision, o.csi_reservation_id" @@ -97,7 +103,7 @@ public class WorkspaceExecutionStore { var storage = ResidentWorkspaces.heldStorage(held.handle()); if (!Long.toString(held.revision()).equals(storage.get("reservationRevision")) || !held.reservationId().equals(storage.get("reservationId"))) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -114,7 +120,7 @@ public class WorkspaceExecutionStore { try { WorkspaceLifecycleStore.requireClaim(jdbc, session.tenantId(), session.sessionId(), authority, false); } catch (ManagedAgentException error) { - throw new RuntimeBrokerException(error.getStatus().value(), error.getCode(), error.getMessage(), false, error); + throw new RuntimeBrokerException(error.getStatus(), error.getCode(), error.getMessage(), false, error); } authorizePassiveAttachment(session, authority); if (storageGuard != null && residents.find(session.workspace()) == null) { @@ -124,7 +130,7 @@ public class WorkspaceExecutionStore { public void authorizeLegacyClose(SessionRecord session) { if (!WorkspaceLifecycleStore.legacyClose(jdbc, session.tenantId(), session.sessionId())) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } authorizePassiveAttachment(session, null, true); if (storageGuard != null && residents.find(session.workspace()) == null) { @@ -169,7 +175,7 @@ public class WorkspaceExecutionStore { + " AND submission_attempted = TRUE AND harness_event_epoch IS NOT NULL" + " AND dispatch_owner = ? AND dispatch_lease_until >= " + ManagedSchemaTime.SQL_NOW, Long.class, session.tenantId(), session.sessionId(), turnId, dispatchOwner) != 1) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } authorizeAttachment(session, false, false, null, false, true); } @@ -211,7 +217,7 @@ public class WorkspaceExecutionStore { || !session.tenantId().equals(binding.getTenantId()) || !WorkspaceExecutionProfile.CONTEXT_CONFIG_REF.equals( binding.getContextConfigRef())) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if (!cancellation && !committedStop && authority == null && !legacyClose) { requireWorktreeAncestorsReady(jdbc, session.tenantId(), session.sessionId()); @@ -223,7 +229,7 @@ public class WorkspaceExecutionStore { if (!WorkspaceToolProfiles.FILES.equals(session.toolProfile()) || jdbc.queryForObject("SELECT COUNT(*) FROM ma_session WHERE tenant_id = ? AND session_id = ?" + " AND parent_session_id IS NULL", Long.class, session.tenantId(), session.sessionId()) != 1) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } ResidentWorkspaces.requireRegistration(jdbc, resident); } @@ -309,32 +315,32 @@ public class WorkspaceExecutionStore { cancellation || committedStop ? new Object[] {session.tenantId(), session.sessionId()} : new Object[] {executionActor, session.tenantId(), session.sessionId()}); if (grants.size() != 1) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if (grants.getFirst() == 1) { - throw actionResponse ? unavailablePendingGrant() : unavailable(); + throw actionResponse ? com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailablePendingGrant() : com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if (grants.getFirst() != 2) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } - private static void requireWorktreeAncestorsReady(JdbcTemplate jdbc, String tenantId, String sessionId) { + private void requireWorktreeAncestorsReady(JdbcTemplate jdbc, String tenantId, String sessionId) { java.util.Set visited = new java.util.HashSet<>(); String current = sessionId; while (visited.add(current)) { - SessionWorktreeStore.requireReady(jdbc, tenantId, current); + SessionWorktreeStore.requireReady(worktreeFiles, jdbc, tenantId, current); var parents = jdbc.query("SELECT parent_session_id FROM ma_session WHERE tenant_id = ? AND session_id = ?", (row, index) -> row.getString(1), tenantId, current); if (parents.size() != 1) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if (parents.getFirst() == null) { return; } current = parents.getFirst(); } - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } static byte[] inheritedExecutionActor(JdbcTemplate jdbc, ContextBinding binding, String sessionId) { @@ -345,7 +351,7 @@ public class WorkspaceExecutionStore { + " workspace_id, workspace_generation, workspace_storage_id FROM ma_session" + " WHERE tenant_id = ? AND session_id = ?", binding.getTenantId(), current); if (rows.size() != 1) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } var row = rows.getFirst(); if (!binding.getTenantId().equals(row.get("tenant_id")) || !current.equals(row.get("session_id")) @@ -353,18 +359,18 @@ public class WorkspaceExecutionStore { || !(row.get("workspace_generation") instanceof Number generation) || binding.getWorkspaceGeneration() != generation.longValue() || !binding.getStorageId().equals(row.get("workspace_storage_id"))) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } String parent = (String) row.get("parent_session_id"); if (parent == null) { if (!(row.get("creator_actor_key") instanceof byte[] actor)) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return actor; } current = parent; } - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } public void claim(ContextBinding binding, RuntimeSessionRecord session) { @@ -392,7 +398,7 @@ public class WorkspaceExecutionStore { && Long.toString(binding.getWorkspaceGeneration()).equals(row.getString("workspace_generation")) && binding.getStorageId().equals(row.getString("storage_id")), session.getBindingId()); if (live.size() != 1 || !live.getFirst()) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } List active = jdbc.query("SELECT binding_id, runtime_generation, runtime_session_id," + " harness_session_id, session_state FROM ma_runtime_session" @@ -405,7 +411,7 @@ public class WorkspaceExecutionStore { || "READY".equals(row.getString("session_state"))), session.getBindingId(), session.getRuntimeGeneration(), session.getRuntimeSessionId()); if (active.size() != 1 || !active.getFirst()) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } jdbc.update("INSERT INTO ma_storage_owner" + " (storage_key, storage_kind) VALUES (?, ?) ON DUPLICATE KEY UPDATE" @@ -414,7 +420,7 @@ public class WorkspaceExecutionStore { + " FROM ma_storage_owner WHERE storage_key = ?" + " AND storage_kind IN ('LOCAL', 'SSH_LOCAL') FOR UPDATE", key); if (owners.size() != 1) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if (storageGuard != null) { storageGuard.verifyLocked(binding); @@ -435,7 +441,7 @@ public class WorkspaceExecutionStore { + " AND runtime_generation = ? AND runtime_session_id = ? AND harness_session_id = ?" + " AND (execution_storage_key IS NULL OR execution_storage_key = ?)", key, session.getBindingId(), session.getRuntimeGeneration(), session.getRuntimeSessionId(), session.getSession().getHarnessSessionId(), key) != 1) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } }); } @@ -461,7 +467,7 @@ public class WorkspaceExecutionStore { WorkspaceStorageKindGuard.lockDomain(jdbc, binding.getTenantId()); String key = storageKey(binding); if (!isHeld(binding, session)) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } var members = WorkspaceOperations.rows(jdbc, "SELECT binding_id, runtime_generation, runtime_session_id," + " execution_exclusive FROM ma_runtime_session WHERE execution_storage_key = ? FOR UPDATE", key); @@ -539,7 +545,7 @@ public class WorkspaceExecutionStore { requireResidentHeld(binding, session, true); if (session.getState() != RuntimeSessionRecord.State.RELEASING && session.getState() != RuntimeSessionRecord.State.RELEASED) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } }); return; @@ -556,7 +562,7 @@ public class WorkspaceExecutionStore { || "DRAINING".equals(row.getString("binding_state"))), session.getBindingId()); if (live.size() != 1 || !live.getFirst()) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } jdbc.update("UPDATE ma_runtime_session SET execution_storage_key = NULL, execution_exclusive = FALSE" + " WHERE execution_storage_key = ? AND binding_id = ? AND runtime_generation = ?" @@ -590,7 +596,7 @@ public class WorkspaceExecutionStore { List generations = jdbc.queryForList("SELECT claim_generation FROM ma_operation WHERE operation_key = ? AND operation_kind = ? AND claim_until > " + ManagedSchemaTime.SQL_NOW + " FOR UPDATE", Long.class, WorkspaceOperations.key(kind, binding.getTenantId(), maintenanceId), kind.name()); if (generations.size() != 1 || generations.getFirst() != claimGeneration) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } jdbc.update("INSERT INTO ma_storage_owner" + " (storage_key, storage_kind) VALUES (?, 'LOCAL') ON DUPLICATE KEY UPDATE" @@ -600,7 +606,7 @@ public class WorkspaceExecutionStore { (row, index) -> new String[] {row.getString("holder_key"), row.getString("maintenance_id")}, key); if (rows.size() != 1) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if (storageGuard != null) { storageGuard.verifyLocked(binding); @@ -649,7 +655,7 @@ public class WorkspaceExecutionStore { public boolean canStopDrained(RuntimeBindingRecord saved) { if ((saved.getState() != RuntimeBindingRecord.State.DRAINING && saved.getState() != RuntimeBindingRecord.State.LOST) || !saved.isDrainRequested() || !saved.getRequest().isManagedContext() || saved.getOperationOwner() == null - || !java.util.Set.of(LocalProcessRuntimeProvisioner.KIND, com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeProvisioner.KIND).contains(saved.getRequest().getProvisionerKind()) + || !java.util.Set.of(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS).contains(saved.getRequest().getProvisionerKind()) || !(WorkspaceExecutionProfile.admitsIsolationClass(saved.getRequest().getScope().getIsolationClass()) || HarnessOwnership.sharesRuntimeSessions(saved.getRequest()))) { return false; @@ -692,7 +698,7 @@ public class WorkspaceExecutionStore { public void releaseLost(RuntimeBindingRecord saved) { if (!saved.getRequest().isManagedContext() || saved.getState() != RuntimeBindingRecord.State.LOST || !saved.hasStoppedWriters() || saved.getOperationOwner() == null) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } transaction.executeWithoutResult(status -> { WorkspaceStorageKindGuard.lockDomain(jdbc, saved.getRequest().getScope().getTenantId()); @@ -721,7 +727,7 @@ public class WorkspaceExecutionStore { || jdbc.queryForObject("SELECT COUNT(*) FROM ma_tool_execution WHERE binding_id = ?" + " AND runtime_generation = ? AND execution_state NOT IN ('SETTLED', 'ABANDONED')", Long.class, saved.getBindingId(), saved.getGeneration()) != 0) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } var memberKeys = jdbc.queryForList("SELECT execution_storage_key FROM ma_runtime_session" + " WHERE binding_id = ? AND runtime_generation = ? AND execution_storage_key IS NOT NULL FOR UPDATE", @@ -730,13 +736,13 @@ public class WorkspaceExecutionStore { return; } if (key == null || memberKeys.stream().anyMatch(member -> !key.equals(member))) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if (jdbc.queryForObject("SELECT COUNT(*) FROM ma_runtime_binding WHERE binding_id = ?" + " AND runtime_generation = ? AND operation_owner = ? AND operation_generation = ?" + " AND operation_lease_until > " + ManagedSchemaTime.SQL_NOW, Long.class, saved.getBindingId(), saved.getGeneration(), saved.getOperationOwner(), saved.getOperationGeneration()) != 1) { - throw unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } jdbc.update("UPDATE ma_runtime_session SET execution_storage_key = NULL, execution_exclusive = FALSE" + " WHERE binding_id = ? AND runtime_generation = ? AND execution_storage_key = ?", @@ -750,38 +756,19 @@ public class WorkspaceExecutionStore { return "qwen-code".equals(agentId) || agentDefinitionDigest != null; } - public static RuntimeBrokerException unavailable() { - return new RuntimeBrokerException(409, "workspace_unavailable", - "Workspace execution authority is unavailable.", false); - } - // Same structural refusal, with the discarded cause kept for the log: // without it a failed mount verification reports nothing but the verdict. - public static RuntimeBrokerException unavailable(Throwable cause) { - return new RuntimeBrokerException(409, "workspace_unavailable", - "Workspace execution authority is unavailable.", false, cause); - } // A probe's momentary I/O failure is not the structural verdict the // terminal refusal promises: it retries through the delivery machine, // keeping the cause for the log. Structural refusals keep - // unavailable(). - public static RuntimeBrokerException unavailableTransient( - Throwable cause) { - return new RuntimeBrokerException(409, "workspace_unavailable", - "Workspace mount cannot be verified right now.", true, - cause); - } + // com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(). // A creation grant or the registry state changes under operator // control: refusing a committed decision over it is not the - // structural verdict of unavailable(), and the delivery machine must + // structural verdict of com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(), and the delivery machine must // retry so a restored grant still reaches the Harness. Structural - // refusals keep unavailable(). - public static RuntimeBrokerException unavailablePendingGrant() { - return new RuntimeBrokerException(409, "workspace_unavailable", - "Workspace access grant or registry state is changing.", true); - } + // refusals keep com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(). static RuntimeBrokerException busy() { return new RuntimeBrokerException(409, "workspace_busy", diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStore.java similarity index 83% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStore.java index 00cfa0f3c2f..acc598150d1 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStore.java @@ -1,16 +1,15 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.LocalWorkspaceFiles; +import com.alibaba.qwen.code.managedagent.contracts.workspace.SshWorkspaceFiles; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.workspace.SshWorkspaceService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.provisioning.WorkspaceFileProcess; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; import org.springframework.transaction.PlatformTransactionManager; @@ -23,17 +22,25 @@ import java.util.Objects; @Component public class WorkspaceFileStore { + private LocalWorkspaceFiles localFiles; + @org.springframework.beans.factory.annotation.Autowired + public void configureLocalFiles(LocalWorkspaceFiles value) { localFiles = value; } + + private com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles worktreeFiles; + @org.springframework.beans.factory.annotation.Autowired + public void configureWorktreeFiles(com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles value) { worktreeFiles = value; } + private final ManagedWorkspaceRegistry registry; private final WorkspaceStorageGuard guard; - private final SshWorkspaceService ssh; - private final ManagedAgentProperties properties; + private final SshWorkspaceFiles ssh; + private final ManagedAgentSettings properties; private final JdbcTemplate jdbc; private final TransactionTemplate transaction; private final ObjectMapper mapper; private final java.util.concurrent.Semaphore reads = new java.util.concurrent.Semaphore(4); public WorkspaceFileStore(ManagedWorkspaceRegistry registry, WorkspaceStorageGuard guard, - SshWorkspaceService ssh, ManagedAgentProperties properties, @Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, + SshWorkspaceFiles ssh, ManagedAgentSettings properties, @Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, @Qualifier("managedAgentTransactionManager") PlatformTransactionManager manager, @Qualifier("managedAgentObjectMapper") ObjectMapper mapper) { this.registry = registry; this.guard = guard; @@ -90,7 +97,7 @@ public class WorkspaceFileStore { Integer maximum = mapper.convertValue(result.files().get("maxUploadBytes"), Integer.class); return new QueryResult(result.revision(), directory, List.of(entries), Boolean.TRUE.equals(result.files().get("truncated")), - Integer.valueOf(WorkspaceFileProcess.MAX_UPLOAD_BYTES).equals(maximum) ? maximum : null); + Integer.valueOf(LocalWorkspaceFiles.MAX_UPLOAD_BYTES).equals(maximum) ? maximum : null); } catch (IllegalArgumentException error) { throw unavailable(); } @@ -131,7 +138,7 @@ public class WorkspaceFileStore { throw changed(); } if (!reads.tryAcquire()) { - throw new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, "workspace_files_busy", "Workspace file access is busy; try again."); + throw new ManagedAgentException(503, "workspace_files_busy", "Workspace file access is busy; try again."); } try { Map result; @@ -152,13 +159,13 @@ public class WorkspaceFileStore { request.put("sessionFileScope", before.sessionFileScope()); } request.put("files", files); - result = WorkspaceFileProcess.read(properties.getRuntimeBroker().getNodeExecutable(), + result = localFiles.readFiles(properties.getRuntimeBroker().getNodeExecutable(), properties.getRuntimeBroker().getWorkerEntry(), request); if (!root.equals(guard.fileRoot(before.binding()))) { throw changed(); } } else { - throw new ManagedAgentException(HttpStatus.CONFLICT, "workspace_files_unsupported", "This Workspace storage does not support file references."); + throw new ManagedAgentException(409, "workspace_files_unsupported", "This Workspace storage does not support file references."); } Scope after = scope(tenant, actor, locator); if (!before.revision().equals(after.revision())) { @@ -166,7 +173,7 @@ public class WorkspaceFileStore { } if (!(result.get("files") instanceof Map contents) || !Integer.valueOf(1).equals( mapper.convertValue(contents.get("protocolVersion"), Integer.class))) { - throw WorkspaceFileProcess.unsupported(); + throw LocalWorkspaceFiles.unsupported(); } return new Exchange(before.revision(), mapper.convertValue(contents, mapper.getTypeFactory().constructMapType(LinkedHashMap.class, String.class, Object.class))); @@ -185,13 +192,13 @@ public class WorkspaceFileStore { ? registry.resolveForCreation(tenant, actor, new WorkspaceSelection(locator.workspaceId(), locator.cwdRelative())).binding() : sessionBinding(tenant, actor, locator.sessionId()); if (!properties.getHarness().isWorkspaceFilesEnabled()) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "workspace_files_disabled", "Workspace file access is disabled."); + throw new ManagedAgentException(409, "workspace_files_disabled", "Workspace file access is disabled."); } Map sessionFileScope = null; long worktreeRevision = 0; if (locator.sessionId() != null) { - SessionWorktreeStore.requireReady(jdbc, tenant, locator.sessionId()); - var worktree = SessionWorktreeStore.descriptor(jdbc, tenant, locator.sessionId()); + SessionWorktreeStore.requireReady(worktreeFiles, jdbc, tenant, locator.sessionId()); + var worktree = SessionWorktreeStore.descriptor(worktreeFiles, jdbc, tenant, locator.sessionId()); if (worktree != null) { String prefix = ".qwen-managed-worktrees/" + worktree.worktreeId(); if (!binding.getCwdRelative().equals(prefix) && !binding.getCwdRelative().startsWith(prefix + "/")) { @@ -202,7 +209,7 @@ public class WorkspaceFileStore { try { var identity = java.nio.file.Files.readAttributes(java.nio.file.Path.of((String) root.get("root")) .resolve(".qwen-managed-worktrees").resolve(worktree.worktreeId()), "unix:dev,ino", java.nio.file.LinkOption.NOFOLLOW_LINKS); - SessionWorktreeStore.requireReady(jdbc, tenant, locator.sessionId()); + SessionWorktreeStore.requireReady(worktreeFiles, jdbc, tenant, locator.sessionId()); sessionFileScope = Map.of("sessionId", locator.sessionId(), "contextDigest", binding.getContextDigest(), "worktreeId", worktree.worktreeId(), "worktreeRevision", worktreeRevision, "cwdRelative", binding.getCwdRelative(), "worktreeRootIdentity", Map.of("device", Long.toUnsignedString(((Number) identity.get("dev")).longValue()), @@ -250,18 +257,18 @@ public class WorkspaceFileStore { } private static ManagedAgentException invalidLocator() { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", "Select either a Session or a Workspace directory."); + return new ManagedAgentException(400, "invalid_request", "Select either a Session or a Workspace directory."); } private static ManagedAgentException sessionNotFound() { - return new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", "Session not found."); + return new ManagedAgentException(404, "session_not_found", "Session not found."); } private static ManagedAgentException changed() { - return new ManagedAgentException(HttpStatus.CONFLICT, "workspace_file_scope_changed", "Workspace changed; select the file again."); + return new ManagedAgentException(409, "workspace_file_scope_changed", "Workspace changed; select the file again."); } private static ManagedAgentException unavailable() { - return new ManagedAgentException(HttpStatus.CONFLICT, "workspace_file_unavailable", "Workspace file is unavailable."); + return new ManagedAgentException(409, "workspace_file_unavailable", "Workspace file is unavailable."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStore.java similarity index 90% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStore.java index 7b1139e834f..d49889a92f5 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStore.java @@ -1,22 +1,21 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.LocalWorkspaceFiles; +import com.alibaba.qwen.code.managedagent.contracts.workspace.SshWorkspaceFiles; +import com.alibaba.qwen.code.managedagent.contracts.workspace.SshWorkspaceFiles.FileTarget; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.workspace.SshWorkspaceService; -import com.alibaba.qwen.code.managedagent.workspace.SshWorkspaceService.FileTarget; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.runtimebroker.BrokerValues; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.provisioning.WorkspaceFileProcess; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -38,14 +37,18 @@ import java.util.concurrent.Semaphore; @Component public class WorkspaceFileUploadStore { + private LocalWorkspaceFiles localFiles; + @org.springframework.beans.factory.annotation.Autowired + public void configureLocalFiles(LocalWorkspaceFiles value) { localFiles = value; } + private static final Logger LOG = LoggerFactory.getLogger(WorkspaceFileUploadStore.class); private static final String KIND = "WORKSPACE_UPLOAD"; - private static final int LIMIT = WorkspaceFileProcess.MAX_UPLOAD_BYTES; + private static final int LIMIT = LocalWorkspaceFiles.MAX_UPLOAD_BYTES; private final WorkspaceFileStore files; private final ManagedWorkspaceRegistry registry; private final WorkspaceStorageGuard guard; - private final SshWorkspaceService ssh; - private final ManagedAgentProperties properties; + private final SshWorkspaceFiles ssh; + private final ManagedAgentSettings properties; private final JdbcTemplate jdbc; private final TransactionTemplate transaction; private final ObjectMapper mapper; @@ -54,7 +57,7 @@ public class WorkspaceFileUploadStore { private final Set running = ConcurrentHashMap.newKeySet(); public WorkspaceFileUploadStore(WorkspaceFileStore files, ManagedWorkspaceRegistry registry, - WorkspaceStorageGuard guard, SshWorkspaceService ssh, ManagedAgentProperties properties, + WorkspaceStorageGuard guard, SshWorkspaceFiles ssh, ManagedAgentSettings properties, @Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, @Qualifier("managedAgentTransactionManager") PlatformTransactionManager manager, @Qualifier("managedAgentObjectMapper") ObjectMapper mapper, @Qualifier("managedAgentExecutor") Executor executor) { this.files = files; @@ -137,7 +140,7 @@ public class WorkspaceFileUploadStore { throw pending(); } if ("failed".equals(result.state())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, result.code(), "Workspace upload failed."); + throw new ManagedAgentException(409, result.code(), "Workspace upload failed."); } return new UploadResult(result.scopeRevision(), result.uploadId(), result.path(), result.sizeBytes(), result.sha256()); } finally { @@ -224,7 +227,7 @@ public class WorkspaceFileUploadStore { if (previous != null) { authorize(previous, actor); if (!previous.digest().equals(digest)) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "workspace_upload_conflict", "Upload ID belongs to another request."); + throw new ManagedAgentException(409, "workspace_upload_conflict", "Upload ID belongs to another request."); } return previous; } @@ -233,7 +236,7 @@ public class WorkspaceFileUploadStore { throw changed(); } if (!Set.of("LOCAL", "SSH_LOCAL").contains(scope.kind())) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "workspace_files_unsupported", "This Workspace storage does not support uploads."); + throw new ManagedAgentException(409, "workspace_files_unsupported", "This Workspace storage does not support uploads."); } WorkspaceMigrationAdmission.requireOpen(jdbc, tenant, scope.binding().getStorageId()); String physical = jdbc.queryForObject("SELECT physical_key FROM ma_storage WHERE alias_key = ?", String.class, @@ -274,7 +277,7 @@ public class WorkspaceFileUploadStore { ManagedSchemaKeys.key(scope.binding().getStorageId()), scope.binding().getStorageId().getBytes(StandardCharsets.UTF_8), JdbcRuntimeBindingRepository.storageAliasKey(tenant, scope.binding().getStorageId()), physical, - ManagedWorkspaceRegistry.actorKey(tenant, actor), digest, encode(target)); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenant, actor), digest, encode(target)); if (jdbc.update("UPDATE ma_storage_owner SET holder_key = ? WHERE storage_key = ? AND holder_key IS NULL AND maintenance_id IS NULL", key, physical) != 1) { throw busy(); } @@ -300,7 +303,7 @@ public class WorkspaceFileUploadStore { } else { Map request = new LinkedHashMap<>(target.request()); request.put("files", message); - response = WorkspaceFileProcess.upload(target.node(), target.entry(), request); + response = localFiles.uploadFiles(target.node(), target.entry(), request); } if (!(response.get("files") instanceof Map result) || !Long.valueOf(1).equals(BrokerValues.exactLong(result.get("protocolVersion"))) @@ -443,26 +446,26 @@ public class WorkspaceFileUploadStore { } private static ManagedAgentException invalid() { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_request", "Workspace upload request is invalid."); + return new ManagedAgentException(400, "invalid_request", "Workspace upload request is invalid."); } private static ManagedAgentException changed() { - return new ManagedAgentException(HttpStatus.CONFLICT, "workspace_file_scope_changed", "Workspace changed; select the upload destination again."); + return new ManagedAgentException(409, "workspace_file_scope_changed", "Workspace changed; select the upload destination again."); } private static ManagedAgentException pending() { - return new ManagedAgentException(HttpStatus.SERVICE_UNAVAILABLE, "workspace_upload_pending", "Workspace upload outcome is pending; query the original upload ID."); + return new ManagedAgentException(503, "workspace_upload_pending", "Workspace upload outcome is pending; query the original upload ID."); } private static ManagedAgentException busy() { - return new ManagedAgentException(HttpStatus.CONFLICT, "workspace_busy", "Workspace file access is busy; try again."); + return new ManagedAgentException(409, "workspace_busy", "Workspace file access is busy; try again."); } private static ManagedAgentException notFound() { - return new ManagedAgentException(HttpStatus.NOT_FOUND, "workspace_upload_not_found", "Workspace upload not found."); + return new ManagedAgentException(404, "workspace_upload_not_found", "Workspace upload not found."); } private static ManagedAgentException tooLarge() { - return new ManagedAgentException(HttpStatus.PAYLOAD_TOO_LARGE, "workspace_upload_too_large", "Workspace upload exceeds 8 MiB."); + return new ManagedAgentException(413, "workspace_upload_too_large", "Workspace upload exceeds 8 MiB."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStore.java similarity index 82% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStore.java index cd986e25488..875ad462c5e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStore.java @@ -1,19 +1,20 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedHookRecords; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @@ -24,7 +25,8 @@ import java.util.List; @Repository @Qualifier("managedAgentTransactionManager") -public class WorkspaceLifecycleStore { +public class WorkspaceLifecycleStore + implements com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore { private static final ObjectMapper OCCURRENCE_JSON = new ObjectMapper(); public static boolean legacyClose(JdbcTemplate jdbc, String tenant, String session) { @@ -41,7 +43,7 @@ public class WorkspaceLifecycleStore { private ResidentWorkspaces residents = new ResidentWorkspaces(java.util.List.of()); @org.springframework.beans.factory.annotation.Autowired - void setResidentConfiguration(com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties properties) { + void setResidentConfiguration(com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings properties) { residents = new ResidentWorkspaces(properties.getRuntimeBroker().getResidentWorkspaces()); } @@ -73,14 +75,14 @@ public class WorkspaceLifecycleStore { public static void requireClaim(JdbcTemplate jdbc, String tenant, String session, RuntimeLifecycleAuthority authority, boolean drainingAllowed) { if (authority == null) { - throw blocked("workspace_lifecycle_claim_fenced"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_claim_fenced"); } var rows = jdbc.query("SELECT o.claim_generation, o.claim_until AS lease_until, o.delivery_state, o.lifecycle_protocol_version, s.status, f.phase, f.operation_id FROM ma_operation o JOIN ma_session s ON s.tenant_id = o.tenant_id AND s.session_id = o.session_id JOIN ma_session_head f ON f.tenant_id = s.tenant_id AND f.harness_session_id = s.session_id WHERE o.operation_kind IN ('CLOSE', 'ARCHIVE', 'DELETE') AND o.tenant_id = ? AND o.session_id = ? AND o.operation_id = ? AND f.tenant_key = ? AND f.harness_key = ? FOR UPDATE", (row, index) -> com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows.read(row), tenant, session, authority.operationId(), JdbcRuntimeBindingRepository.harnessDrainKey(tenant), JdbcRuntimeBindingRepository.harnessDrainKey(session)); long now = jdbc.queryForObject("SELECT UNIX_TIMESTAMP(), EXTRACT(MICROSECOND FROM CURRENT_TIMESTAMP(6))", (row, index) -> row.getLong(1) * 1000 + row.getLong(2) / 1000); if (rows.size() != 1) { - throw blocked("workspace_lifecycle_claim_fenced"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_claim_fenced"); } var row = rows.getFirst(); if (((Number) row.get("claim_generation")).longValue() != authority.claimGeneration() @@ -90,7 +92,7 @@ public class WorkspaceLifecycleStore { || !List.of("CLOSING", "DELETING").contains(row.get("status")) || !authority.operationId().equals(row.get("operation_id")) || !"LIFECYCLE_ONLY".equals(row.get("phase")) && !(drainingAllowed && "DRAINING".equals(row.get("phase")))) { - throw blocked("workspace_lifecycle_claim_fenced"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_claim_fenced"); } } @@ -122,7 +124,7 @@ public class WorkspaceLifecycleStore { } if (!tenant.equals(heads.getFirst().get("tenant_id")) || !session.equals(heads.getFirst().get("session_id")) || ((Number) heads.getFirst().get("compacted_through_revision")).longValue() != 0) { - throw blocked("workspace_lifecycle_journal_unverified"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_journal_unverified"); } long headRevision = ((Number) heads.getFirst().get("journal_revision")).longValue(); // The head row locked above is the per-Session fence: writers append @@ -140,13 +142,13 @@ public class WorkspaceLifecycleStore { if (scan.maxRevision() < headRevision) { scan = scanJournal(jdbc, json, tenant, session, v2, true); if (scan.maxRevision() < headRevision) { - throw blocked("workspace_lifecycle_journal_unverified"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_journal_unverified"); } } var pending = scan.pending(); var cleanup = scan.cleanup(); if (!pending.isEmpty() || !allowCleanup && cleanup.values().stream().anyMatch(state -> !"confirmed".equals(state))) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "turn_active", "The Session has an active Turn."); + throw new ManagedAgentException(409, "turn_active", "The Session has an active Turn."); } return java.util.Set.copyOf(scan.accepted()); } @@ -189,7 +191,7 @@ public class WorkspaceLifecycleStore { settled.add(turn); } } catch (java.io.IOException error) { - throw blocked("workspace_lifecycle_journal_unverified"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_journal_unverified"); } } }, tenant, session); @@ -199,7 +201,7 @@ public class WorkspaceLifecycleStore { @Transactional public JsonNode recoverEffects(OperationRecord operation) { lock(operation); - requireClaim(jdbc, operation.tenantId(), operation.sessionId(), authority(operation), true); + requireClaim(jdbc, operation.tenantId(), operation.sessionId(), com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation), true); String saved = jdbc.queryForObject("SELECT lifecycle_effects_receipt_json FROM ma_operation" + " WHERE operation_kind IN ('CLOSE', 'ARCHIVE', 'DELETE') AND tenant_id = ? AND session_id = ? AND operation_id = ? FOR UPDATE", String.class, operation.tenantId(), operation.sessionId(), operation.operationId()); @@ -250,7 +252,7 @@ public class WorkspaceLifecycleStore { @Transactional public void saveEffects(OperationRecord operation, JsonNode receipt) { lock(operation); - requireClaim(jdbc, operation.tenantId(), operation.sessionId(), authority(operation), false); + requireClaim(jdbc, operation.tenantId(), operation.sessionId(), com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation), false); saveLocked(operation, receipt); } @@ -268,7 +270,7 @@ public class WorkspaceLifecycleStore { + " WHERE operation_kind IN ('CLOSE', 'ARCHIVE', 'DELETE') AND tenant_id = ? AND session_id = ? AND operation_id = ? FOR UPDATE", String.class, operation.tenantId(), operation.sessionId(), operation.operationId()); if (saved == null) { - throw blocked("workspace_lifecycle_hooks_unsettled"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_hooks_unsettled"); } verifyEffects(operation, parse(saved)); if (operation.lifecycleProtocolVersion() == 2) { @@ -279,24 +281,24 @@ public class WorkspaceLifecycleStore { JdbcRuntimeBindingRepository.harnessDrainKey(operation.sessionId())); if (!writers.isEmpty() && (!operation.tenantId().equals(writers.getFirst().get("tenant_id")) || !operation.sessionId().equals(writers.getFirst().get("session_id")))) { - throw blocked("workspace_lifecycle_claim_fenced"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_claim_fenced"); } if (!writers.isEmpty() && "ACTIVE".equals(writers.getFirst().get("state")) && ((Number) writers.getFirst().get("live_writer")).intValue() != 0) { - throw blocked("workspace_lifecycle_writer_active"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_writer_active"); } if (residents.verifyClosed(jdbc, operation.tenantId(), operation.sessionId())) { if (!writers.isEmpty() && !"SEALED".equals(writers.getFirst().get("state"))) { - throw blocked("workspace_lifecycle_writer_active"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_writer_active"); } return; } RuntimeBindingRepository repository = bindings.getIfAvailable(); - if (repository == null) { - throw blocked("workspace_close_identity_unverified"); + if (!(repository instanceof com.alibaba.qwen.code.runtimebroker.persistence.JdbcHarnessResources)) { + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_close_identity_unverified"); } jdbc.execute((ConnectionCallback) connection -> { - repository.verifyHarnessStopped(connection, operation.tenantId(), operation.sessionId()); + ((com.alibaba.qwen.code.runtimebroker.persistence.JdbcHarnessResources) repository).verifyHarnessStopped(connection, operation.tenantId(), operation.sessionId()); return null; }); } @@ -304,10 +306,10 @@ public class WorkspaceLifecycleStore { @Transactional public void requireSettledExecutions(OperationRecord operation) { lock(operation); - requireClaim(jdbc, operation.tenantId(), operation.sessionId(), authority(operation), true); + requireClaim(jdbc, operation.tenantId(), operation.sessionId(), com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation), true); if (operation.lifecycleProtocolVersion() == 2) { if (profiles == null) { - throw blocked("workspace_lifecycle_protocol_unavailable"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_protocol_unavailable"); } profiles.verifyExecutions(operation); } @@ -315,34 +317,34 @@ public class WorkspaceLifecycleStore { public void verifyOutputContents(OperationRecord operation) { if (profiles == null) { - throw blocked("workspace_lifecycle_protocol_unavailable"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_protocol_unavailable"); } try { profiles.verifyPublications(operation); } catch (IllegalArgumentException invalid) { - throw blocked("workspace_lifecycle_resources_unsettled"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_resources_unsettled"); } } @Transactional public void verifyResources(OperationRecord operation) { lock(operation); - requireClaim(jdbc, operation.tenantId(), operation.sessionId(), authority(operation), true); - if (!resourceClaim(jdbc, operation.tenantId(), operation.sessionId(), authority(operation)) || profiles == null) { - throw blocked("workspace_lifecycle_resources_unsettled"); + requireClaim(jdbc, operation.tenantId(), operation.sessionId(), com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation), true); + if (!resourceClaim(jdbc, operation.tenantId(), operation.sessionId(), com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation)) || profiles == null) { + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_resources_unsettled"); } RuntimeBindingRepository repository = bindings.getIfAvailable(); - if (repository == null) { - throw blocked("workspace_close_identity_unverified"); + if (!(repository instanceof com.alibaba.qwen.code.runtimebroker.persistence.JdbcHarnessResources)) { + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_close_identity_unverified"); } var originals = jdbc.execute((ConnectionCallback>) connection -> - repository.verifyHarnessResources(connection, operation.tenantId(), operation.sessionId())); + ((com.alibaba.qwen.code.runtimebroker.persistence.JdbcHarnessResources) repository).verifyHarnessResources(connection, operation.tenantId(), operation.sessionId())); String profile = jdbc.queryForObject("SELECT tool_profile FROM ma_session WHERE tenant_id = ? AND session_id = ?", String.class, operation.tenantId(), operation.sessionId()); try { profiles.verifyResources(operation, originals, profile); } catch (IllegalArgumentException invalid) { - throw blocked("workspace_lifecycle_resources_unsettled"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_resources_unsettled"); } } @@ -367,51 +369,51 @@ public class WorkspaceLifecycleStore { || !receiptOperation.equals(operation.operationId()) && !failedLifecyclePredecessor(operation, receiptOperation) || !operation.kind().name().toLowerCase(java.util.Locale.ROOT).equals(receipt.path("kind").asText()) || !receipt.path("effects").isArray()) { - throw blocked("workspace_lifecycle_receipt_invalid"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_receipt_invalid"); } if (operation.lifecycleProtocolVersion() == 2) { if (profiles == null) { - throw blocked("workspace_lifecycle_protocol_unavailable"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_protocol_unavailable"); } profiles.verifyEffects(operation); } JsonNode header = header(operation.tenantId(), operation.sessionId()); if (receipt.path("neverInitialized").asBoolean()) { if (header != null || !receipt.path("effects").isEmpty() || !neverInitialized(operation)) { - throw blocked("workspace_lifecycle_receipt_invalid"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_receipt_invalid"); } return; } if (header == null || !header.path("definitionRef").equals(receipt.path("definitionRef"))) { - throw blocked("workspace_lifecycle_receipt_invalid"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_receipt_invalid"); } JsonNode definition = records.readRecordResource(operation.tenantId(), operation.sessionId(), receipt.path("definitionRef")); if (WorkspaceToolProfiles.lifecycleProtocol(definition.path("toolProfile").asText()) != operation.lifecycleProtocolVersion()) { - throw blocked("workspace_lifecycle_profile_unavailable"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_profile_unavailable"); } for (JsonNode execution : records.listRecords(operation.tenantId(), operation.sessionId(), "hook_execution")) { if (execution.path("resultRef").isNull() && !"not_started_proven".equals(execution.path("run").path("execution").asText()) || "recovery_blocked".equals(execution.path("run").path("state").asText())) { - throw blocked("workspace_lifecycle_hooks_unsettled"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_hooks_unsettled"); } } if (!definition.has("hookCatalog")) { if (!receipt.path("effects").isEmpty()) { - throw blocked("workspace_lifecycle_receipt_invalid"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_receipt_invalid"); } return; } List events = events(operation); if (receipt.path("effects").size() != events.size()) { - throw blocked("workspace_lifecycle_hooks_unsettled"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_hooks_unsettled"); } for (int index = 0; index < events.size(); index++) { String event = events.get(index); JsonNode effect = receipt.path("effects").get(index); String latest = latestHookExecutionResource(operation, occurrence(event, receiptOperation)); if (latest == null || !latest.equals(effect.path("recordRef").path("resourceId").asText())) { - throw blocked("workspace_lifecycle_receipt_invalid"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_receipt_invalid"); } JsonNode record = records.readRecordResource(operation.tenantId(), operation.sessionId(), effect.path("recordRef")); ManagedHookRecords.requireExecution(record); @@ -419,13 +421,13 @@ public class WorkspaceLifecycleStore { || !occurrence(event, receiptOperation).equals(record.path("hookExecutionId").asText()) || !"__plan__".equals(record.path("hookId").asText()) || record.path("resultRef").isNull() || "recovery_blocked".equals(record.path("run").path("state").asText())) { - throw blocked("workspace_lifecycle_hooks_unsettled"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_hooks_unsettled"); } records.readRecordResource(operation.tenantId(), operation.sessionId(), record.path("resultRef")); JsonNode plan = records.readRecordResource(operation.tenantId(), operation.sessionId(), record.path("planRef")); if (!event.equals(plan.path("input").path("hook_event_name").asText()) || !operation.sessionId().equals(plan.path("input").path("session_id").asText())) { - throw blocked("workspace_lifecycle_receipt_invalid"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_receipt_invalid"); } } } @@ -459,7 +461,7 @@ public class WorkspaceLifecycleStore { return jdbc.queryForList("SELECT record_resource_id FROM ma_session_record" + " WHERE session_scope_key = ? AND record_key = ? AND tenant_id = ? AND session_id = ?" + " AND domain = 'hook_execution' AND record_id = ? FOR UPDATE", String.class, - ManagedSessionStore.sessionScopeKey(operation.tenantId(), operation.sessionId()), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(operation.tenantId(), operation.sessionId()), ManagedExtensionProjection.recordKey(operation.sessionId(), "hook_execution", occurrence), operation.tenantId(), operation.sessionId(), occurrence).stream().findFirst().orElse(null); } @@ -469,9 +471,9 @@ public class WorkspaceLifecycleStore { + " WHERE session_scope_key = ? AND tenant_id = ? AND session_id = ? AND resource_id = ?", (row, index) -> json.createObjectNode().put("resourceId", resource).put("kind", row.getString("kind")) .put("schemaVersion", row.getInt("schema_version")).put("byteLength", row.getLong("byte_length")) - .put("digest", row.getString("sha256")), ManagedSessionStore.sessionScopeKey(operation.tenantId(), operation.sessionId()), + .put("digest", row.getString("sha256")), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(operation.tenantId(), operation.sessionId()), operation.tenantId(), operation.sessionId(), resource).stream() - .findFirst().orElseThrow(() -> blocked("workspace_lifecycle_receipt_invalid")); + .findFirst().orElseThrow(() -> com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_receipt_invalid")); } private JsonNode header(String tenant, String session) { @@ -486,7 +488,7 @@ public class WorkspaceLifecycleStore { return record.path("managedSession"); } } - throw blocked("workspace_lifecycle_receipt_invalid"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_receipt_invalid"); } private boolean failedLifecyclePredecessor(OperationRecord operation, @@ -530,15 +532,7 @@ public class WorkspaceLifecycleStore { try { return json.readTree(value); } catch (java.io.IOException error) { - throw blocked("workspace_lifecycle_receipt_invalid"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_receipt_invalid"); } } - - public static RuntimeLifecycleAuthority authority(OperationRecord operation) { - return new RuntimeLifecycleAuthority(operation.operationId(), operation.claimGeneration()); - } - - public static ManagedAgentException blocked(String code) { - return new ManagedAgentException(HttpStatus.CONFLICT, code, "Workspace lifecycle recovery is blocked."); - } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLocalDirectories.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLocalDirectories.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLocalDirectories.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLocalDirectories.java index 3159bc30437..e9b9a9545b9 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLocalDirectories.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLocalDirectories.java @@ -1,8 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import org.springframework.http.HttpStatus; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import java.io.IOException; import java.nio.file.Files; @@ -15,7 +14,7 @@ import java.util.List; final class WorkspaceLocalDirectories { private final List privateRoots; - WorkspaceLocalDirectories(ManagedAgentProperties properties) { + WorkspaceLocalDirectories(ManagedAgentSettings properties) { var broker = properties.getRuntimeBroker(); privateRoots = new ArrayList<>(); for (String value : List.of(broker.getStateDirectory(), broker.getWorkspaceCreationRoot())) { @@ -116,7 +115,7 @@ final class WorkspaceLocalDirectories { } private static ManagedAgentException invalid() { - return new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_local_directory", + return new ManagedAgentException(400, "invalid_local_directory", "Select an accessible local project directory outside private Runtime state."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceRecovery.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceRecovery.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceRecovery.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceRecovery.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationAdmission.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationAdmission.java similarity index 83% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationAdmission.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationAdmission.java index 29117ab1e61..df72cecba3f 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationAdmission.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationAdmission.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.JdbcTemplate; @@ -33,14 +33,14 @@ final class WorkspaceMigrationAdmission { return null; } var row = rows.getFirst(); - WorkspaceRecoveryStore.check(tenant.equals(row.get("tenant_id")) && storage.equals(row.get("storage_id")), + com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.check(tenant.equals(row.get("tenant_id")) && storage.equals(row.get("storage_id")), "migration_identity_conflict"); return (String) row.get("fence_operation_id"); } static void requireOpen(JdbcTemplate jdbc, String tenant, String storage) { if (storage != null && owner(jdbc, tenant, storage) != null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } requirePhysicalOpen(jdbc, tenant, storage); } @@ -57,7 +57,7 @@ final class WorkspaceMigrationAdmission { } var alias = aliases.getFirst(); if (!tenant.equals(alias.get("tenant_id")) || !storage.equals(alias.get("storage_id"))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if (alias.get("physical_key") == null || "CSI".equals(alias.get("storage_kind"))) { return; @@ -67,7 +67,7 @@ final class WorkspaceMigrationAdmission { var states = jdbc.queryForList("SELECT mount_state FROM ma_storage_owner WHERE storage_key = ?" + (lock ? " FOR UPDATE" : ""), String.class, physical); if (states.size() != 1 || !"READY".equals(states.getFirst())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -85,7 +85,7 @@ final class WorkspaceMigrationAdmission { row.getString("tenant_id"), row.getString("session_id"), row.getString("workspace_storage_id")}, tenant, session); if (!sessions.isEmpty()) { var saved = sessions.getFirst(); - WorkspaceRecoveryStore.check(tenant.equals(saved[0]) && session.equals(saved[1]), "migration_identity_conflict"); + com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.check(tenant.equals(saved[0]) && session.equals(saved[1]), "migration_identity_conflict"); if (saved[2] != null) { return saved[2]; } @@ -99,7 +99,7 @@ final class WorkspaceMigrationAdmission { return null; } var head = heads.getFirst(); - WorkspaceRecoveryStore.check(tenant.equals(head[0]) && session.equals(head[1]), "migration_identity_conflict"); + com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.check(tenant.equals(head[0]) && session.equals(head[1]), "migration_identity_conflict"); workspace = head[2]; } if (workspace == null) { @@ -112,7 +112,7 @@ final class WorkspaceMigrationAdmission { return null; } var row = registry.getFirst(); - WorkspaceRecoveryStore.check(tenant.equals(row[0]) && expectedWorkspace.equals(row[1]), "migration_identity_conflict"); + com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.check(tenant.equals(row[0]) && expectedWorkspace.equals(row[1]), "migration_identity_conflict"); return row[2]; } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStore.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStore.java index b2fe93792cf..f6e8bb695c9 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStore.java @@ -1,13 +1,14 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RecoveryWorker; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerOperations; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.springframework.jdbc.core.JdbcTemplate; @@ -25,10 +26,15 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; +import static com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.*; import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceRecoveryStore.*; /** Offline storage transition; public Session and journal evidence remain immutable. */ public final class WorkspaceMigrationStore { + private static final com.fasterxml.jackson.databind.ObjectMapper JSON = new com.fasterxml.jackson.databind.ObjectMapper(); + + private final RecoveryWorker worker; + private final JdbcTemplate jdbc; private final PlatformTransactionManager manager; private final TransactionTemplate transaction; @@ -43,7 +49,8 @@ public final class WorkspaceMigrationStore { private static final WorkspaceOperations.Kind KIND = WorkspaceOperations.Kind.STORAGE_MIGRATION; public WorkspaceMigrationStore(JdbcTemplate jdbc, PlatformTransactionManager manager, - WorkspaceStorageGuard guard, RuntimeBindingRepository bindings, byte[] bytes, boolean create) { + WorkspaceStorageGuard guard, RuntimeBindingRepository bindings, byte[] bytes, boolean create, RecoveryWorker worker) { + this.worker = worker; this.jdbc = jdbc; this.manager = manager; this.transaction = new TransactionTemplate(manager); @@ -89,7 +96,7 @@ public final class WorkspaceMigrationStore { check(Files.isDirectory(directory, LinkOption.NOFOLLOW_LINKS) && directory.equals(directory.toRealPath()), code); if ("stateDirectory".equals(field)) { - LocalProcessRuntimeProvisioner.validateStateDirectory(directory); + worker.validateStateDirectory(directory); } } catch (IOException error) { throw failure(code); @@ -141,7 +148,7 @@ public final class WorkspaceMigrationStore { return result; } - public void retire(RuntimeBrokerService broker) { + public void retire(RuntimeBrokerOperations broker) { String state = (String) requireRow(false).get("state"); if (Set.of("RETIRED", "PREPARING", "PREPARED", "COMPLETED").contains(state)) { return; @@ -222,7 +229,7 @@ public final class WorkspaceMigrationStore { guard.discardMigrationTemporary(path("targetRoot"), marker, id); var workerFailure = new AtomicReference(); try { - WorkspaceRecoveryMain.executeWorker(verifyRequest, recovery, (method, params) -> { + worker.execute(verifyRequest, recovery, (method, params) -> { JsonNode reply = recovery.call(method, params); if ("failure".equals(method)) { workerFailure.set(text(params, "code")); @@ -367,7 +374,7 @@ public final class WorkspaceMigrationStore { "migration_work_unsettled"); check(jdbc.queryForObject("SELECT COUNT(*) FROM ma_session_worktree WHERE physical_key IN" + " (SELECT physical_key FROM ma_storage WHERE alias_key = ?) AND state <> 'REMOVED'", Integer.class, - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage)) == 0, + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage)) == 0, "migration_work_unsettled"); long now = jdbc.queryForObject("SELECT UNIX_TIMESTAMP(), EXTRACT(MICROSECOND FROM CURRENT_TIMESTAMP(6))", (row, index) -> Math.addExact(Math.multiplyExact(row.getLong(1), 1000), row.getLong(2) / 1000)); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperations.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperations.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperations.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperations.java index 4788265ede6..1c48351b1eb 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperations.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperations.java @@ -38,7 +38,7 @@ final class WorkspaceOperations { } static String hash(String... fields) { - return com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(fields); + return com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(fields); } Map find(Kind kind, String id, boolean lock) { @@ -60,7 +60,7 @@ final class WorkspaceOperations { Object physical = data.get("physical_key"); if (storage != null) { var aliases = jdbc.queryForList("SELECT storage_kind FROM ma_storage WHERE alias_key = ?", String.class, - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage)); + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage)); if (aliases.size() == 1 && "LOCAL".equals(aliases.getFirst())) { physical = LocalStorageOwners.key(jdbc, tenant, storage); if (kind == Kind.CHILD_WORKSPACE || kind == Kind.SESSION_WORKTREE) { @@ -75,10 +75,10 @@ final class WorkspaceOperations { requireState(kind, state); jdbc.update("INSERT INTO ma_operation (operation_key, scope_key, session_scope_key, operation_kind, operation_id, tenant_id, session_id, target_kind, target_key, target_id, target_revision, storage_key, physical_key, state, phase, delivery_state, request_digest, request_json, result_json, available_at, gmt_create, gmt_modified) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, " + ManagedSchemaTime.SQL_NOW + ", " + ManagedSchemaTime.SQL_NOW + ")", key(kind, id), hash(tenant), data.get("session_id") instanceof String session - ? ManagedSessionStore.sessionScopeKey(tenant, session) : null, kind.name(), id.getBytes(StandardCharsets.UTF_8), + ? com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session) : null, kind.name(), id.getBytes(StandardCharsets.UTF_8), tenant.getBytes(StandardCharsets.UTF_8), data.get("session_id"), data.containsKey("binding_id") ? "RUNTIME_BINDING" : "STORAGE", hash(target), target.getBytes(StandardCharsets.UTF_8), revision, - storage == null ? null : com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), + storage == null ? null : com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), physical, state, data.getOrDefault("phase", state), data.get("delivery_state"), data.getOrDefault("request_digest", hash(request)), request, envelope(data), data.getOrDefault("available_at", 0L)); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperatorRecoveryStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperatorRecoveryStore.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperatorRecoveryStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperatorRecoveryStore.java index aec4db7f712..6b5031bf160 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperatorRecoveryStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceOperatorRecoveryStore.java @@ -1,11 +1,10 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.runtimebroker.HarnessOwnership; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; @@ -263,7 +262,7 @@ public final class WorkspaceOperatorRecoveryStore { if (binding == null || binding.getGeneration() != generation || !binding.getRequest().isManagedContext() || binding.getResourceHandle() == null - || !LocalProcessRuntimeProvisioner.KIND.equals(binding.getResourceHandle().getKind()) + || !com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(binding.getResourceHandle().getKind()) || binding.getResourceHandle().getVersion() != 2 || binding.getProvisionSeed() == null || binding.getLease() == null) { throw blocked(); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleVerifier.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleVerifier.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleVerifier.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleVerifier.java index f83e37bc83d..f8f1ec63067 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleVerifier.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleVerifier.java @@ -1,10 +1,11 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; @@ -40,7 +41,7 @@ public class WorkspaceProfileLifecycleVerifier { void verifyExecutions(OperationRecord operation) { for (ToolExecutionRecord execution : executions(operation)) { if (!hold(execution) && !execution.isSettled()) { - throw WorkspaceLifecycleStore.blocked("workspace_close_execution_unsettled"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_close_execution_unsettled"); } } } @@ -103,7 +104,7 @@ public class WorkspaceProfileLifecycleVerifier { try { generation = Long.parseLong(runtime.path("generation").asText()); } catch (NumberFormatException invalid) { - throw WorkspaceLifecycleStore.blocked("workspace_close_identity_unverified"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_close_identity_unverified"); } if (binding == null || generation < 1 || generation > binding.getGeneration()) { unsettled(); @@ -129,7 +130,7 @@ public class WorkspaceProfileLifecycleVerifier { var backgroundCalls = backgroundCalls(operation); var sourcesByCall = jdbc.queryForList("SELECT request_json FROM ma_operation" + " WHERE operation_kind = 'TOOL_RESULT_PROJECTION' AND session_scope_key = ? AND tenant_id = ? AND session_id = ? FOR UPDATE", String.class, - ManagedSessionStore.sessionScopeKey(operation.tenantId(), operation.sessionId()), operation.tenantId(), operation.sessionId()).stream().map(this::source) + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(operation.tenantId(), operation.sessionId()), operation.tenantId(), operation.sessionId()).stream().map(this::source) .collect(Collectors.groupingBy(ManagedToolResultStore.Source::executionCallId)); var inlineInputs = nativeInlineInputs(operation); for (ToolExecutionRecord execution : executions.values()) { @@ -279,7 +280,7 @@ public class WorkspaceProfileLifecycleVerifier { var refs = jdbc.queryForList("SELECT resource_id, kind, schema_version, byte_length, sha256" + " FROM ma_resource WHERE session_scope_key = ? AND tenant_id = ? AND session_id = ?" + " AND kind = ? AND state = 'REFERENCED' FOR UPDATE", - ManagedSessionStore.sessionScopeKey(operation.tenantId(), operation.sessionId()), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(operation.tenantId(), operation.sessionId()), operation.tenantId(), operation.sessionId(), "managed-tool-input"); for (var row : refs) { var ref = JSON.createObjectNode().put("resourceId", row.get("resource_id").toString()) @@ -339,7 +340,7 @@ public class WorkspaceProfileLifecycleVerifier { try { return JSON.readValue(text, ManagedToolResultStore.Source.class); } catch (java.io.IOException error) { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_resources_unsettled"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_resources_unsettled"); } } @@ -448,7 +449,7 @@ public class WorkspaceProfileLifecycleVerifier { private byte[] readContent(OperationRecord operation, String id, JsonNode expected) { var rows = jdbc.queryForList("SELECT resource_id, kind, schema_version, byte_length, sha256, inline_bytes, storage_kind, state" + " FROM ma_resource WHERE session_scope_key = ? AND tenant_id = ? AND session_id = ?" - + " AND resource_id = ? FOR UPDATE", ManagedSessionStore.sessionScopeKey(operation.tenantId(), operation.sessionId()), + + " AND resource_id = ? FOR UPDATE", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(operation.tenantId(), operation.sessionId()), operation.tenantId(), operation.sessionId(), id); if (rows.size() != 1) { unsettled(); @@ -489,6 +490,6 @@ public class WorkspaceProfileLifecycleVerifier { } private static void unsettled() { - throw WorkspaceLifecycleStore.blocked("workspace_lifecycle_resources_unsettled"); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_resources_unsettled"); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReader.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReader.java similarity index 92% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReader.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReader.java index 88a49eeb1f1..159ea51ed37 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReader.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReader.java @@ -1,6 +1,8 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -14,15 +16,16 @@ import java.util.List; import java.util.Map; import java.util.Set; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceRecoveryStore.JSON; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceRecoveryStore.check; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceRecoveryStore.hash; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceRecoveryStore.parse; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceRecoveryStore.positive; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceRecoveryStore.text; +import static com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.check; +import static com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash; +import static com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.parse; +import static com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.positive; +import static com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.text; /** Original authority reads without acquiring, renewing or quarantining anything. */ final class WorkspaceRecoveryReader { + private static final com.fasterxml.jackson.databind.ObjectMapper JSON = new com.fasterxml.jackson.databind.ObjectMapper(); + private final JdbcTemplate jdbc; private final ToolPublicationObjectStore objects; @@ -59,8 +62,8 @@ final class WorkspaceRecoveryReader { JsonNode resource(JsonNode source, JsonNode ref) { validateRef(ref); JsonNode head = head(source); - String sessionScope = ManagedSessionStore.sessionScopeKey(text(head, "tenantId"), text(head, "sessionId")); - String resourceKey = ManagedSessionStore.resourceKey(sessionScope, text(ref, "resourceId")); + String sessionScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(text(head, "tenantId"), text(head, "sessionId")); + String resourceKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(sessionScope, text(ref, "resourceId")); var rows = WorkspaceOperations.rows(jdbc, "SELECT " + ManagedSchemaProjection.RESOURCE.columns() + " FROM ma_resource WHERE resource_key = ? AND session_scope_key = ? AND tenant_id = ?" + " AND workspace_id = ? AND session_id = ? AND resource_id = ?", resourceKey, sessionScope, text(head, "tenantId"), text(head, "workspaceId"), text(head, "sessionId"), text(ref, "resourceId")); @@ -112,7 +115,7 @@ final class WorkspaceRecoveryReader { + " WHERE resource_key = ? AND scope_key = ? AND resource_id = ? AND capture_state = 'COLLECTED'", Long.class, resourceKey, scope(head), text(ref, "resourceId")); check(collected != null && collected > 0, "resource_corrupt"); - throw WorkspaceRecoveryStore.failure("resource_collected"); + throw com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.failure("resource_collected"); } } else { check("TOOL_PUBLICATION".equals(row.get("storage_kind")) && "REFERENCED".equals(row.get("state")) @@ -135,7 +138,7 @@ final class WorkspaceRecoveryReader { JsonNode head = head(source); var rows = WorkspaceOperations.rows(jdbc, "SELECT publication_id, slot_key FROM ma_resource" + " WHERE resource_key = ? AND scope_key = ? AND resource_id = ?", - ManagedSessionStore.resourceKey(ManagedSessionStore.sessionScopeKey(text(head, "tenantId"), text(head, "sessionId")), text(ref, "resourceId")), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(text(head, "tenantId"), text(head, "sessionId")), text(ref, "resourceId")), scope(head), text(ref, "resourceId")); check(rows.size() == 1, "resource_missing"); var row = rows.getFirst(); @@ -172,7 +175,7 @@ final class WorkspaceRecoveryReader { try (InputStream input = objects.open(key)) { bytes = input.readNBytes((int) length + 1); } catch (IOException error) { - throw WorkspaceRecoveryStore.failure("publication_object_io_failed"); + throw com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.failure("publication_object_io_failed"); } } verifyBytes(bytes, length, (String) row.get("sha256")); @@ -249,7 +252,7 @@ final class WorkspaceRecoveryReader { && "VERIFIED".equals(manifest.get("capture_state")) && publication.get("seal_manifest_sha256").equals(manifest.get("sha256")), "publication_seal_unverified"); verifyBytes(bytes, number(manifest, "byte_length"), (String) manifest.get("sha256")); - JsonNode document = WorkspaceRecoveryStore.parse(bytes); + JsonNode document = com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.parse(bytes); check(document.path("schemaVersion").asInt() == 1 && document.path("seals").isArray() && document.path("seals").size() <= 2, "publication_seal_invalid"); for (JsonNode seal : document.path("seals")) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStore.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStore.java similarity index 92% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStore.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStore.java index 2ed3d6d3e1a..3c2e1ef1c7b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStore.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStore.java @@ -1,16 +1,15 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import org.apache.commons.codec.digest.DigestUtils; import org.springframework.dao.PessimisticLockingFailureException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.PlatformTransactionManager; @@ -28,11 +27,13 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.UUID; + +import static com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.*; /** Offline derived metadata only; no Session writer or authority mutation. */ -public final class WorkspaceRecoveryStore { - static final ObjectMapper JSON = new ObjectMapper(); +public final class WorkspaceRecoveryStore implements com.alibaba.qwen.code.managedagent.contracts.workspace.RecoveryWorker.Session { + private static final com.fasterxml.jackson.databind.ObjectMapper JSON = new com.fasterxml.jackson.databind.ObjectMapper(); + private static final RequestDigests SKILL_DIGESTS = new RequestDigests(); private static final int PAGE = 32; private final JdbcTemplate jdbc; @@ -232,7 +233,7 @@ public final class WorkspaceRecoveryStore { }); } - void workerFailed() { + public void workerFailed() { Map current = operation(id); if (current != null && current.get("last_error_code") == null) { recordFailure("worker_failed"); @@ -550,7 +551,7 @@ public final class WorkspaceRecoveryStore { + " AND status IN ('ACCEPTED', 'RUNNING', 'CANCELLING')", tenant, session) == 0, "source_drift"); check(count(jdbc, "SELECT COUNT(*) FROM ma_operation WHERE session_scope_key = ?" + " AND operation_kind IN ('CLOSE', 'ARCHIVE', 'DELETE', 'ACTION_RESPONSE', 'CWD_CHANGE', 'TASK_CANCEL')" - + " AND state NOT IN ('COMPLETED', 'FAILED')", ManagedSessionStore.sessionScopeKey(tenant, session)) == 0, "source_drift"); + + " AND state NOT IN ('COMPLETED', 'FAILED')", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session)) == 0, "source_drift"); ObjectNode source = JSON.createObjectNode().put("sessionId", session); ObjectNode binding = source.putObject("binding").put("tenantId", tenant); fields(binding, row, "workspaceId", "workspace_id", "workspaceGeneration", "workspace_generation", @@ -589,8 +590,8 @@ public final class WorkspaceRecoveryStore { // Missing keys gap-lock other tenants; the retention authority lock prevents new writers here. boolean lockHead = lockSources && count(jdbc, "SELECT COUNT(*) FROM ma_session_head WHERE tenant_key = ? AND harness_key = ?", - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.harnessDrainKey(tenant), - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.harnessDrainKey(session)) != 0; + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.harnessDrainKey(tenant), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.harnessDrainKey(session)) != 0; var heads = jdbc.query("SELECT " + ManagedSchemaProjection.SESSION_HEAD.columns() + " FROM ma_session_head WHERE tenant_key = ? AND harness_key = ? AND head_kind = 'MANAGED_SESSION'" + (lockHead ? " FOR UPDATE" : ""), (result, index) -> { @@ -600,8 +601,8 @@ public final class WorkspaceRecoveryStore { value.put("writer_lease_fingerprint", lease == null ? null : LocalDateTime.ofInstant(lease.toInstant(), java.time.ZoneOffset.UTC).toString()); return value; - }, com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.harnessDrainKey(tenant), - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.harnessDrainKey(session)); + }, com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.harnessDrainKey(tenant), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.harnessDrainKey(session)); check(heads.size() <= 1, "source_drift"); if (heads.isEmpty()) { source.putNull("head"); @@ -624,7 +625,7 @@ public final class WorkspaceRecoveryStore { "latestCheckpointResourceId", "latest_checkpoint_resource_id", "compactedThroughRevision", "compacted_through_revision", "recoveryStatus", "recovery_status", "recoveryDetailCode", "recovery_detail_code"); } - String privateScope = ManagedSessionStore.sessionScopeKey(tenant, session); + String privateScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String currentRead = lockSources ? " FOR UPDATE" : ""; check(jdbc.queryForList("SELECT r.resource_key FROM ma_resource r JOIN ma_resource_ref f" + " ON f.resource_key = r.resource_key AND f.session_scope_key = r.session_scope_key" @@ -796,87 +797,6 @@ public final class WorkspaceRecoveryStore { } } - static JsonNode parse(String text) { - return parse(text.getBytes(StandardCharsets.UTF_8)); - } - - static JsonNode parse(byte[] bytes) { - check(bytes.length <= 16 * 1024 * 1024, "message_too_large"); - return ToolPublicationContract.readJson(bytes); - } - - static String text(JsonNode node, String field) { - JsonNode value = node.path(field); - check(value.isTextual() && !value.asText().isEmpty() && value.asText().length() <= 8192, "invalid_request"); - return value.asText(); - } - - static long positive(JsonNode node, String field) { - JsonNode value = node.path(field); - check(value.isIntegralNumber() && value.canConvertToLong() && value.asLong() > 0 - && value.asLong() <= ManagedSessionStoreModels.MAX_SAFE_COUNTER, "invalid_request"); - return value.asLong(); - } - - static String uuid(JsonNode node, String field) { - String value = text(node, field); - try { - check(UUID.fromString(value).toString().equals(value), "invalid_request"); - } catch (IllegalArgumentException error) { - throw failure("invalid_request"); - } - return value; - } - - static String hash(String value) { - return hash(value.getBytes(StandardCharsets.UTF_8)); - } - - static String hash(byte[] bytes) { - return HexFormat.of().formatHex(digest().digest(bytes)); - } - - private static MessageDigest digest() { - try { - return DigestUtils.getSha256Digest(); - } catch (IllegalArgumentException error) { - throw new IllegalStateException(error); - } - } - - static void check(boolean condition, String code) { - if (!condition) { - throw failure(code); - } - } - - // The deepest cause is not the verdict: a structural refusal keeps its - // service code even when the discarded I/O cause sits deeper in the chain. - public static String errorCode(Throwable error) { - for (Throwable current = error; current != null && current != current.getCause(); - current = current.getCause()) { - if (current instanceof RecoveryFailure failure) { - return failure.code; - } - if (current instanceof RuntimeBrokerException failure) { - return failure.getCode(); - } - } - return "migration_failed"; - } - - static RecoveryFailure failure(String code) { - return new RecoveryFailure(code); - } - - static final class RecoveryFailure extends IllegalStateException { - final String code; - RecoveryFailure(String code) { - super("Workspace recovery: " + code); - this.code = code; - } - } - private record SourceCut(String digest, long count) { } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuard.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuard.java similarity index 77% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuard.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuard.java index 3feadb96fe2..c5ba8a31220 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuard.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuard.java @@ -1,16 +1,15 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader.Identity; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.provisioning.MacOsHostIdentity; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.codec.digest.HmacAlgorithms; -import org.apache.commons.codec.digest.HmacUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.JdbcTemplate; @@ -30,46 +29,32 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.attribute.FileTime; -import java.security.GeneralSecurityException; -import java.time.DateTimeException; -import java.time.Instant; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.ResolverStyle; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.UUID; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Component public class WorkspaceStorageGuard { private static final String MARKER = ".qwen-managed-storage.json"; - private static final DateTimeFormatter LINUX_BIRTH_TIME_FORMAT = DateTimeFormatter - .ofPattern("uuuu-MM-dd HH:mm:ss.SSSSSSSSS xx", Locale.ROOT) - .withResolverStyle(ResolverStyle.STRICT); + private final JdbcTemplate jdbc; private final TransactionTemplate transaction; private final Map roots; private final WorkspaceCreationRoots createdRoots; private final boolean enabled; - private final IdentityReader identities; + private final StorageIdentityReader identities; private SshWorkspaceStore ssh; private final ObjectMapper json = new ObjectMapper(); @Autowired public WorkspaceStorageGuard(@Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, @Qualifier("managedAgentTransactionManager") PlatformTransactionManager manager, - ManagedAgentProperties properties) { - this(jdbc, manager, properties, WorkspaceStorageGuard::nativeIdentity); - if (enabled && !List.of("Linux", "Mac OS X").contains(System.getProperty("os.name"))) { + ManagedAgentSettings properties, StorageIdentityReader identities) { + if (properties.getRuntimeBroker().isVerifiedWorkspaceRecoveryEnabled() + && !List.of("Linux", "Mac OS X").contains(System.getProperty("os.name"))) { throw new IllegalStateException("Verified Workspace recovery requires Linux or macOS"); } - } - - WorkspaceStorageGuard(JdbcTemplate jdbc, PlatformTransactionManager manager, - ManagedAgentProperties properties, IdentityReader identities) { this.jdbc = jdbc; this.transaction = new TransactionTemplate(manager); this.enabled = properties.getRuntimeBroker().isVerifiedWorkspaceRecoveryEnabled(); @@ -87,7 +72,7 @@ public class WorkspaceStorageGuard { } @Autowired - void configureSsh(SshWorkspaceStore store, ManagedAgentProperties properties) { + void configureSsh(SshWorkspaceStore store, ManagedAgentSettings properties) { this.ssh = properties.getRuntimeBroker().getSsh().getNodes().isEmpty() ? null : store; } @@ -148,7 +133,7 @@ public class WorkspaceStorageGuard { long revision, String operationId) { requireOwnedRoot(tenantId, storageId); if (!enabled || !validId(operationId)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return authorityRead(() -> { WorkspaceStorageKindGuard.lockDomain(jdbc, tenantId); @@ -158,7 +143,7 @@ public class WorkspaceStorageGuard { Path original = root(tenantId, storageId); requireMatching(saved, identity(original), tenantId, storageId); if (!marker(saved).equals(readMarker(original))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return new RecoveryRegistration(tenantId, storageId, saved.registrationId(), revision, operationId, saved.root(), saved.hostId(), saved.device(), saved.inode(), saved.birthTime()); @@ -176,7 +161,7 @@ public class WorkspaceStorageGuard { saved.revision(), saved.operationId(), saved.root(), saved.hostId(), saved.device(), saved.inode(), saved.birthTime()); if (!metadata.equals(expected) || !physical.equals(LocalStorageOwners.physicalKey(new Identity( saved.root(), saved.hostId(), saved.device(), saved.inode(), saved.birthTime())))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return null; }); @@ -195,7 +180,7 @@ public class WorkspaceStorageGuard { || saved.bindingId() != null || saved.runtimeGeneration() != null || saved.runtimeSessionId() != null || !validId(saved.registrationId()) || saved.completedOperationId() != null || WorkspaceExecutionStore.hasExecutionMembers(jdbc, physical)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -225,7 +210,7 @@ public class WorkspaceStorageGuard { void verifyMaintenanceRecovery(ContextBinding binding) { WorkspaceStorageKindGuard.requireLocalAlias(jdbc, binding.getTenantId(), binding.getStorageId()); if (!enabled) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } verify(binding.getTenantId(), binding.getStorageId(), binding.getCwdRelative(), true); } @@ -236,7 +221,7 @@ public class WorkspaceStorageGuard { Path verifiedRoot(ContextBinding binding) { if (!enabled) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } WorkspaceStorageKindGuard.requireLocalAlias(jdbc, binding.getTenantId(), binding.getStorageId()); verify(binding); @@ -252,7 +237,7 @@ public class WorkspaceStorageGuard { void registerCreated(String tenantId, String storageId, String operationId) { var root = createdRoots.registration(tenantId, storageId, true); if (root == null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } register(tenantId, storageId, operationId, root.path(), root.expectedIdentity()); } @@ -263,12 +248,12 @@ public class WorkspaceStorageGuard { private void register(String tenantId, String storageId, String operationId, Path root, Identity expectedIdentity) { if (!enabled || !validId(operationId)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } WorkspaceStorageKindGuard.requireFreshTransaction(); Identity identity = identity(root); if (expectedIdentity != null && !expectedIdentity.equals(identity)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } String key = LocalStorageOwners.physicalKey(identity); Registration prepared = transaction.execute(status -> { @@ -277,7 +262,7 @@ public class WorkspaceStorageGuard { if (expectedIdentity != null) { Registration existing = row(key, true); if (existing != null && (!tenantId.equals(existing.tenantId()) || !storageId.equals(existing.storageId()))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } WorkspaceStorageKindGuard.requireLocalAlias(jdbc, tenantId, storageId); @@ -290,14 +275,14 @@ public class WorkspaceStorageGuard { requireNoExecutionMembers(key); if (row == null || row.holderKey() != null || row.bindingId() != null || row.runtimeGeneration() != null || row.runtimeSessionId() != null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if ("READY".equals(row.state()) && row.operationId() == null && row.revision() > 0) { requireMatching(row, identity, tenantId, storageId); return row; } if (!"UNVERIFIED".equals(row.state())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if (row.operationId() == null && row.registrationId() == null) { String registrationId = UUID.randomUUID().toString(); @@ -310,20 +295,20 @@ public class WorkspaceStorageGuard { return row(key, true); } if (!operationId.equals(row.operationId())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } requireMatching(row, identity, tenantId, storageId); return row; }); if (prepared == null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Marker marker = marker(prepared); Path metadataRoot = markerRoot(marker, root, !"READY".equals(prepared.state())); if ("READY".equals(prepared.state())) { requireMatching(prepared, identity(root), tenantId, storageId); if (!marker.equals(readMarker(metadataRoot))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } else { publishMarker(metadataRoot, marker); @@ -336,18 +321,18 @@ public class WorkspaceStorageGuard { requireNoExecutionMembers(key); if (row == null || row.holderKey() != null || row.bindingId() != null || row.runtimeGeneration() != null || row.runtimeSessionId() != null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } requireMatching(row, identity(root), tenantId, storageId); if (!marker.equals(marker(row)) || !marker.equals(readMarker(metadataRoot))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } if ("READY".equals(row.state()) && row.operationId() == null && row.revision() > 0) { completeAlias(tenantId, storageId, key); return; } if (!"UNVERIFIED".equals(row.state()) || !operationId.equals(row.operationId())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } int changed = jdbc.update("UPDATE ma_storage_owner" + " SET mount_state = 'READY', mount_revision = mount_revision + 1," @@ -355,7 +340,7 @@ public class WorkspaceStorageGuard { + " AND mount_state = 'UNVERIFIED' AND mount_operation_id = ?" + " AND mount_revision = 0", operationId, key, operationId); if (changed != 1) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } completeAlias(tenantId, storageId, key); }); @@ -368,29 +353,29 @@ public class WorkspaceStorageGuard { + " ON DUPLICATE KEY UPDATE gmt_modified = CASE WHEN physical_key IS NULL OR registration_json IS NULL OR storage_kind = 'UNREGISTERED' THEN VALUES(gmt_modified) ELSE gmt_modified END, physical_key = COALESCE(physical_key, VALUES(physical_key))," + " storage_kind = CASE WHEN storage_kind = 'UNREGISTERED' THEN 'LOCAL' ELSE storage_kind END," + " registration_json = COALESCE(registration_json, VALUES(registration_json))", - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageFenceKey(tenant), - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageFenceKey(storage), tenant, storage, physical, request); + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageFenceKey(tenant), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageFenceKey(storage), tenant, storage, physical, request); var alias = LocalStorageOwners.alias(jdbc, tenant, storage, true); if (alias == null || !physical.equals(alias.get("physical_key")) || !"LOCAL".equals(alias.get("storage_kind")) || !List.of("FENCE_ONLY", "REGISTERED").contains(alias.get("registration_state")) || alias.get("fence_operation_id") != null - || !operation.equals(WorkspaceRecoveryStore.parse((String) alias.get("registration_json")).path("operationId").asText())) { - throw WorkspaceExecutionStore.unavailable(); + || !operation.equals(com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.parse((String) alias.get("registration_json")).path("operationId").asText())) { + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } private void completeAlias(String tenant, String storage, String physical) { if (jdbc.update("UPDATE ma_storage SET gmt_modified = CASE WHEN registration_state <> 'REGISTERED' OR registration_revision IS NULL THEN " + ManagedSchemaTime.SQL_NOW + " ELSE gmt_modified END, registration_state = 'REGISTERED', registration_revision = COALESCE(registration_revision, 1) WHERE alias_key = ? AND physical_key = ? AND storage_kind = 'LOCAL'", - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), physical) != 1) { - throw WorkspaceExecutionStore.unavailable(); + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), physical) != 1) { + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } public void fence(String tenantId, String storageId, long revision, String operationId) { requireOwnedRoot(tenantId, storageId); if (!enabled || !validId(operationId)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } transaction.executeWithoutResult(status -> { WorkspaceStorageKindGuard.lockDomain(jdbc, tenantId); @@ -399,9 +384,9 @@ public class WorkspaceStorageGuard { if (migration != null) { var saved = new WorkspaceOperations(jdbc, tenantId).find( WorkspaceOperations.Kind.STORAGE_MIGRATION, migration, false); - if (saved == null || !operationId.equals(WorkspaceRecoveryStore.parse((String) saved.get("request_json")) + if (saved == null || !operationId.equals(com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.parse((String) saved.get("request_json")) .path("fenceOperationId").asText())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } String physical = LocalStorageOwners.lockCovered(jdbc, tenantId, storageId); @@ -421,7 +406,7 @@ public class WorkspaceStorageGuard { + " AND runtime_generation IS NULL AND runtime_session_id IS NULL", operationId, physical, revision); if (changed != 1) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } }); } @@ -430,14 +415,14 @@ public class WorkspaceStorageGuard { String operationId) { requireOwnedRoot(tenantId, storageId); if (!enabled || !validId(operationId)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Path root = root(tenantId, storageId); transaction.executeWithoutResult(status -> { WorkspaceStorageKindGuard.lockDomain(jdbc, tenantId); WorkspaceStorageKindGuard.requireLocalAlias(jdbc, tenantId, storageId); if (WorkspaceMigrationAdmission.owner(jdbc, tenantId, storageId) != null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } String physical = LocalStorageOwners.lockCovered(jdbc, tenantId, storageId); Registration row = row(physical, true); @@ -446,7 +431,7 @@ public class WorkspaceStorageGuard { && operationId.equals(row.completedOperationId())) { requireMatching(row, identity(root), tenantId, storageId); if (!marker(row).equals(readMarker(root))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return; } @@ -455,11 +440,11 @@ public class WorkspaceStorageGuard { || row.revision() != revision || !operationId.equals(row.operationId()) || row.holderKey() != null || row.bindingId() != null || row.runtimeGeneration() != null || row.runtimeSessionId() != null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } requireMatching(row, identity(root), tenantId, storageId); if (!marker(row).equals(readMarker(root))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } int changed = jdbc.update("UPDATE ma_storage_owner" + " SET mount_state = 'READY', mount_revision = mount_revision + 1," @@ -468,7 +453,7 @@ public class WorkspaceStorageGuard { + " AND mount_revision = ? AND mount_operation_id = ?", operationId, physical, revision, operationId); if (changed != 1) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } }); } @@ -479,28 +464,28 @@ public class WorkspaceStorageGuard { Registration row = row(key(tenantId, storageId), lock); if (row == null || !"READY".equals(row.state()) || row.operationId() != null || row.revision() <= 0) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } requireMatching(row, actual, tenantId, storageId); verifyHistory(tenantId, storageId, false); if (!marker(row).equals(readMarker(markerRoot(marker(row), root, false)))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Path directory = root.resolve(cwd).normalize(); try { if (!directory.startsWith(root) || !Files.isDirectory(directory, LinkOption.NOFOLLOW_LINKS) || !directory.toRealPath().equals(directory)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } catch (IOException | IllegalArgumentException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } private void requireNoExecutionMembers(String physical) { if (WorkspaceExecutionStore.hasExecutionMembers(jdbc, physical)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -514,27 +499,27 @@ public class WorkspaceStorageGuard { try { expected = json.readValue((String) migrations.getFirst().get("history_identity_json"), Identity.class); } catch (IOException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } String home = System.getenv("QWEN_HOME"); if (home == null || !Path.of(home).isAbsolute() || !Path.of(home).resolve("file-history").toString().equals(expected.root())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } try { if (!Path.of(home).toRealPath().equals(Path.of(home)) || !expected.equals(identities.read(Path.of(expected.root())))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } catch (NoSuchFileException | AccessDeniedException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } catch (IOException error) { if (probe) { - throw WorkspaceExecutionStore.unavailableTransient(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailableTransient(error); } - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } catch (RuntimeException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } } @@ -566,19 +551,19 @@ public class WorkspaceStorageGuard { try { actual = identities.read(root); } catch (IOException error) { - throw WorkspaceExecutionStore.unavailableTransient(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailableTransient(error); } catch (RuntimeException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Registration row = row(key(tenantId, storageId), false); if (row == null || !"READY".equals(row.state()) || row.operationId() != null || row.revision() <= 0) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } requireMatching(row, actual, tenantId, storageId); verifyHistory(tenantId, storageId, true); if (!marker(row).equals(readMarkerProbe(markerRoot(marker(row), root, false)))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Path directory = root.resolve(cwd).normalize(); try { @@ -587,39 +572,39 @@ public class WorkspaceStorageGuard { BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS).isDirectory() || !directory.toRealPath().equals(directory)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } directory.getFileSystem().provider().checkAccess(directory, AccessMode.READ, AccessMode.EXECUTE); } catch (NoSuchFileException | AccessDeniedException | SecurityException | IllegalArgumentException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } catch (IOException error) { - throw WorkspaceExecutionStore.unavailableTransient(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailableTransient(error); } } private Marker readMarkerProbe(Path root) { Path file = root.resolve(MARKER); if (!Files.isRegularFile(file, LinkOption.NOFOLLOW_LINKS)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ, LinkOption.NOFOLLOW_LINKS)) { if (channel.size() > 4096) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } ByteBuffer bytes = ByteBuffer.allocate((int) channel.size()); while (bytes.hasRemaining()) { if (channel.read(bytes) <= 0) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } return json.readValue(bytes.array(), Marker.class); } catch (JsonProcessingException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } catch (IOException error) { - throw WorkspaceExecutionStore.unavailableTransient(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailableTransient(error); } } @@ -632,7 +617,7 @@ public class WorkspaceStorageGuard { + " FROM ma_storage_owner WHERE storage_key = ? AND storage_kind = 'LOCAL'" + (lock ? " FOR UPDATE" : ""), (result, index) -> { if (!key.equals(result.getString("physical_key"))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return new Registration( result.getString("tenant_id"), result.getString("storage_id"), @@ -658,11 +643,11 @@ public class WorkspaceStorageGuard { Registration saved = row(physical, true); if (saved == null || saved.revision() != revision || !List.of("READY", "FENCED").contains(saved.state()) || "FENCED".equals(saved.state()) && !fence.equals(saved.operationId())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } requireMatching(saved, identity(root(tenant, storage)), tenant, storage); if (!marker(saved).equals(readMarker(root(tenant, storage)))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return new RecoveryRegistration(tenant, storage, saved.registrationId(), revision, fence, saved.root(), saved.hostId(), saved.device(), saved.inode(), saved.birthTime()); @@ -679,7 +664,7 @@ public class WorkspaceStorageGuard { return json.writeValueAsBytes(new Marker(2, tenant, storage, target.root(), target.hostId(), target.device(), target.inode(), target.birthTime(), registration)); } catch (IOException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -696,18 +681,18 @@ public class WorkspaceStorageGuard { if (!Files.isRegularFile(temporary, LinkOption.NOFOLLOW_LINKS) || ((Number) Files.getAttribute(temporary, "unix:nlink", LinkOption.NOFOLLOW_LINKS)).longValue() != 1 || Files.size(temporary) > expected.length) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } byte[] partial = Files.readAllBytes(temporary); if (!java.util.Arrays.equals(partial, java.util.Arrays.copyOf(expected, partial.length))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Files.delete(temporary); try (FileChannel directory = FileChannel.open(root, StandardOpenOption.READ)) { directory.force(true); } } catch (IOException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -718,7 +703,7 @@ public class WorkspaceStorageGuard { expected = json.readValue(expectedBytes, Marker.class); Registration provenance = row(key(original.tenantId(), original.storageId()), false); if (provenance == null || !original.registrationId().equals(provenance.registrationId())) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Marker old = marker(provenance); Marker current = readMarker(root); @@ -726,7 +711,7 @@ public class WorkspaceStorageGuard { return; } if (!old.equals(current)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } Path temporary = migrationTemporary(root, operation); if (Files.exists(temporary, LinkOption.NOFOLLOW_LINKS)) { @@ -734,7 +719,7 @@ public class WorkspaceStorageGuard { || ((Number) Files.getAttribute(temporary, "unix:nlink", LinkOption.NOFOLLOW_LINKS)).longValue() != 1 || Files.size(temporary) != expectedBytes.length || !java.util.Arrays.equals(Files.readAllBytes(temporary), expectedBytes)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } else { try (FileChannel channel = FileChannel.open(temporary, StandardOpenOption.CREATE_NEW, @@ -760,10 +745,10 @@ public class WorkspaceStorageGuard { directory.force(true); } if (!expected.equals(readMarker(root))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } catch (IOException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -776,12 +761,12 @@ public class WorkspaceStorageGuard { if (!source.hostId().equals(target.hostId()) || !target.equals(identity(Path.of(target.root()))) || !new String(migrationMarker(source.tenantId(), source.storageId(), target, registration), StandardCharsets.UTF_8) .equals(markerJson(Path.of(target.root())))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } String previous = LocalStorageOwners.lockCovered(jdbc, source.tenantId(), source.storageId()); String physical = LocalStorageOwners.physicalKey(target); if (physical.equals(previous)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } int created = jdbc.update("INSERT INTO ma_storage_owner (storage_key, physical_key, storage_kind, tenant_id, storage_id," + " mount_root, mount_host_id, mount_device, mount_inode, mount_birth_time, mount_registration_id," @@ -790,7 +775,7 @@ public class WorkspaceStorageGuard { target.birthTime(), registration, source.mountRevision() + 1, operation); if (created != 1 || jdbc.update("UPDATE ma_storage SET gmt_modified = " + ManagedSchemaTime.SQL_NOW + ", physical_key = ?, registration_revision = registration_revision + 1 WHERE alias_key = ? AND physical_key = ? AND storage_kind = 'LOCAL'", physical, JdbcRuntimeBindingRepository.storageAliasKey(source.tenantId(), source.storageId()), previous) != 1) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } }); } @@ -799,7 +784,7 @@ public class WorkspaceStorageGuard { try { return json.writeValueAsString(readMarker(root)); } catch (IOException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -807,7 +792,7 @@ public class WorkspaceStorageGuard { try { LocalStorageOwners.requireNonOverlapping(jdbc, identity); } catch (com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException conflict) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -819,7 +804,7 @@ public class WorkspaceStorageGuard { private Path root(String tenantId, String storageId) { Path root = configuredRoot(tenantId, storageId); if (root == null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return root; } @@ -828,103 +813,10 @@ public class WorkspaceStorageGuard { try { return identities.read(root); } catch (IOException | RuntimeException error) { - throw WorkspaceExecutionStore.unavailable(error); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(error); } } - private static Identity nativeIdentity(Path root) throws IOException { - String os = System.getProperty("os.name"); - if (!List.of("Linux", "Mac OS X").contains(os)) { - throw new IOException("Unsupported mount identity provider"); - } - Map attributes = Files.readAttributes(root, - "unix:isDirectory,dev,ino,creationTime,lastModifiedTime", LinkOption.NOFOLLOW_LINKS); - if (!Boolean.TRUE.equals(attributes.get("isDirectory")) || !root.isAbsolute() - || !root.equals(root.toRealPath())) { - throw new IOException("Workspace root is not canonical"); - } - String device = Long.toUnsignedString(((Number) attributes.get("dev")).longValue()); - String inode = Long.toUnsignedString(((Number) attributes.get("ino")).longValue()); - String birthTime = "Mac OS X".equals(os) ? MacOsHostIdentity.birthTime(root, device, inode) - : linuxBirthTime(root, device, inode, (FileTime) attributes.get("creationTime"), - (FileTime) attributes.get("lastModifiedTime")); - String machineId = "Mac OS X".equals(os) ? MacOsHostIdentity.machineId() - : Files.readString(Path.of("/etc/machine-id")).strip(); - if (machineId.isEmpty() || machineId.length() > 256) { - throw new IOException("Host identity is unavailable"); - } - String host; - try { - host = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, - machineId.getBytes(StandardCharsets.UTF_8)).hmacHex( - "Qwen-Code/verified-workspace/v2".getBytes(StandardCharsets.UTF_8)); - } catch (IllegalArgumentException error) { - if (error.getCause() instanceof GeneralSecurityException cause) { - throw new IOException("Host identity is unavailable", cause); - } - throw error; - } - return new Identity(root.toString(), host, device, inode, birthTime); - } - - private static String linuxBirthTime(Path root, String device, String inode, - FileTime creation, FileTime modified) throws IOException { - if (!creation.equals(modified) || creation.compareTo(FileTime.fromMillis(0)) <= 0) { - return verifiedBirthTime(creation, modified); - } - // A marker write can share the birth clock tick. Confirm the exact root, - // rather than treating the JDK's possible mtime fallback as birth evidence. - ProcessBuilder builder = new ProcessBuilder("/usr/bin/stat", "-c", "%d|%i|%w", "--", root.toString()) - .redirectError(ProcessBuilder.Redirect.DISCARD); - builder.environment().put("LC_ALL", "C"); - builder.environment().put("TZ", "UTC"); - Process process = builder.start(); - try { - process.getOutputStream().close(); - if (!process.waitFor(5, TimeUnit.SECONDS) || process.exitValue() != 0) { - throw new IOException("Workspace birth time probe failed"); - } - byte[] output = process.getInputStream().readNBytes(257); - if (output.length > 256) { - throw new IOException("Workspace birth time probe exceeds its size limit"); - } - return verifiedNativeBirthTime(new String(output, StandardCharsets.UTF_8).strip(), - device, inode, creation); - } catch (InterruptedException error) { - Thread.currentThread().interrupt(); - throw new IOException("Workspace birth time probe interrupted", error); - } finally { - process.destroyForcibly(); - process.getInputStream().close(); - process.getErrorStream().close(); - } - } - - static String verifiedNativeBirthTime(String output, String device, String inode, - FileTime creation) throws IOException { - String[] fields = output.split("\\|", -1); - if (fields.length != 3 || !device.equals(fields[0]) || !inode.equals(fields[1])) { - throw new IOException("Workspace storage identity changed or is unavailable"); - } - try { - Instant birth = OffsetDateTime.parse(fields[2], LINUX_BIRTH_TIME_FORMAT).toInstant(); - if (!birth.isAfter(Instant.EPOCH) || !birth.equals(creation.toInstant())) { - throw new IOException("Workspace birth time is unavailable or ambiguous"); - } - return creation.toInstant().toString(); - } catch (DateTimeException error) { - throw new IOException("Workspace native birth time is invalid", error); - } - } - - static String verifiedBirthTime(FileTime creation, FileTime modified) throws IOException { - // OpenJDK 21 substitutes mtime or epoch when Linux does not provide birth time. - if (creation.compareTo(FileTime.fromMillis(0)) <= 0 || creation.equals(modified)) { - throw new IOException("Workspace birth time is unavailable or ambiguous"); - } - return creation.toInstant().toString(); - } - private void requireMatching(Registration row, Identity identity, String tenantId, String storageId) { var alias = LocalStorageOwners.alias(jdbc, tenantId, storageId, false); @@ -933,7 +825,7 @@ public class WorkspaceStorageGuard { || !identity.root().equals(row.root()) || !identity.hostId().equals(row.hostId()) || !identity.device().equals(row.device()) || !identity.inode().equals(row.inode()) || !identity.birthTime().equals(row.birthTime()) || row.registrationId() == null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -961,22 +853,22 @@ public class WorkspaceStorageGuard { private Marker readMarker(Path root) { Path file = root.resolve(MARKER); if (!Files.isRegularFile(file, LinkOption.NOFOLLOW_LINKS)) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ, LinkOption.NOFOLLOW_LINKS)) { if (channel.size() > 4096) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } ByteBuffer bytes = ByteBuffer.allocate((int) channel.size()); while (bytes.hasRemaining()) { if (channel.read(bytes) <= 0) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } return json.readValue(bytes.array(), Marker.class); } catch (IOException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -997,14 +889,14 @@ public class WorkspaceStorageGuard { Files.createLink(file, temporary); } catch (java.nio.file.FileAlreadyExistsException ignored) { if (!expected.equals(readMarker(root))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } try (FileChannel directory = FileChannel.open(root, StandardOpenOption.READ)) { directory.force(true); } } catch (IOException error) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } finally { try { Files.deleteIfExists(temporary); @@ -1013,7 +905,7 @@ public class WorkspaceStorageGuard { } } if (!expected.equals(readMarker(root))) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -1029,13 +921,6 @@ public class WorkspaceStorageGuard { return LocalStorageOwners.key(jdbc, tenantId, storageId); } - interface IdentityReader { - Identity read(Path root) throws IOException; - } - - record Identity(String root, String hostId, String device, String inode, String birthTime) { - } - private record Storage(String tenantId, String storageId) { } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageKindGuard.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageKindGuard.java similarity index 77% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageKindGuard.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageKindGuard.java index adcc8c9bad2..87421cdc68f 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageKindGuard.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageKindGuard.java @@ -1,6 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -11,7 +12,7 @@ final class WorkspaceStorageKindGuard { static void requireFreshTransaction() { if (TransactionSynchronizationManager.isActualTransactionActive()) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } @@ -31,14 +32,14 @@ final class WorkspaceStorageKindGuard { static void requireLocalAlias(JdbcTemplate jdbc, String tenantId, String storageId) { if (jdbc.queryForObject("SELECT COUNT(*) FROM ma_storage WHERE alias_key = ? AND storage_kind IN ('CSI', 'SSH_LOCAL', 'EXTERNAL')", Long.class, WorkspaceCsiRegistration.aliasKey(tenantId, storageId)) != 0) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } static void requireExecutionAlias(JdbcTemplate jdbc, String tenantId, String storageId) { if (jdbc.queryForObject("SELECT COUNT(*) FROM ma_storage WHERE alias_key = ? AND storage_kind IN ('CSI', 'EXTERNAL')", Long.class, WorkspaceCsiRegistration.aliasKey(tenantId, storageId)) != 0) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/CancelEvidence.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/evidence/CancelEvidence.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/CancelEvidence.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/evidence/CancelEvidence.java index 6fef23d7da6..5f0a2503542 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/session/CancelEvidence.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/evidence/CancelEvidence.java @@ -1,10 +1,10 @@ -package com.alibaba.qwen.code.managedagent.session; +package com.alibaba.qwen.code.managedagent.persistence.evidence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.session.AcceptanceEvidence; import com.alibaba.qwen.code.managedagent.model.StoreModels.AcceptanceVerdict; import com.alibaba.qwen.code.managedagent.model.StoreModels.ParkedOperation; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -46,7 +46,7 @@ public class CancelEvidence implements AcceptanceEvidence { + " AND o.target_kind = 'TURN' AND o.state = 'RECOVERY_BLOCKED'" + " AND o.delivery_state = 'BLOCKED' AND o.claim_owner IS NULL", (row, index) -> new Target(row.getString(1), row.getString(2), row.getString(3)), - ManagedSessionStore.sessionScopeKey(parked.tenantId(), parked.sessionId()), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(parked.tenantId(), parked.sessionId()), parked.tenantId(), parked.sessionId(), parked.operationId()); if (targets.isEmpty()) { return Optional.empty(); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/JdbcRuntimeBrokerSchema.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/JdbcRuntimeBrokerSchema.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/JdbcRuntimeBrokerSchema.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/JdbcRuntimeBrokerSchema.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchema.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchema.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchema.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchema.java index e2ebdc086f6..a8a0f9e406c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchema.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchema.java @@ -1,13 +1,13 @@ package com.alibaba.qwen.code.runtimebroker.managedschema; +import javax.sql.DataSource; + import java.io.IOException; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.SQLException; import java.util.Objects; -import javax.sql.DataSource; - /** Explicit initializer for the fresh Managed database, independent of legacy bootstrap. */ public final class ManagedSchema { public static final String FLYWAY_LOCATION = "classpath:db/managed-schema"; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalog.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalog.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalog.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalog.java index 72eb4cf2b41..a141e9488d8 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalog.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalog.java @@ -1,5 +1,7 @@ package com.alibaba.qwen.code.runtimebroker.managedschema; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract; + import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.SQLException; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaProjection.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaProjection.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaProjection.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaProjection.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaRows.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaRows.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaRows.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaRows.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTime.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTime.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTime.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTime.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolReservation.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiNativeToolReservation.java similarity index 86% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolReservation.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiNativeToolReservation.java index 96833cd6354..c83f3ed1e79 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolReservation.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiNativeToolReservation.java @@ -1,24 +1,15 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; +import com.alibaba.qwen.code.runtimebroker.JsonCodec; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; - -import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.canonical; -import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.closed; -import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.id; -import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.number; -import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.reference; -import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.sha256; -import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.text; -import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.utf8; -import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.uuid; - -import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceException; -import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceRelativePath; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import java.io.IOException; -import java.io.InputStream; + import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -31,26 +22,28 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.regex.Pattern; + +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.canonical; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.closed; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.id; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.number; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.reference; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.text; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.utf8; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof.uuid; +import static com.alibaba.qwen.code.runtimebroker.CsiNativeToolContent.qualifyContent; /** Original assistant allocation evidence; it does not authorize execution. */ final class CsiNativeToolReservation { private static final ObjectMapper JSON = new ObjectMapper(); private static final Set FIELDS = Set.of("sessionId", "promptId", "callId", "argsDigest", "batchId", "functionCallId", "partIndex", "ordinal", "inputRef", "toolDefinitionRef", "dispatchMode"); - private static final String JS_WHITESPACE = "[\\x09-\\x0d\\x20\\x{a0}\\x{1680}\\x{2000}-\\x{200a}" - + "\\x{2028}\\x{2029}\\x{202f}\\x{205f}\\x{3000}\\x{feff}]"; - private static final Pattern JS_TRIM = Pattern.compile("^" + JS_WHITESPACE + "+|" + JS_WHITESPACE + "+$"); - private static final JsonNode DEFINITIONS = definitions(); + private static final int LIMIT = 4096; private CsiNativeToolReservation() { } - static String trim(String value) { - return JS_TRIM.matcher(value).replaceAll(""); - } - static Map inventory(Connection connection, JdbcCsiFilesRetirementGuard.Original original) throws SQLException { return inventory(connection, original, JdbcCsiActivationAdmission.JournalRead.LIVE); } @@ -407,100 +400,6 @@ final class CsiNativeToolReservation { qualifyContent(original.request(), batch, ref, execution.getRequestDigest(), inputBytes, definitionBytes); } - static ObjectNode qualifyContent(RuntimeProvisionRequest original, CsiNativeActivationProof.OriginalBatch batch, - JsonNode ref, String requestDigest, byte[] inputBytes, byte[] definitionBytes) { - closed(ref, FIELDS); - String session = original.getIsolationKey(); - uuid(id(ref, "callId")); - uuid(id(ref, "batchId")); - require(session.equals(id(ref, "sessionId")) && batch.promptId().equals(id(ref, "promptId")) - && batch.batch().messageId().equals(id(ref, "batchId")) && "deferred".equals(text(ref, "dispatchMode"))); - long ordinal = number(ref.get("ordinal")); - require(ordinal < batch.batch().calls().size()); - var call = batch.batch().calls().get((int) ordinal); - require(call.id().equals(id(ref, "functionCallId")) && call.partIndex() == number(ref.get("partIndex"))); - require(inputBytes != null && inputBytes.length <= 64 * 1024 - && definitionBytes != null && definitionBytes.length <= 64 * 1024); - reference(ref.path("inputRef"), "managed-tool-input", ignored -> inputBytes); - reference(ref.path("toolDefinitionRef"), "managed-tool-definition", ignored -> definitionBytes); - require(!id(ref.path("inputRef"), "resourceId").equals(id(ref.path("toolDefinitionRef"), "resourceId"))); - var content = content(original, call, inputBytes, definitionBytes); - String digest = "sha256:" + content.digest(); - require(digest.equals(text(ref, "argsDigest")) && digest.equals(requestDigest)); - return content.normalized(); - } - - record Content(ObjectNode normalized, String digest) { - } - - static String refusalReason(RuntimeProvisionRequest original, CsiNativeActivationProof.FunctionCall call) { - ObjectNode input; - try { - input = normalizedInput(call.name(), call.args()); - } catch (RuntimeBrokerException | WorkspaceException | IllegalArgumentException invalid) { - return "CSI file input is invalid."; - } - ObjectNode payload = JSON.createObjectNode().put("toolName", call.name()); - payload.set("input", input); - ObjectNode wrapper = JSON.createObjectNode().put("harnessSessionId", original.getIsolationKey()) - .put("runtimeSessionId", original.getIsolationKey()).put("payloadJson", canonical(payload)); - return utf8(canonical(wrapper)).length > 64 * 1024 - ? "CSI file input exceeds the durable Session limit." : null; - } - - static Content content(RuntimeProvisionRequest original, CsiNativeActivationProof.FunctionCall call, - byte[] inputBytes, byte[] definitionBytes) { - require(inputBytes != null && inputBytes.length <= 64 * 1024 - && definitionBytes != null && definitionBytes.length <= 64 * 1024); - String session = original.getIsolationKey(); - JsonNode wrapper = CsiNativeActivationProof.readObject(inputBytes); - closed(wrapper, Set.of("harnessSessionId", "runtimeSessionId", "payloadJson")); - require(session.equals(id(wrapper, "harnessSessionId")) && session.equals(id(wrapper, "runtimeSessionId")) - && wrapper.path("payloadJson").isTextual()); - byte[] payloadBytes = utf8(wrapper.path("payloadJson").textValue()); - JsonNode payload = CsiNativeActivationProof.readObject(payloadBytes); - closed(payload, Set.of("toolName", "input")); - require(call.name().equals(text(payload, "toolName"))); - ObjectNode normalized = normalizedInput(call.name(), call.args()); - require(canonical(normalized).equals(canonical(payload.path("input")))); - JsonNode definition = CsiNativeActivationProof.readObject(definitionBytes); - JsonNode expected = null; - for (JsonNode item : DEFINITIONS) { - if (call.name().equals(item.path("name").textValue())) { - expected = item; - } - } - require(expected != null && canonical(expected).equals(canonical(definition))); - return new Content(normalized, sha256(payloadBytes)); - } - - private static ObjectNode normalizedInput(String name, JsonNode args) { - Set required = switch (name) { - case "read_file" -> Set.of("file_path"); - case "write_file" -> Set.of("file_path", "content"); - case "edit" -> Set.of("file_path", "old_string", "new_string"); - default -> throw invalid(); - }; - Set optional = "read_file".equals(name) ? Set.of("offset", "limit") - : "edit".equals(name) ? Set.of("replace_all") : Set.of(); - require(args.isObject()); - args.fieldNames().forEachRemaining(field -> require(required.contains(field) || optional.contains(field))); - for (String field : required) { - require(args.path(field).isTextual()); - utf8(args.path(field).textValue()); - } - for (String field : List.of("offset", "limit")) { - if (args.has(field)) { - require(number(args.get(field)) >= ("limit".equals(field) ? 1 : 0)); - } - } - require(!args.has("replace_all") || args.path("replace_all").isBoolean()); - ObjectNode result = args.deepCopy(); - result.put("file_path", WorkspaceRelativePath.normalize(trim(args.path("file_path").textValue()))); - require(!".".equals(result.path("file_path").textValue())); - return result; - } - private static byte[] bytes(Map resources, JsonNode ref, String kind) { Resource resource = resources.get(id(ref, "resourceId")); require(resource != null && "PUBLISHED".equals(resource.state) && "MYSQL_INLINE".equals(resource.storage) @@ -725,7 +624,7 @@ final class CsiNativeToolReservation { JdbcRepositorySupport.setStringParameter( statement, 1, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( scope(original), id(ref, "resourceId"))); JdbcRepositorySupport.setStringParameter(statement, 2, scope(original)); JdbcRepositorySupport.setStringParameter( @@ -748,18 +647,7 @@ final class CsiNativeToolReservation { } private static String scope(JdbcCsiFilesRetirementGuard.Original original) { - return com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(original.request().getScope().getTenantId(), original.request().getIsolationKey()); - } - - private static JsonNode definitions() { - try (InputStream input = CsiNativeToolReservation.class.getResourceAsStream("/csi-native-file-declarations.json")) { - require(input != null); - JsonNode definitions = CsiNativeActivationProof.readJson(input.readNBytes(64 * 1024 + 1)); - require(definitions.isArray() && definitions.size() == 3); - return definitions; - } catch (IOException error) { - throw new IllegalStateException("Private CSI declarations are unavailable", error); - } + return com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(original.request().getScope().getTenantId(), original.request().getIsolationKey()); } private static PreparedStatement statement(Connection connection, String sql) throws SQLException { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/InMemoryRuntimeBindingRepository.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/InMemoryRuntimeBindingRepository.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/InMemoryRuntimeBindingRepository.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/InMemoryRuntimeBindingRepository.java index 885efdd9c52..d83f1a16f60 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/InMemoryRuntimeBindingRepository.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/InMemoryRuntimeBindingRepository.java @@ -1,4 +1,19 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; + +import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.HarnessOwnership; +import com.alibaba.qwen.code.runtimebroker.RuntimeAdmission; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; import java.time.Clock; import java.time.Duration; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/InMemoryRuntimeSessionRepository.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/InMemoryRuntimeSessionRepository.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/InMemoryRuntimeSessionRepository.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/InMemoryRuntimeSessionRepository.java index 64b92e33789..9de7cf1723d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/InMemoryRuntimeSessionRepository.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/InMemoryRuntimeSessionRepository.java @@ -1,8 +1,15 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; + +import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; -import java.util.HashMap; import java.time.Instant; import java.util.Comparator; +import java.util.HashMap; import java.util.Map; /** Process-local logical Session repository for tests and single-node use. */ diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/InMemoryToolExecutionRepository.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/InMemoryToolExecutionRepository.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/InMemoryToolExecutionRepository.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/InMemoryToolExecutionRepository.java index 0e441fc4735..f4666c6215f 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/InMemoryToolExecutionRepository.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/InMemoryToolExecutionRepository.java @@ -1,5 +1,11 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.ToolHoldRow; import com.alibaba.qwen.code.runtimebroker.transport.ManagedHoldFamily; import java.time.Clock; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiActivationAdmission.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiActivationAdmission.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiActivationAdmission.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiActivationAdmission.java index 7116d3803c8..8d723960e50 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiActivationAdmission.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiActivationAdmission.java @@ -1,13 +1,21 @@ -package com.alibaba.qwen.code.runtimebroker; - -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; +import com.alibaba.qwen.code.runtimebroker.ManagedCheckpointHistory; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; - +import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiFilesProtocol; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; + import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -33,7 +41,7 @@ public final class JdbcCsiActivationAdmission { static ReservationScope lockForReservation(Connection connection, RuntimeBindingRecord hint, JdbcRuntimeBindingRepository bindings) throws SQLException { - require(hint != null && JdbcCsiFilesRetirementGuard.isProfile(hint.getRequest().getScope())); + require(hint != null && CsiFilesRetirementProfile.isProfile(hint.getRequest().getScope())); var original = lockOriginal(connection, hint.getBindingId()); require(original != null && original.request().equals(hint.getRequest()) && original.generation() == hint.getGeneration()); original.requireAdmission(); @@ -49,7 +57,7 @@ public final class JdbcCsiActivationAdmission { throw new IllegalArgumentException("Execution binding is unavailable"); } var original = lockForExecution(connection, hint.getBindingId(), bindings); - require(!JdbcCsiFilesRetirementGuard.isProfile(hint.getRequest().getScope()) || original != null); + require(!CsiFilesRetirementProfile.isProfile(hint.getRequest().getScope()) || original != null); if (original != null) { require(original.request().equals(hint.getRequest()) && original.generation() == hint.getGeneration()); } @@ -282,8 +290,8 @@ public final class JdbcCsiActivationAdmission { try (var statement = statement(connection, "SELECT resource_id FROM ma_resource WHERE resource_key = ? OR (resource_id = ?" + " AND ((tenant_id = ? AND session_id = ?) OR session_scope_key = ?)) FOR UPDATE")) { - String scope = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(original.request().getScope().getTenantId(), original.request().getIsolationKey()); - statement.setString(1, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(scope, resourceId)); + String scope = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(original.request().getScope().getTenantId(), original.request().getIsolationKey()); + statement.setString(1, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scope, resourceId)); JdbcRepositorySupport.setIdentity(statement, 2, resourceId); JdbcRepositorySupport.setIdentity(statement, 3, original.request().getScope().getTenantId()); JdbcRepositorySupport.setIdentity(statement, 4, original.request().getIsolationKey()); @@ -484,11 +492,11 @@ public final class JdbcCsiActivationAdmission { JdbcRepositorySupport.setIdentity(statement, 2, original.request().getIsolationKey()); statement.setString( 3, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( original.request().getScope().getTenantId())); statement.setString( 4, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( original.request().getIsolationKey())); JdbcRepositorySupport.setIdentity(statement, 5, original.request().getIsolationKey()); try (ResultSet head = statement.executeQuery()) { @@ -822,9 +830,9 @@ public final class JdbcCsiActivationAdmission { + " AND x.owner_key = ? AND x.owner_revision = ? AND x.journal_revision = ?" + " AND x.tenant_id = ? AND x.workspace_id = ? AND x.session_id = ? AND x.resource_id = ?" + " AND r.deletion_operation_key IS NULL")) { - String scope = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(original.request().getScope().getTenantId(), original.request().getIsolationKey()); + String scope = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(original.request().getScope().getTenantId(), original.request().getIsolationKey()); String resourceId = ref.path("resourceId").textValue(); - statement.setString(1, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(scope, resourceId)); + statement.setString(1, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scope, resourceId)); statement.setString(2, scope); JdbcRepositorySupport.setIdentity(statement, 3, original.request().getScope().getTenantId()); JdbcRepositorySupport.setIdentity(statement, 4, original.request().getScope().getWorkspaceId()); @@ -880,7 +888,7 @@ public final class JdbcCsiActivationAdmission { if (maximum < 1) { return 0; } - String scope = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + String scope = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( original.request().getScope().getTenantId(), original.request().getIsolationKey()); String resourceId = ref.path("resourceId").textValue(); try (PreparedStatement statement = read.statement(connection, @@ -891,7 +899,7 @@ public final class JdbcCsiActivationAdmission { + " AND x.workspace_id = ? AND x.session_id = ? AND x.resource_id = ?" + " AND x.owner_kind = 'JOURNAL' AND x.owner_key = ?" + " AND x.owner_revision = x.journal_revision AND x.owner_revision <= ? ORDER BY x.owner_revision LIMIT 1")) { - statement.setString(1, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(scope, resourceId)); + statement.setString(1, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scope, resourceId)); statement.setString(2, scope); JdbcRepositorySupport.setIdentity(statement, 3, original.request().getScope().getTenantId()); JdbcRepositorySupport.setIdentity(statement, 4, original.request().getScope().getWorkspaceId()); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiApplicationProgress.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiApplicationProgress.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiApplicationProgress.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiApplicationProgress.java index 6033cd336b9..1775d73d327 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiApplicationProgress.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiApplicationProgress.java @@ -1,4 +1,10 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; + +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; +import com.alibaba.qwen.code.runtimebroker.CsiOwnershipRevision; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -50,14 +56,14 @@ public final class JdbcCsiApplicationProgress { if (connection == null || connection.getAutoCommit() || original == null) { throw unavailable(); } - require(JdbcCsiFilesRetirementGuard.isProfile(original.request().getScope()) && original.generation() == 1); + require(CsiFilesRetirementProfile.isProfile(original.request().getScope()) && original.generation() == 1); original.requireContinuation(); lockReservation(connection, original); Long ceiling = JdbcCsiRetirementSeal.lock(connection, original); try (PreparedStatement statement = statement(connection, "SELECT * FROM ma_operation WHERE operation_kind = 'CSI_RETIREMENT'" + " AND target_key = ? AND target_revision = ? AND tenant_id = ? FOR UPDATE")) { - statement.setString(1, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(original.bindingId())); + statement.setString(1, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(original.bindingId())); statement.setLong(2, original.generation()); JdbcRepositorySupport.setIdentity(statement, 3, original.request().getScope().getTenantId()); try (var row = statement.executeQuery()) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiApplicationRetirement.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiApplicationRetirement.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiApplicationRetirement.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiApplicationRetirement.java index 9f19a2e8635..79bf377dafe 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiApplicationRetirement.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiApplicationRetirement.java @@ -1,4 +1,8 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; + +import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import java.sql.Connection; import java.sql.PreparedStatement; @@ -75,7 +79,7 @@ public final class JdbcCsiApplicationRetirement { require(ceiling != null); try (PreparedStatement statement = statement(connection, "SELECT * FROM ma_operation WHERE operation_key = ? AND operation_kind = 'CSI_RETIREMENT' FOR UPDATE")) { - statement.setString(1, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("CSI_RETIREMENT", tenantId, retirementId)); + statement.setString(1, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("CSI_RETIREMENT", tenantId, retirementId)); try (var row = statement.executeQuery()) { require(row.next() && retirementId.equals(com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows.identity(row, "operation_id")) && "qwen-csi-application-retirement/1".equals(row.getString("progress_format"))); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiExecutionAdmission.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiExecutionAdmission.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiExecutionAdmission.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiExecutionAdmission.java index 5dab6bd4a98..acfb4c1acd7 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiExecutionAdmission.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiExecutionAdmission.java @@ -1,7 +1,14 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import com.alibaba.qwen.code.runtimebroker.transport.CsiNativeReadbackProtocol; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -296,7 +303,7 @@ public final class JdbcCsiExecutionAdmission { ToolExecutionRecord execution, CsiNativeActivationProof.Prefix prefix, long maximumRevision, long ceiling, String saved, DispatchCheckpoint checkpoint, Map pinnedContext) { require(original != null && original.request() != null && maximumRevision > 0 - && JdbcCsiFilesRetirementGuard.isProfile(original.request().getScope()) + && CsiFilesRetirementProfile.isProfile(original.request().getScope()) && ceiling > 0 && ceiling <= original.version()); var value = grantHeader(original, execution, prefix, ceiling, saved); if (value.isNull()) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiFilesRetirementGuard.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiFilesRetirementGuard.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiFilesRetirementGuard.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiFilesRetirementGuard.java index 440d5c6d74b..5ddd118eb4d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiFilesRetirementGuard.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiFilesRetirementGuard.java @@ -1,8 +1,14 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; - import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; + import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -181,7 +187,7 @@ public final class JdbcCsiFilesRetirementGuard { JdbcRepositorySupport.setStringParameter( statement, 1, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( tenantId)); try (ResultSet row = statement.executeQuery()) { if (!row.next()) { @@ -205,7 +211,7 @@ public final class JdbcCsiFilesRetirementGuard { JdbcRepositorySupport.setStringParameter( statement, 1, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( tenantId)); JdbcRepositorySupport.setIdentity(statement, 2, tenantId); statement.executeUpdate(); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiReadyAdmission.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiReadyAdmission.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiReadyAdmission.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiReadyAdmission.java index 8dc58cce429..4ec4f55710b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiReadyAdmission.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiReadyAdmission.java @@ -1,4 +1,7 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; + +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -34,7 +37,7 @@ public final class JdbcCsiReadyAdmission { public static Admission lock(Connection connection, JdbcCsiFilesRetirementGuard.Original original) throws SQLException { require(connection != null && !connection.getAutoCommit() && original != null); - require(JdbcCsiFilesRetirementGuard.isProfile(original.request().getScope()) && original.generation() == 1); + require(CsiFilesRetirementProfile.isProfile(original.request().getScope()) && original.generation() == 1); original.requireAdmission(); lockReservation(connection, original); JdbcCsiRetirementSeal.requireOpen(connection, original); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiRetirementSeal.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiRetirementSeal.java similarity index 93% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiRetirementSeal.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiRetirementSeal.java index e08f6454064..c6808ef4cdd 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiRetirementSeal.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiRetirementSeal.java @@ -1,5 +1,9 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; +import com.alibaba.qwen.code.runtimebroker.CsiOwnershipRevision; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiFilesProtocol; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.StreamReadConstraints; @@ -7,6 +11,7 @@ import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; + import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; @@ -41,7 +46,7 @@ final class JdbcCsiRetirementSeal { JdbcRepositorySupport.setStringParameter( statement, 1, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( original.bindingId())); statement.setLong(2, original.generation()); JdbcRepositorySupport.setIdentity( @@ -59,7 +64,7 @@ final class JdbcCsiRetirementSeal { try (PreparedStatement statement = statement(connection, "SELECT * FROM ma_operation WHERE operation_kind = 'CSI_RETIREMENT'" + " AND target_key = ? AND target_revision = ? AND tenant_id = ? FOR UPDATE")) { - statement.setString(1, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(original.bindingId())); + statement.setString(1, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(original.bindingId())); statement.setLong(2, original.generation()); JdbcRepositorySupport.setIdentity(statement, 3, original.request().getScope().getTenantId()); try (var row = statement.executeQuery()) { @@ -89,7 +94,7 @@ final class JdbcCsiRetirementSeal { try (var statement = read.statement(connection, "SELECT * FROM ma_operation WHERE operation_kind = 'CSI_RETIREMENT'" + " AND target_key = ? AND target_revision = ? AND tenant_id = ?")) { - statement.setString(1, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(original.bindingId())); + statement.setString(1, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(original.bindingId())); statement.setLong(2, original.generation()); JdbcRepositorySupport.setIdentity(statement, 3, original.request().getScope().getTenantId()); try (var row = statement.executeQuery()) { @@ -144,14 +149,14 @@ final class JdbcCsiRetirementSeal { && "RUNTIME_BINDING".equals(row.getString("target_kind")) && "DRAINING".equals(row.getString("phase")) && "DRAINING".equals(row.getString("state")) && row.getInt("payload_version") == 1 - && java.util.Arrays.equals(com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.identity(tenant), row.getBytes("tenant_id")) - && java.util.Arrays.equals(com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.identity(original.bindingId()), row.getBytes("target_id")) - && com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(original.bindingId()).equals(row.getString("target_key")) + && java.util.Arrays.equals(com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.identity(tenant), row.getBytes("tenant_id")) + && java.util.Arrays.equals(com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.identity(original.bindingId()), row.getBytes("target_id")) + && com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(original.bindingId()).equals(row.getString("target_key")) && row.getLong("target_revision") == original.generation() && !row.wasNull() - && com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(tenant).equals(row.getString("scope_key")) - && com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("CSI_RETIREMENT", tenant, operation).equals(row.getString("operation_key")) + && com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(tenant).equals(row.getString("scope_key")) + && com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("CSI_RETIREMENT", tenant, operation).equals(row.getString("operation_key")) && JdbcRuntimeBindingRepository.storageAliasKey(tenant, original.request().getStorageId()).equals(row.getString("storage_key")) - && com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(encoded).equals(row.getString("request_digest"))); + && com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(encoded).equals(row.getString("request_digest"))); return encoded; } diff --git a/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcHarnessResources.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcHarnessResources.java new file mode 100644 index 00000000000..69483fdad6a --- /dev/null +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcHarnessResources.java @@ -0,0 +1,18 @@ +package com.alibaba.qwen.code.runtimebroker.persistence; + +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; + +import java.util.List; + +public interface JdbcHarnessResources { + default List verifyHarnessResources(java.sql.Connection connection, + String tenantId, String harnessSessionId) throws java.sql.SQLException { + throw new RuntimeBrokerException(409, "workspace_close_identity_unverified", "Resource verification is unavailable", false); + } + + default void verifyHarnessStopped(java.sql.Connection connection, String tenantId, String harnessSessionId) + throws java.sql.SQLException { + throw new RuntimeBrokerException(409, "workspace_close_identity_unverified", "Stop verification is unavailable", false); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositorySupport.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRepositorySupport.java similarity index 72% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositorySupport.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRepositorySupport.java index 8dca5f2660d..8c9120f372c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositorySupport.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRepositorySupport.java @@ -1,19 +1,21 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; + +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeRequestKeys; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.StoreFailureException; +import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; + +import javax.sql.DataSource; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; -import javax.sql.DataSource; final class JdbcRepositorySupport { private JdbcRepositorySupport() { @@ -27,28 +29,15 @@ final class JdbcRepositorySupport { } static String requestKey(RuntimeProvisionRequest request) { - RuntimeScope scope = request.getScope(); - if (request.isManagedContext()) { - return digest("managed-context/1", scope.getTenantId(), - scope.getWorkspaceId(), scope.getWorkspaceGeneration(), - scope.getCanonicalCwd(), scope.getCapabilityDigest(), - scope.getIsolationClass(), request.getIsolationKey(), - request.getProvisionerKind(), request.getStorageId()); - } - return digest(scope.getTenantId(), scope.getWorkspaceId(), - scope.getWorkspaceGeneration(), scope.getCanonicalCwd(), - scope.getCapabilityDigest(), scope.getIsolationClass(), - request.getIsolationKey(), request.getProvisionerKind()); + return RuntimeRequestKeys.requestKey(request); } static String scopeKey(RuntimeScope scope) { - return digest(scope.getTenantId(), scope.getWorkspaceId(), - scope.getWorkspaceGeneration(), scope.getCanonicalCwd(), - scope.getCapabilityDigest(), scope.getIsolationClass()); + return RuntimeRequestKeys.scopeKey(scope); } static String valueKey(String value) { - return digest(value); + return RuntimeRequestKeys.valueKey(value); } static Duration requireDuration(Duration duration) { @@ -174,7 +163,7 @@ final class JdbcRepositorySupport { static void setIdentity(java.sql.PreparedStatement statement, int index, String value) throws SQLException { statement.setBytes(index, value == null ? null - : com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.identity(value)); + : com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.identity(value)); } static void setStringParameter(java.sql.PreparedStatement statement, int index, String value) @@ -196,38 +185,6 @@ final class JdbcRepositorySupport { "Runtime Broker database operation failed", exception); } - /** - * A genuine store outage, distinct from the permanent invariant - * violations the repositories raise as plain IllegalStateExceptions: the - * service maps only this type to a retryable 503. - */ - static final class StoreFailureException extends IllegalStateException { - StoreFailureException(String message, Throwable cause) { - super(message, cause); - } - } - - private static String digest(String... values) { - try { - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - for (String value : values) { - if (value == null) { - digest.update(ByteBuffer.allocate(Integer.BYTES) - .putInt(-1).array()); - } else { - byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - digest.update(ByteBuffer.allocate(Integer.BYTES) - .putInt(bytes.length).array()); - digest.update(bytes); - } - } - return java.util.HexFormat.of().formatHex(digest.digest()); - } catch (NoSuchAlgorithmException exception) { - throw new IllegalStateException("SHA-256 is unavailable", - exception); - } - } - private static void rollback(Connection connection, Exception original) { try { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeBindingRepository.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeBindingRepository.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeBindingRepository.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeBindingRepository.java index b9eb7db8c3f..2e90bb66746 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeBindingRepository.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeBindingRepository.java @@ -1,7 +1,29 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.HarnessOwnership; +import com.alibaba.qwen.code.runtimebroker.NativeCsiExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.ResidentWorkspaceProfile; +import com.alibaba.qwen.code.runtimebroker.RuntimeAdmission; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeDrainReceipt; +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; +import com.alibaba.qwen.code.runtimebroker.RuntimeRecoveryEvidence; +import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; +import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.security.ProtectedSecret; import com.alibaba.qwen.code.runtimebroker.security.SecretProtector; @@ -27,7 +49,7 @@ import java.util.function.Supplier; /** JDBC Runtime binding repository coordinated through a shared database. */ public final class JdbcRuntimeBindingRepository - implements RuntimeBindingRepository { + implements RuntimeBindingRepository, NativeCsiExecutionRepository, JdbcHarnessResources { private static final String BINDING_COLUMNS = String.join( ", ", @@ -104,7 +126,7 @@ public final class JdbcRuntimeBindingRepository connection -> { if (JdbcCsiFilesRetirementGuard.lockRuntimeSession(connection, expected) != null) { - throw JdbcCsiFilesRetirementGuard.releaseUnavailable(); + throw CsiFilesRetirementProfile.releaseUnavailable(); } RuntimeBindingRecord binding = selectById(connection, expected.getBindingId(), true); @@ -279,7 +301,7 @@ public final class JdbcRuntimeBindingRepository connection -> { if (JdbcCsiFilesRetirementGuard.lockRuntimeSession(connection, expected) != null) { - throw JdbcCsiFilesRetirementGuard.releaseUnavailable(); + throw CsiFilesRetirementProfile.releaseUnavailable(); } // Statement order is load-bearing under InnoDB REPEATABLE READ: // this locking read must stay first, and the plain execution read @@ -372,8 +394,8 @@ public final class JdbcRuntimeBindingRepository || !current.getBindingId().equals(slot.activeBindingId)) { return null; } - if (JdbcCsiFilesRetirementGuard.isProfile(current.getRequest().getScope())) { - throw JdbcCsiFilesRetirementGuard.releaseUnavailable(); + if (CsiFilesRetirementProfile.isProfile(current.getRequest().getScope())) { + throw CsiFilesRetirementProfile.releaseUnavailable(); } if (current.getLossEvidence() == null) { return current; @@ -570,7 +592,7 @@ public final class JdbcRuntimeBindingRepository if (original == null) { throw new IllegalArgumentException("Binding is unavailable"); } - boolean csi = JdbcCsiFilesRetirementGuard.isProfile(original.getRequest().getScope()); + boolean csi = CsiFilesRetirementProfile.isProfile(original.getRequest().getScope()); if (existing != null && !csi) { return existing; } @@ -636,7 +658,8 @@ public final class JdbcRuntimeBindingRepository } } - ToolExecutionRecord prepareCsiExecution( + @Override + public ToolExecutionRecord prepareCsiExecution( RuntimeSessionRepository sessions, ToolExecutionRepository executions, RuntimeBindingRecord hint, @@ -677,13 +700,15 @@ public final class JdbcRuntimeBindingRepository }); } - Map readCsiBatch( + @Override + public Map readCsiBatch( RuntimeSessionRepository sessions, ToolExecutionRepository executions, RuntimeBindingRecord hint, String promptId, String batchId) { return readCsiBatch(sessions, executions, hint, promptId, batchId, null); } - Map readCsiBatch( + @Override + public Map readCsiBatch( RuntimeSessionRepository sessions, ToolExecutionRepository executions, RuntimeBindingRecord hint, @@ -762,7 +787,7 @@ public final class JdbcRuntimeBindingRepository } public static String harnessDrainKey(String value) { - return com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(value); + return com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(value); } @Override @@ -1940,7 +1965,7 @@ public final class JdbcRuntimeBindingRepository statement.setQueryTimeout(10); statement.setString( 1, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( original.bindingId())); statement.setLong(2, original.generation()); JdbcRepositorySupport.setIdentity( @@ -1961,7 +1986,7 @@ public final class JdbcRuntimeBindingRepository BindingControl control, RuntimeBindingRecord current) { if (control == null) { if (current != null - && JdbcCsiFilesRetirementGuard.isProfile(current.getRequest().getScope())) { + && CsiFilesRetirementProfile.isProfile(current.getRequest().getScope())) { throw controlUnavailable(); } return; @@ -2266,7 +2291,7 @@ public final class JdbcRuntimeBindingRepository + "OR CAST(workspace_id AS BINARY(2048)) = CAST(? AS BINARY(2048))) LIMIT 1")) { JdbcRepositorySupport.setIdentity(statement, 1, scope.getTenantId()); JdbcRepositorySupport.setIdentity(statement, 2, scope.getTenantId()); - JdbcRepositorySupport.setIdentity(statement, 3, LocalProcessRuntimeProvisioner.KIND); + JdbcRepositorySupport.setIdentity(statement, 3, com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS); JdbcRepositorySupport.setIdentity(statement, 4, request.getStorageId()); JdbcRepositorySupport.setIdentity(statement, 5, scope.getCanonicalCwd()); statement.setBoolean(6, ResidentWorkspaceProfile.selects(request)); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeRetention.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeRetention.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeRetention.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeRetention.java index d86cb8eecfc..4983f667b39 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeRetention.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeRetention.java @@ -1,4 +1,8 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; + +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; + +import javax.sql.DataSource; import java.sql.Connection; import java.sql.PreparedStatement; @@ -8,24 +12,15 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.List; -import javax.sql.DataSource; /** Bounded cleanup of unreferenced retired Runtime generations. */ -public final class JdbcRuntimeRetention { +public final class JdbcRuntimeRetention implements com.alibaba.qwen.code.runtimebroker.RuntimeRetention { public interface ReferenceGuard { boolean bindingReferenced(Connection connection, RuntimeBindingRecord binding) throws SQLException; boolean executionReferenced(Connection connection, String executionCallId) throws SQLException; } - public enum Phase { BINDINGS, EXECUTIONS, SESSIONS } - - public record Cursor(String bindingState, Instant lastActiveAt, String bindingId, - Phase phase, String childState, String childKey) { } - - public record BatchResult(Cursor cursor, int bindingsScanned, int childrenScanned, int skipped, - int executionsDeleted, int sessionsDeleted, int bindingsDeleted) { } - private final DataSource dataSource; private final JdbcRuntimeBindingRepository bindings; private final ReferenceGuard references; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeSessionRepository.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeSessionRepository.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeSessionRepository.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeSessionRepository.java index c6cd0760cc9..d83e788d8bd 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeSessionRepository.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeSessionRepository.java @@ -1,12 +1,23 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSession; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.transport.CsiFileFinalizeProtocol; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiFilesProtocol; + +import javax.sql.DataSource; + import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import javax.sql.DataSource; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -89,10 +100,10 @@ public final class JdbcRuntimeSessionRepository + " AND target_key = ? AND target_id = ? AND target_revision = ?" + " AND progress_stage = 'CUT' AND progress_version = 3 FOR UPDATE")) { statement.setQueryTimeout(10); - statement.setString(1, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("CSI_RETIREMENT", original.request().getScope().getTenantId(), authority.retirementId())); + statement.setString(1, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("CSI_RETIREMENT", original.request().getScope().getTenantId(), authority.retirementId())); JdbcRepositorySupport.setIdentity(statement, 2, original.request().getScope().getTenantId()); JdbcRepositorySupport.setIdentity(statement, 3, authority.retirementId()); - statement.setString(4, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(original.bindingId())); + statement.setString(4, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(original.bindingId())); JdbcRepositorySupport.setIdentity(statement, 5, original.bindingId()); statement.setLong(6, original.generation()); try (var row = statement.executeQuery()) { @@ -183,7 +194,7 @@ public final class JdbcRuntimeSessionRepository RuntimeSessionRecord candidate) { requireCandidate(candidate); RuntimeScope scope = candidate.getSession().getScope(); - if (JdbcCsiFilesRetirementGuard.isProfile(scope)) { + if (CsiFilesRetirementProfile.isProfile(scope)) { return JdbcRepositorySupport.transaction(dataSource, connection -> { var original = JdbcCsiFilesRetirementGuard.lockRuntimeSession(connection, candidate); original.requireAdmission(); @@ -342,7 +353,7 @@ public final class JdbcRuntimeSessionRepository JdbcCsiFilesRetirementGuard.requireSingleSession(connection, original); if (replacement.getState() == RuntimeSessionRecord.State.RELEASING || replacement.getState() == RuntimeSessionRecord.State.RELEASED) { - throw JdbcCsiFilesRetirementGuard.releaseUnavailable(); + throw CsiFilesRetirementProfile.releaseUnavailable(); } if (expected.getState() == RuntimeSessionRecord.State.ACQUIRING && replacement.getState() == RuntimeSessionRecord.State.READY) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcToolExecutionRepository.java b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcToolExecutionRepository.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcToolExecutionRepository.java rename to packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcToolExecutionRepository.java index ba144bbf8a4..b89c7f7c7cb 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/JdbcToolExecutionRepository.java +++ b/packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcToolExecutionRepository.java @@ -1,8 +1,16 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONReader; import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.RuntimeAdmission; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.ToolHoldRow; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.transport.ManagedHoldFamily; diff --git a/packages/managed-agent-server/src/main/resources/db/managed-schema/V1__managed_schema.sql b/packages/managed-agent-server/persistence/src/main/resources/db/managed-schema/V1__managed_schema.sql similarity index 100% rename from packages/managed-agent-server/src/main/resources/db/managed-schema/V1__managed_schema.sql rename to packages/managed-agent-server/persistence/src/main/resources/db/managed-schema/V1__managed_schema.sql diff --git a/packages/managed-agent-server/pom.xml b/packages/managed-agent-server/pom.xml index c5f69481812..6fd6e36dd2d 100644 --- a/packages/managed-agent-server/pom.xml +++ b/packages/managed-agent-server/pom.xml @@ -1,19 +1,15 @@ - + 4.0.0 - org.springframework.boot spring-boot-starter-parent 3.5.16 - com.alibaba - qwen-managed-agent-server + qwen-managed-agent-parent 0.1.0-alpha - Qwen Managed Agent Server + Qwen Managed Agent Parent Standalone Spring control plane for Qwen Managed Agents. https://github.com/QwenLM/qwen-code/tree/main/packages/managed-agent-server @@ -34,7 +30,6 @@ https://github.com/QwenLM/qwen-code/graphs/contributors - 21 true @@ -50,354 +45,148 @@ 3.5.6 1.5.9 4.10.4.1 - 5.1.0 + ${project.basedir} + 1.5.1 - - - - org.springframework - spring-jdbc - - - org.apache.commons - commons-lang3 - - - commons-codec - commons-codec - - - com.aliyun.oss - aliyun-sdk-oss - 3.18.4 - - - org.apache.rocketmq - rocketmq-client-java - ${rocketmq-client-java.version} - - - com.alibaba - qwencode-sdk - ${qwencode-sdk.version} - - - com.alibaba.fastjson2 - fastjson2 - ${fastjson2.version} - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-actuator - true - - - org.springframework.boot - spring-boot-starter-jdbc - true - - - com.zaxxer - HikariCP - - - - - com.alibaba - druid - ${druid.version} - - - org.springframework.boot - spring-boot-starter-validation - - - org.flywaydb - flyway-core - true - - - org.flywaydb - flyway-mysql - true - - - com.mysql - mysql-connector-j - runtime - - - com.h2database - h2 - runtime - true - - - org.springframework.boot - spring-boot-starter-test - test - - - - org.junit.platform - junit-platform-launcher - test - - - com.networknt - json-schema-validator - ${json-schema-validator.version} - - - + pom + + contracts + control-plane + runtime-broker + persistence + adapters + server + verification + + + + + com.aliyun.oss + aliyun-sdk-oss + 3.18.4 + + + org.apache.rocketmq + rocketmq-client-java + ${rocketmq-client-java.version} + + + com.alibaba + qwencode-sdk + ${qwencode-sdk.version} + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson2.version} + + + com.alibaba + druid + ${druid.version} + + + com.networknt + json-schema-validator + ${json-schema-validator.version} + + + com.alibaba + qwen-managed-agent-contracts + ${project.version} + + + com.alibaba + qwen-managed-agent-control-plane + ${project.version} + + + com.alibaba + qwen-managed-agent-runtime-broker + ${project.version} + + + com.alibaba + qwen-managed-agent-persistence + ${project.version} + + + com.alibaba + qwen-managed-agent-adapters + ${project.version} + + + com.alibaba + qwen-managed-agent-server + ${project.version} + + + com.tngtech.archunit + archunit + ${archunit.version} + + + - - - src/main/resources - - - ../core/src/managed-runtime/contracts - - managed-tool-result-v1.schema.json - - contracts - - - - - org.apache.maven.plugins - maven-jar-plugin - - - default-jar - application*.yml - - - workspace-bundle-resources - ${managed.operator-tools.jar.phase} - jar - workspace-bundle - - - workspace-migration-resources - ${managed.operator-tools.jar.phase} - jar - workspace-migration - - - workspace-maintenance-recovery-resources - ${managed.operator-tools.jar.phase} - jar - workspace-maintenance-recovery - - - csi-session-resources - ${managed.operator-tools.jar.phase} - jar - csi-session - - - csi-runtime-resources - ${managed.operator-tools.jar.phase} - jar - csi-runtime - - - operator-recovery-resources - ${managed.operator-tools.jar.phase} - jar - operator-recovery - - - standalone-resources - jar - exec - - - - - org.springframework.boot - spring-boot-maven-plugin - - - repackage - - exec - com.alibaba.qwen.code.managedagent.ManagedAgentServerApplication - - - - workspace-bundle - repackage - - workspace-bundle - ${managed.operator-tools.attach} - ${managed.operator-tools.skip} - com.alibaba.qwen.code.managedagent.persistence.WorkspaceRecoveryMain - - - - workspace-migration - repackage - - workspace-migration - ${managed.operator-tools.attach} - ${managed.operator-tools.skip} - com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceMigrationMain - - - - workspace-maintenance-recovery - repackage - - workspace-maintenance-recovery - ${managed.operator-tools.attach} - ${managed.operator-tools.skip} - com.alibaba.qwen.code.managedagent.persistence.WorkspaceMaintenanceRecoveryMain - - - - csi-session - repackage - - csi-session - ${managed.operator-tools.attach} - ${managed.operator-tools.skip} - com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiSessionMain - - - - csi-runtime - repackage - - csi-runtime - ${managed.operator-tools.attach} - ${managed.operator-tools.skip} - com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRuntimeMain - - - - operator-recovery - repackage - - operator-recovery - ${managed.operator-tools.attach} - ${managed.operator-tools.skip} - com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceRecoveryCommand - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - ${checkstyle-maven-plugin.version} - - ${project.basedir}/../sdk-java/qwencode/qwen-code-checks.xml - true - com/alibaba/qwen/code/runtimebroker/** - - - - - check - - - - runtime-broker-checkstyle - - check - - - ${project.basedir}/../sdk-java/qwencode/checkstyle.xml - com/alibaba/qwen/code/runtimebroker/**/*.java - false - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - true - fault-gate - - - present - present - present - present - present - present - - - - - artifact-packaging - integration-test - test - - **/ManagedAgentPackagingIT.java - - ${project.version} - ${managed.operator-tools.attach} - - - - - - - org.jacoco - jacoco-maven-plugin - ${jacoco-maven-plugin.version} - - - prepare-agent - - - report - test - report - - - - - - com.github.spotbugs - spotbugs-maven-plugin - ${spotbugs-maven-plugin.version} - - High - ${project.basedir}/spotbugs-excludes.xml - - - - - check - - - - - + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${checkstyle-maven-plugin.version} + + ${managed.module.root}/../sdk-java/qwencode/qwen-code-checks.xml + true + com/alibaba/qwen/code/runtimebroker/** + + + + + check + + + + runtime-broker-checkstyle + + check + + + ${managed.module.root}/../sdk-java/qwencode/checkstyle.xml + com/alibaba/qwen/code/runtimebroker/**/*.java + false + + + + + + com.github.spotbugs + spotbugs-maven-plugin + ${spotbugs-maven-plugin.version} + + High + ${managed.module.root}/spotbugs-excludes.xml + + + + + check + + + + + + - no-operator-tools - managed.operator-tools.attachfalse + + + managed.operator-tools.attach + false + + none true @@ -430,7 +219,9 @@ attach-sources - jar-no-fork + + jar-no-fork + @@ -441,7 +232,9 @@ attach-javadocs - jar + + jar + @@ -459,74 +252,8 @@ sign-artifacts verify - sign - - - - - - - - fault-gates - - ${project.basedir}/../../dist/cli.js - - - - org.apache.maven.plugins - maven-surefire-plugin - - true - 600 - fault-gate - - ${qwen.cli.entry} - - - - - - - mysql-integration - - 1 - - - - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - - **/Hosted*IT.java - - **/ManagedAgentPackagingIT.java - - ${mysql.integration.forks} - true - - ${mysql.integration.forks} - - 0${surefire.forkNumber} - - - - junit.jupiter.execution.parallel.enabled = false - - - - - - integration-test - verify + sign @@ -534,572 +261,5 @@ - - hosted-harness-mysql - - - - - org.apache.maven.plugins - maven-surefire-plugin - true - - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - - **/Hosted*IT.java - - true - 1 - - - junit.jupiter.extensions.autodetection.enabled = true - junit.jupiter.execution.parallel.enabled = false - qwen.hosted.mysql.isolation = true - - - - 11444 - - - - - integration-test - verify - - - - - - - - - hosted-harness-gates - - org.apache.maven.plugins - maven-surefire-plugin - true - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - **/Hosted*Gate.java - true - - true - 2400 - - integration-testverify - - - - hosted-process-crashes - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - **/HostedProcessCrashIT.java - true - 720 - - integration-testverify - - - - hosted-workspace-tools - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - **/HostedWorkspaceToolTurnIT.java - true - 300 - - integration-testverify - - - - hosted-channel-gates - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - **/HostedChannel*IT.java - true - 1200 - - integration-testverify - - - - hosted-monitor-run-gates - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - **/HostedMonitorRun*IT.java - true - - 3600 - - integration-testverify - - - - hosted-session-relay-gates - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - **/HostedSessionRelay*IT.java - true - - 2100 - - integration-testverify - - - - hosted-workflow-gates - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - **/HostedWorkflow*IT.java - true - - 1800 - - integration-testverify - - - - hosted-mcp-gates - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - **/HostedMcp*IT.java - true - - 1200 - - integration-testverify - - - - hosted-automation-gates - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - **/HostedAutomation*IT.java - true - - 1200 - - integration-testverify - - - - o4-mysql-gates - - org.apache.maven.plugins - maven-surefire-plugin - true - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - **/O4MySqlGate.java - true - true - -Xmx256m - true - 600 - - integration-testverify - - - - o4-oss-gates - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - **/O4OssGate.java - true - true - -Xmx256m - true - 5400 - - integration-testverify - - - - - event-transport-broker-gates - - - org.apache.maven.plugins - maven-surefire-plugin - true - - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - **/EventTransportBrokerGate.java - true - true - 900 - - integration-testverify - - - - - - event-transport-multi-node-gates - - - org.apache.maven.plugins - maven-surefire-plugin - true - - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - - **/EventTransportMultiNodeGate.java - true - true - 3000 - - integration-testverify - - - diff --git a/packages/managed-agent-server/runtime-broker/pom.xml b/packages/managed-agent-server/runtime-broker/pom.xml new file mode 100644 index 00000000000..afe23d07c84 --- /dev/null +++ b/packages/managed-agent-server/runtime-broker/pom.xml @@ -0,0 +1,51 @@ + + 4.0.0 + + com.alibaba + qwen-managed-agent-parent + 0.1.0-alpha + ../pom.xml + + qwen-managed-agent-runtime-broker + Qwen Managed Agent Runtime-Broker + + ${project.basedir}/.. + + + + com.alibaba + qwen-managed-agent-contracts + + + com.fasterxml.jackson.core + jackson-databind + + + org.apache.commons + commons-lang3 + + + commons-codec + commons-codec + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + com.github.spotbugs + spotbugs-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerService.java b/packages/managed-agent-server/runtime-broker/src/main/java/com/alibaba/qwen/code/runtimebroker/service/RuntimeBrokerService.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerService.java rename to packages/managed-agent-server/runtime-broker/src/main/java/com/alibaba/qwen/code/runtimebroker/service/RuntimeBrokerService.java index 392e85d7919..54f19f28bba 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerService.java +++ b/packages/managed-agent-server/runtime-broker/src/main/java/com/alibaba/qwen/code/runtimebroker/service/RuntimeBrokerService.java @@ -1,14 +1,47 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.service; -import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalRuntimeKinds; -import com.alibaba.qwen.code.runtimebroker.provisioning.ProviderRuntimeProtocol; -import com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.ExecutionReconciliation; +import com.alibaba.qwen.code.runtimebroker.HarnessOwnership; +import com.alibaba.qwen.code.runtimebroker.HarnessSessionResolver; +import com.alibaba.qwen.code.runtimebroker.JsonCodec; +import com.alibaba.qwen.code.runtimebroker.ManagedExtensionExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.NativeCsiExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.ResidentWorkspaceProfile; +import com.alibaba.qwen.code.runtimebroker.RuntimeAttestation; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeDrainReceipt; +import com.alibaba.qwen.code.runtimebroker.RuntimeExtensionCommandVerifier; +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority; +import com.alibaba.qwen.code.runtimebroker.RuntimeObservation; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.RuntimePublicationGrant; +import com.alibaba.qwen.code.runtimebroker.RuntimePublicationVerifier; +import com.alibaba.qwen.code.runtimebroker.RuntimeRecoveryEvidence; +import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSession; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.StoreFailureException; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.ToolHoldRow; +import com.alibaba.qwen.code.runtimebroker.protocol.ExternalRuntimeKinds; +import com.alibaba.qwen.code.runtimebroker.protocol.ProviderRuntimeProtocol; import com.alibaba.qwen.code.runtimebroker.transport.CsiFileHistoryProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.ManagedExtensionCommandProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.ManagedExtensionProtocol; import com.alibaba.qwen.code.runtimebroker.transport.ManagedHoldFamily; import com.alibaba.qwen.code.runtimebroker.transport.ManagedHookProtocol; import com.alibaba.qwen.code.runtimebroker.transport.ManagedMcpProtocol; -import com.alibaba.qwen.code.runtimebroker.transport.ManagedExtensionProtocol; -import com.alibaba.qwen.code.runtimebroker.transport.ManagedExtensionCommandProtocol; import com.alibaba.qwen.code.runtimebroker.transport.ManagedMonitorProtocol; import java.nio.charset.StandardCharsets; @@ -42,7 +75,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; /** Repository-backed orchestration for the private Managed Runtime contract. */ -public final class RuntimeBrokerService implements AutoCloseable { +public final class RuntimeBrokerService implements AutoCloseable, com.alibaba.qwen.code.runtimebroker.RuntimeBrokerOperations { // A cancellation answers with one of these states. private static final Set RUNTIME_EXECUTION_STATES = Set.of( "prepared", "executing", "cancel_requested", "settled", @@ -451,8 +484,8 @@ public final class RuntimeBrokerService implements AutoCloseable { || !harnessSessionId.equals(binding.getRequest().getIsolationKey())) { return failed(conflict("workspace_close_identity_unverified", "Saved resource scope differs")); } - if (JdbcCsiFilesRetirementGuard.isProfile(binding.getRequest().getScope())) { - return failed(JdbcCsiFilesRetirementGuard.releaseUnavailable()); + if (CsiFilesRetirementProfile.isProfile(binding.getRequest().getScope())) { + return failed(CsiFilesRetirementProfile.releaseUnavailable()); } work = work.thenCompose(ignored -> { bindingRepository.requireHarnessCleanup(binding.getRequest().getScope(), harnessSessionId, authority); @@ -740,7 +773,7 @@ public final class RuntimeBrokerService implements AutoCloseable { } private CompletionStage acknowledgeDrainedTools(RuntimeBindingRecord binding, String cursor) { - if (!SshRuntimeProvisioner.KIND.equals(binding.getRequest().getProvisionerKind()) || binding.getLease() == null) { + if (!com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS.equals(binding.getRequest().getProvisionerKind()) || binding.getLease() == null) { return CompletableFuture.completedFuture(null); } var batch = executionRepository.findByBinding(binding.getBindingId(), binding.getGeneration(), cursor, 100); @@ -753,7 +786,7 @@ public final class RuntimeBrokerService implements AutoCloseable { } private CompletionStage stopAcknowledgedRuntime(RuntimeBindingRecord binding) { - if (!SshRuntimeProvisioner.KIND.equals(binding.getRequest().getProvisionerKind()) || binding.getLease() == null) { + if (!com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS.equals(binding.getRequest().getProvisionerKind()) || binding.getLease() == null) { return acknowledgeDrainedTools(binding, null).thenCompose(ignored -> provisioner.stopDrained(binding)); } return safeStage(() -> provisioner.reconcile(binding.getRequest(), binding.getProvisionSeed(), @@ -781,7 +814,7 @@ public final class RuntimeBrokerService implements AutoCloseable { } private CompletionStage acknowledgePersistedTools(RuntimeBindingRecord binding, ToolExecutionRecord execution) { - if (!SshRuntimeProvisioner.KIND.equals(binding.getRequest().getProvisionerKind()) + if (!com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS.equals(binding.getRequest().getProvisionerKind()) || !execution.isSettled() || execution.getResult() == null || ProviderRuntimeProtocol.isReference(execution.getReference())) { return CompletableFuture.completedFuture(null); @@ -814,7 +847,7 @@ public final class RuntimeBrokerService implements AutoCloseable { } private static Map sshNoStartReceipt(RuntimeBindingRecord binding, ToolExecutionRecord execution) { - if (!SshRuntimeProvisioner.KIND.equals(binding.getRequest().getProvisionerKind()) + if (!com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS.equals(binding.getRequest().getProvisionerKind()) || !execution.isSettled() || !"not_started".equals(execution.getExecutionStatus()) || execution.getResult() == null || !execution.getResult().containsKey("capture") || execution.getResult().get("capture") != null @@ -1299,7 +1332,7 @@ public final class RuntimeBrokerService implements AutoCloseable { } return requireReadySession(harnessSessionId, runtimeSessionId) .thenCompose(context -> { - if (JdbcCsiFilesRetirementGuard.isProfile(context.session().getScope())) { + if (CsiFilesRetirementProfile.isProfile(context.session().getScope())) { throw new RuntimeBrokerException(501, "csi_control_not_qualified", "Generic CSI runtime controls are not qualified.", false); } @@ -1397,7 +1430,7 @@ public final class RuntimeBrokerService implements AutoCloseable { private CompletionStage csiFileHistory(String harnessSessionId, String runtimeSessionId, Map operation) { return requireSession(harnessSessionId, runtimeSessionId).thenCompose(context -> { - if (!JdbcCsiFilesRetirementGuard.isProfile(context.session().getScope())) { + if (!CsiFilesRetirementProfile.isProfile(context.session().getScope())) { throw conflict("runtime_control_invalid", "CSI history requires the original private Runtime"); } context.lock(); @@ -1522,7 +1555,7 @@ public final class RuntimeBrokerService implements AutoCloseable { .thenApply(context -> { context.lock(); try { - JdbcRuntimeBindingRepository jdbc = privateCsiRepository(context); + NativeCsiExecutionRepository jdbc = privateCsiRepository(context); ToolExecutionRecord candidate = ToolExecutionRecord.prepared(nextExecutionId(), key, context.binding().getBindingId(), context.binding().getGeneration(), harnessSessionId, runtimeSessionId, referenceString(immutable, "promptId"), referenceString(immutable, "callId"), @@ -1556,13 +1589,13 @@ public final class RuntimeBrokerService implements AutoCloseable { }))); } - private JdbcRuntimeBindingRepository privateCsiRepository(SessionContext context) { + private NativeCsiExecutionRepository privateCsiRepository(SessionContext context) { requireReadySessionRecord(context); - if (!JdbcCsiFilesRetirementGuard.isProfile(context.binding().getRequest().getScope()) + if (!CsiFilesRetirementProfile.isProfile(context.binding().getRequest().getScope()) || !context.session().getHarnessSessionId().equals(context.session().getRuntimeSessionId()) || !context.session().getRuntimeSessionId().equals(context.binding().getRequest().getIsolationKey()) || !context.session().getScope().equals(context.binding().getRequest().getScope()) - || !(bindingRepository instanceof JdbcRuntimeBindingRepository jdbc)) { + || !(bindingRepository instanceof NativeCsiExecutionRepository jdbc)) { throw invalid("csi_native_reservation_unavailable", "Original private CSI repositories are required"); } return jdbc; @@ -1689,7 +1722,7 @@ public final class RuntimeBrokerService implements AutoCloseable { && !"run_shell_command".equals(toolName) && !"monitor".equals(toolName)) { throw invalid("runtime_payload_invalid", "Tool payload is invalid"); } - if (JdbcCsiFilesRetirementGuard.isProfile(context.binding().getRequest().getScope()) + if (CsiFilesRetirementProfile.isProfile(context.binding().getRequest().getScope()) && (v3 || !Set.of("read_file", "write_file", "edit").contains(toolName))) { throw invalid("runtime_payload_invalid", "The private CSI profile requires a file Tool v2 payload"); } @@ -1807,7 +1840,7 @@ public final class RuntimeBrokerService implements AutoCloseable { requireOwnedExecution(harnessSessionId, runtimeSessionId, receipt.getExecutionCallId()); RuntimeBindingRecord parent = bindingRepository.findById(receipt.getBindingId()); - if (parent != null && JdbcCsiFilesRetirementGuard.isProfile(parent.getRequest().getScope())) { + if (parent != null && CsiFilesRetirementProfile.isProfile(parent.getRequest().getScope())) { throw invalid("csi_native_resources_required", "Private CSI retry requires original resource bytes"); } if (!BrokerValues.sameJsonMap(receipt.getReference(), @@ -2125,7 +2158,7 @@ public final class RuntimeBrokerService implements AutoCloseable { throw conflict("runtime_execution_conflict", "A background process settles only on stop evidence"); } - if (JdbcCsiFilesRetirementGuard.isProfile(context.binding().getRequest().getScope()) + if (CsiFilesRetirementProfile.isProfile(context.binding().getRequest().getScope()) && current.getAuthorizedDispatchGeneration() != null) { throw conflict("csi_execution_cancel_unavailable", "An authorized private CSI execution does not support cancellation"); @@ -4338,8 +4371,8 @@ public final class RuntimeBrokerService implements AutoCloseable { private CompletionStage reclaimLostBindingNow( RuntimeBindingRecord record, RuntimeProvisionRequest request) { - if (JdbcCsiFilesRetirementGuard.isProfile(record.getRequest().getScope())) { - return failed(JdbcCsiFilesRetirementGuard.releaseUnavailable()); + if (CsiFilesRetirementProfile.isProfile(record.getRequest().getScope())) { + return failed(CsiFilesRetirementProfile.releaseUnavailable()); } RuntimeBindingRecord claimed = bindingRepository.claimOperation( record.getBindingId(), brokerOwnerId, operationLeaseDuration); @@ -4361,8 +4394,8 @@ public final class RuntimeBrokerService implements AutoCloseable { } private CompletionStage cleanupLost(RuntimeBindingRecord claimed) { - if (JdbcCsiFilesRetirementGuard.isProfile(claimed.getRequest().getScope())) { - return failed(JdbcCsiFilesRetirementGuard.releaseUnavailable()); + if (CsiFilesRetirementProfile.isProfile(claimed.getRequest().getScope())) { + return failed(CsiFilesRetirementProfile.releaseUnavailable()); } return safeStage(() -> { RuntimeBindingRecord recovered = recoverLostDrained(claimed, @@ -4827,7 +4860,7 @@ public final class RuntimeBrokerService implements AutoCloseable { private RuntimeSessionRecord requireAcquireSessionRecord(SessionContext context) { RuntimeSessionRecord record = requireReadySessionRecord(context); - return JdbcCsiFilesRetirementGuard.isProfile(record.getSession().getScope()) + return CsiFilesRetirementProfile.isProfile(record.getSession().getScope()) ? bindingRepository.requireSessionAdmission(sessionRepository, record) : record; } @@ -4868,8 +4901,8 @@ public final class RuntimeBrokerService implements AutoCloseable { private void beginDispatch(SessionContext context, ToolExecutionRecord prepared, Map payload, RuntimePublicationGrant grant) { - if (JdbcCsiFilesRetirementGuard.isProfile(context.binding().getRequest().getScope()) - && (!(bindingRepository instanceof JdbcRuntimeBindingRepository) + if (CsiFilesRetirementProfile.isProfile(context.binding().getRequest().getScope()) + && (!(bindingRepository instanceof NativeCsiExecutionRepository) || payload == null || !Set.of("read_file", "write_file", "edit").contains(String.valueOf(payload.get("toolName"))) || grant != null)) { throw new RuntimeBrokerException(501, "csi_file_dispatch_unavailable", "Private CSI dispatch requires original JDBC authorization and a finite file payload.", false); @@ -4986,7 +5019,7 @@ public final class RuntimeBrokerService implements AutoCloseable { } return null; }).thenCompose(ignored -> { - if (!SshRuntimeProvisioner.KIND.equals(context.binding().getRequest().getProvisionerKind())) { + if (!com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS.equals(context.binding().getRequest().getProvisionerKind())) { return CompletableFuture.completedFuture(null); } ToolExecutionRecord settled = executionRepository.findByExecutionCallId(executing.getExecutionCallId()); @@ -5106,7 +5139,7 @@ public final class RuntimeBrokerService implements AutoCloseable { } private static Map dispatchReference(ToolExecutionRecord record, RuntimeScope scope) { - if (JdbcCsiFilesRetirementGuard.isProfile(scope)) { + if (CsiFilesRetirementProfile.isProfile(scope)) { Map reference = new LinkedHashMap<>(record.getReference()); reference.put("executionCallId", record.getExecutionCallId()); return Map.copyOf(reference); @@ -5516,8 +5549,8 @@ public final class RuntimeBrokerService implements AutoCloseable { throw notFound("runtime_session_not_found", "Runtime Session was not found"); } - if (JdbcCsiFilesRetirementGuard.isProfile(current.getSession().getScope())) { - throw JdbcCsiFilesRetirementGuard.releaseUnavailable(); + if (CsiFilesRetirementProfile.isProfile(current.getSession().getScope())) { + throw CsiFilesRetirementProfile.releaseUnavailable(); } for (int attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) { if (current.getState() @@ -5918,7 +5951,7 @@ public final class RuntimeBrokerService implements AutoCloseable { if (cause instanceof RuntimeBrokerException) { throw new CompletionException(cause); } - if (cause instanceof JdbcRepositorySupport.StoreFailureException) { + if (cause instanceof StoreFailureException) { throw new CompletionException(unavailable( "runtime_broker_store_unavailable", "Managed Runtime execution store is unavailable.", diff --git a/packages/managed-agent-server/server/pom.xml b/packages/managed-agent-server/server/pom.xml new file mode 100644 index 00000000000..571608176ea --- /dev/null +++ b/packages/managed-agent-server/server/pom.xml @@ -0,0 +1,319 @@ + + 4.0.0 + + com.alibaba + qwen-managed-agent-parent + 0.1.0-alpha + ../pom.xml + + qwen-managed-agent-server + Qwen Managed Agent Server + + ${project.basedir}/.. + + + + org.projectlombok + lombok + provided + true + + + com.alibaba + qwen-managed-agent-contracts + + + com.alibaba + qwen-managed-agent-control-plane + + + com.alibaba + qwen-managed-agent-runtime-broker + + + com.alibaba + qwen-managed-agent-persistence + + + com.alibaba + qwen-managed-agent-adapters + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + true + + + org.springframework.boot + spring-boot-starter-jdbc + true + + + com.zaxxer + HikariCP + + + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework + spring-jdbc + + + com.alibaba + druid + + + org.flywaydb + flyway-core + true + + + org.flywaydb + flyway-mysql + true + + + com.mysql + mysql-connector-j + runtime + + + com.h2database + h2 + runtime + true + + + com.fasterxml.jackson.core + jackson-databind + + + com.alibaba.fastjson2 + fastjson2 + + + org.apache.commons + commons-lang3 + + + commons-codec + commons-codec + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + ${lombok.version} + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + com.github.spotbugs + spotbugs-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + org.apache.maven.plugins + maven-jar-plugin + + + default-jar + + + application*.yml + + + + + workspace-bundle-resources + ${managed.operator-tools.jar.phase} + + jar + + + workspace-bundle + + + + workspace-migration-resources + ${managed.operator-tools.jar.phase} + + jar + + + workspace-migration + + + + workspace-maintenance-recovery-resources + ${managed.operator-tools.jar.phase} + + jar + + + workspace-maintenance-recovery + + + + csi-session-resources + ${managed.operator-tools.jar.phase} + + jar + + + csi-session + + + + csi-runtime-resources + ${managed.operator-tools.jar.phase} + + jar + + + csi-runtime + + + + operator-recovery-resources + ${managed.operator-tools.jar.phase} + + jar + + + operator-recovery + + + + standalone-resources + + jar + + + exec + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + repackage + + exec + com.alibaba.qwen.code.managedagent.ManagedAgentServerApplication + + + + workspace-bundle + + repackage + + + workspace-bundle + ${managed.operator-tools.attach} + ${managed.operator-tools.skip} + com.alibaba.qwen.code.managedagent.maintenance.WorkspaceRecoveryMain + + + + workspace-migration + + repackage + + + workspace-migration + ${managed.operator-tools.attach} + ${managed.operator-tools.skip} + com.alibaba.qwen.code.managedagent.maintenance.WorkspaceMigrationMain + + + + workspace-maintenance-recovery + + repackage + + + workspace-maintenance-recovery + ${managed.operator-tools.attach} + ${managed.operator-tools.skip} + com.alibaba.qwen.code.managedagent.maintenance.WorkspaceMaintenanceRecoveryMain + + + + csi-session + + repackage + + + csi-session + ${managed.operator-tools.attach} + ${managed.operator-tools.skip} + com.alibaba.qwen.code.managedagent.maintenance.WorkspaceCsiSessionMain + + + + csi-runtime + + repackage + + + csi-runtime + ${managed.operator-tools.attach} + ${managed.operator-tools.skip} + com.alibaba.qwen.code.managedagent.maintenance.WorkspaceCsiRuntimeMain + + + + operator-recovery + + repackage + + + operator-recovery + ${managed.operator-tools.attach} + ${managed.operator-tools.skip} + com.alibaba.qwen.code.managedagent.maintenance.WorkspaceRecoveryCommand + + + + + + + diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/maintenance/WorkspaceRecoveryApplication.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/maintenance/WorkspaceRecoveryApplication.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/maintenance/WorkspaceRecoveryApplication.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/maintenance/WorkspaceRecoveryApplication.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/ManagedAgentServerApplication.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/ManagedAgentServerApplication.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/ManagedAgentServerApplication.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/ManagedAgentServerApplication.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/AgentDefinitionController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/AgentDefinitionController.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/AgentDefinitionController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/AgentDefinitionController.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandler.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandler.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandler.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandler.java index 18e293459f1..af93165a138 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandler.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandler.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.api; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationStorageException; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationStorageException; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.security.PublicSurface; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; @@ -43,7 +43,7 @@ public class ApiExceptionHandler { @ExceptionHandler(ManagedAgentException.class) public ResponseEntity> api(ManagedAgentException error, HttpServletRequest request, HttpServletResponse response) { - return response(request, response, error.getStatus(), error.getCode(), + return response(request, response, HttpStatus.valueOf(error.getStatus()), error.getCode(), error.getMessage(), error.getDetails()); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/AuthenticatedTenantActor.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/AuthenticatedTenantActor.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/AuthenticatedTenantActor.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/AuthenticatedTenantActor.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ExternalStorageRegistrationController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ExternalStorageRegistrationController.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ExternalStorageRegistrationController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ExternalStorageRegistrationController.java index 9f8060abc7e..5782b3f591e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ExternalStorageRegistrationController.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ExternalStorageRegistrationController.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.api; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.persistence.ExternalStorageRegistration; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ExternalStorageRegistration; import com.alibaba.qwen.code.managedagent.persistence.ExternalStorageRegistrationStore; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.fasterxml.jackson.core.JsonParser; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedActionController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedActionController.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedActionController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedActionController.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedArtifactController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedArtifactController.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedArtifactController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedArtifactController.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedAutomationController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedAutomationController.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedAutomationController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedAutomationController.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedChannelAdapterController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedChannelAdapterController.java similarity index 78% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedChannelAdapterController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedChannelAdapterController.java index c99d71252b1..fb2ff06a639 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedChannelAdapterController.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedChannelAdapterController.java @@ -1,15 +1,15 @@ package com.alibaba.qwen.code.managedagent.api; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ChannelInstanceView; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ClaimRequest; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ClaimResponse; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.DeliveryView; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.InboundAdmission; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.InboundEventRequest; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ReceiptRequest; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.RegisterChannelRequest; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ResendResponse; import com.alibaba.qwen.code.managedagent.channel.ManagedChannelService; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ChannelInstanceView; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ClaimRequest; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ClaimResponse; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.DeliveryView; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.InboundAdmission; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.InboundEventRequest; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ReceiptRequest; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.RegisterChannelRequest; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ResendResponse; import com.alibaba.qwen.code.managedagent.model.TenantContext; import jakarta.validation.Valid; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedChannelController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedChannelController.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedChannelController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedChannelController.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedExtensionManagementController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedExtensionManagementController.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedExtensionManagementController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedExtensionManagementController.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedExtensionPackageController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedExtensionPackageController.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedExtensionPackageController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedExtensionPackageController.java index e032a493ddb..f21a8c1008b 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedExtensionPackageController.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedExtensionPackageController.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.api; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages; import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackageService; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages; import com.alibaba.qwen.code.managedagent.model.TenantContext; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedHookController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedHookController.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedHookController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedHookController.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedMcpController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedMcpController.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedMcpController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedMcpController.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedSessionStoreController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedSessionStoreController.java similarity index 89% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedSessionStoreController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedSessionStoreController.java index a5e8be95e89..beb02b2c377 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedSessionStoreController.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedSessionStoreController.java @@ -1,24 +1,24 @@ package com.alibaba.qwen.code.managedagent.api; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.BlockRecoveryRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitReceipt; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.PublishToolResultRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RecoveryStateReceipt; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RenewWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RestoreHead; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealReceipt; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.ToolResultResourceRef; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.TransactionPage; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.WriterGrant; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.TenantContext; import com.alibaba.qwen.code.managedagent.persistence.ManagedNativeJournalSnapshots; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.BlockRecoveryRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitReceipt; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.PublishToolResultRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.RecoveryStateReceipt; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.RenewWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.RestoreHead; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.SealReceipt; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.SealWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.ToolResultResourceRef; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.TransactionPage; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.WriterGrant; import com.alibaba.qwen.code.managedagent.persistence.ManagedUserMessageStore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonUnwrapped; @@ -82,7 +82,7 @@ public class ManagedSessionStoreController { try { return new com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority(operation, Long.parseLong(generation)); } catch (RuntimeException error) { - throw new ManagedAgentException(org.springframework.http.HttpStatus.BAD_REQUEST, "invalid_lifecycle_authority", "Invalid lifecycle authority."); + throw new ManagedAgentException(400, "invalid_lifecycle_authority", "Invalid lifecycle authority."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedToolDetailsController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedToolDetailsController.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedToolDetailsController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ManagedToolDetailsController.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/PublicAgentController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/PublicAgentController.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/PublicAgentController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/PublicAgentController.java index db96422da16..26eb25270d2 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/PublicAgentController.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/PublicAgentController.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.managedagent.api; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.CommandAdmission; import com.alibaba.qwen.code.managedagent.model.ApiModels.CompressSessionRequest; @@ -27,7 +28,6 @@ import com.alibaba.qwen.code.managedagent.session.ManagedTaskService; import com.alibaba.qwen.code.managedagent.session.SessionLifecycleService; import com.alibaba.qwen.code.managedagent.session.SessionRecoveryService; import com.alibaba.qwen.code.managedagent.workspace.SessionWorktreeService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import jakarta.validation.Valid; import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpHeaders; @@ -80,27 +80,27 @@ public class PublicAgentController { WorkspaceSelection selection = null; if (request.workspace() != null) { if (request.workspace().isNull()) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_request", "Workspace cannot be null."); } selection = WorkspaceSelection.parse(request.workspace(), false); tenant.requireActorId(); } if (Boolean.TRUE.equals(request.stream())) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "unsupported_feature", "Phase 1 streams through the Session events route."); } if (selection == null && request.skills() != null && !request.skills().isEmpty()) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_feature", + throw new ManagedAgentException(400, "unsupported_feature", "Session Skills require a bound Workspace."); } if (selection == null && request.extensionPins() != null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_feature", "Extension packages require a bound Workspace."); + throw new ManagedAgentException(400, "unsupported_feature", "Extension packages require a bound Workspace."); } var worktree = SessionWorktreeService.parse(request.worktree(), false); if (selection == null && worktree != null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_feature", "Session worktrees require a bound Workspace."); + throw new ManagedAgentException(400, "unsupported_feature", "Session worktrees require a bound Workspace."); } CommandAdmission admission = selection == null ? service.createSession(tenant.tenantId(), tenant.actorId(), @@ -223,14 +223,14 @@ public class PublicAgentController { idempotencyKey, sessionId, request.input()); } else if ("agent.session.cancel".equals(request.type())) { if (request.turnId() == null || request.turnId().isBlank()) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "turn_id_required", "Cancellation requires turn_id."); } admission = service.cancelTurn(tenant.tenantId(), tenant.actorId(), idempotencyKey, sessionId, request.turnId()); } else { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "unsupported_event", "Phase 1 accepts input message and cancellation events."); } @@ -272,7 +272,7 @@ public class PublicAgentController { sequence = Long.parseLong(after); } catch (NumberFormatException error) { throw new ManagedAgentException( - org.springframework.http.HttpStatus.BAD_REQUEST, + 400, "invalid_event_cursor", "Item sequence is invalid."); } @@ -344,7 +344,7 @@ public class PublicAgentController { } return value; } catch (NumberFormatException error) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_event_cursor", "Last-Event-ID is invalid."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/RequestIdFilter.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/RequestIdFilter.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/RequestIdFilter.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/RequestIdFilter.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/SessionWorktreeController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/SessionWorktreeController.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/SessionWorktreeController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/SessionWorktreeController.java index c3780aa5ff2..7e87efbcba4 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/SessionWorktreeController.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/SessionWorktreeController.java @@ -1,9 +1,9 @@ package com.alibaba.qwen.code.managedagent.api; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.TenantContext; -import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore.Descriptor; +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Descriptor; import com.alibaba.qwen.code.managedagent.workspace.SessionWorktreeService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import org.springframework.http.CacheControl; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilter.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilter.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilter.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilter.java index 5d9a0a9a869..9306b485c36 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilter.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilter.java @@ -309,7 +309,7 @@ public class SignatureAuthFilter extends OncePerRequestFilter private void authenticateArchive(HttpServletRequest request, HttpServletResponse response, FilterChain chain, String tenantId, String actorId, String signature, String timestamp) throws IOException, ServletException { - long maximum = com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.MAX_ARCHIVE_BYTES; + long maximum = com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.MAX_ARCHIVE_BYTES; if (request.getContentLengthLong() > maximum) { reject(request, response, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, "payload_too_large", "Extension archive exceeds 100 MiB."); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/SshWorkspaceController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/SshWorkspaceController.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/SshWorkspaceController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/SshWorkspaceController.java index e33bb0a7144..8cef6a7205c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/SshWorkspaceController.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/SshWorkspaceController.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.api; import com.alibaba.qwen.code.managedagent.model.TenantContext; -import com.alibaba.qwen.code.managedagent.persistence.SshWorkspaceStore.Operation; +import com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.Operation; import com.alibaba.qwen.code.managedagent.workspace.SshWorkspaceService; import com.alibaba.qwen.code.managedagent.workspace.SshWorkspaceService.Directory; import com.alibaba.qwen.code.managedagent.workspace.SshWorkspaceService.NodePage; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/TenantContextArgumentResolver.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/TenantContextArgumentResolver.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/TenantContextArgumentResolver.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/TenantContextArgumentResolver.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/TenantContextFilter.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/TenantContextFilter.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/TenantContextFilter.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/TenantContextFilter.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationController.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationController.java index 6d10f403f07..c42a77af2e5 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationController.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationController.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.managedagent.api; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.TenantContext; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationAdmissionStore; -import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; import com.fasterxml.jackson.databind.JsonNode; @@ -246,7 +246,7 @@ public class ToolPublicationController { private T limited(Supplier operation) { if (!entries.tryAcquire()) { - throw new ManagedAgentException(HttpStatus.TOO_MANY_REQUESTS, + throw new ManagedAgentException(429, "managed_tool_publication_busy", "Tool publication entry capacity is exhausted"); } try { @@ -260,7 +260,7 @@ public class ToolPublicationController { try { byte[] body = request.getInputStream().readNBytes(maximum + 1); if (body.length > maximum) { - throw new ManagedAgentException(HttpStatus.PAYLOAD_TOO_LARGE, + throw new ManagedAgentException(413, "managed_tool_publication_too_large", "Tool publication body exceeds its limit"); } return body; @@ -277,7 +277,7 @@ public class ToolPublicationController { private static void requireScope(TenantContext tenant, String sessionId, JsonNode key) { if (!tenant.tenantId().equals(key.path("tenantId").asText()) || !sessionId.equals(key.path("sessionId").asText())) { - throw new ManagedAgentException(HttpStatus.NOT_FOUND, + throw new ManagedAgentException(404, "managed_tool_publication_scope", "Publication Session scope conflicts"); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/TrustedActorHeaderFilter.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/TrustedActorHeaderFilter.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/TrustedActorHeaderFilter.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/TrustedActorHeaderFilter.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/UserMessageController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/UserMessageController.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/UserMessageController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/UserMessageController.java index 868895d37ce..767a20e76d0 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/UserMessageController.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/UserMessageController.java @@ -2,8 +2,8 @@ package com.alibaba.qwen.code.managedagent.api; import com.alibaba.qwen.code.managedagent.model.TenantContext; import com.alibaba.qwen.code.managedagent.persistence.ManagedUserMessageStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedUserMessageStore.Command; -import com.alibaba.qwen.code.managedagent.persistence.ManagedUserMessageStore.Snapshot; +import com.alibaba.qwen.code.managedagent.repository.ManagedUserMessageStore.Command; +import com.alibaba.qwen.code.managedagent.repository.ManagedUserMessageStore.Snapshot; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WebShellAgentController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WebShellAgentController.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WebShellAgentController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WebShellAgentController.java index 3c3c70cce70..ff23f991ffd 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WebShellAgentController.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WebShellAgentController.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.managedagent.api; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels; import com.alibaba.qwen.code.managedagent.model.ApiModels.CommandAdmission; @@ -46,11 +47,9 @@ import com.alibaba.qwen.code.managedagent.session.ManagedTaskService; import com.alibaba.qwen.code.managedagent.session.SessionLifecycleService; import com.alibaba.qwen.code.managedagent.session.SessionRecoveryService; import com.alibaba.qwen.code.managedagent.workspace.SessionWorktreeService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -217,31 +216,31 @@ public class WebShellAgentController { WorkspaceSelection selection = null; if (request.workspace() != null) { if (request.workspace().isNull()) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "invalid_request", "Workspace cannot be null."); } selection = WorkspaceSelection.parse(request.workspace(), true); tenant.requireActorId(); } if (selection == null && (request.approvalMode() != null || request.planMode() != null)) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, + throw new ManagedAgentException(400, "unsupported_approval_mode", "Approval mode selection requires a bound Workspace."); } if (selection == null && request.modelId() != null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_feature", + throw new ManagedAgentException(400, "unsupported_feature", "Model selection requires a bound Workspace."); } if (selection == null && request.skills() != null && !request.skills().isEmpty()) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_feature", + throw new ManagedAgentException(400, "unsupported_feature", "Session Skills require a bound Workspace."); } if (selection == null && request.extensionPins() != null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_feature", "Extension packages require a bound Workspace."); + throw new ManagedAgentException(400, "unsupported_feature", "Extension packages require a bound Workspace."); } var worktree = SessionWorktreeService.parse(request.worktree(), true); if (selection == null && worktree != null) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "unsupported_feature", "Session worktrees require a bound Workspace."); + throw new ManagedAgentException(400, "unsupported_feature", "Session worktrees require a bound Workspace."); } // The WebShell handlers forward no metadata at all (null in its // slot): whatever the client sends is accepted and ignored, so no diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WebShellSkillsController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WebShellSkillsController.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WebShellSkillsController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WebShellSkillsController.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationController.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationController.java index 59ded0ea56b..4e8249d1b70 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationController.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationController.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent.api; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.TenantContext; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationStore; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.HttpHeaders; @@ -45,7 +45,7 @@ public class WorkspaceCsiApplicationController { public Map workerAuthority(TenantContext tenant, @PathVariable String sessionId, @RequestHeader(HttpHeaders.AUTHORIZATION) String authorization, @RequestBody byte[] bytes) { if (authorization.length() > 519 || !authorization.matches("Bearer [A-Za-z0-9._~+/-]{1,512}=*")) { - throw new ManagedAgentException(org.springframework.http.HttpStatus.UNAUTHORIZED, "csi_application_authority_unauthorized", "Original Runtime authentication failed."); + throw new ManagedAgentException(401, "csi_application_authority_unauthorized", "Original Runtime authentication failed."); } return store.workerAuthority(tenant.tenantId(), sessionId, authorization.substring(7), bytes); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceDiscoveryController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceDiscoveryController.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceDiscoveryController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceDiscoveryController.java index 0b2770016a2..df617d40c3e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceDiscoveryController.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceDiscoveryController.java @@ -2,8 +2,8 @@ package com.alibaba.qwen.code.managedagent.api; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.TenantContext; -import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry.WorkspaceSummary; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCreationStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.WorkspaceSummary; import com.alibaba.qwen.code.managedagent.workspace.WorkspaceDiscoveryService; import com.alibaba.qwen.code.managedagent.workspace.WorkspaceDiscoveryService.WorkspacePage; import com.fasterxml.jackson.annotation.JsonAnySetter; @@ -12,7 +12,6 @@ import com.fasterxml.jackson.databind.JsonNode; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import org.springframework.http.CacheControl; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -90,14 +89,14 @@ public class WorkspaceDiscoveryController { if (request.localPath() != null) { if (request.workspaceId() != null || request.cwdRelative() != null) { throw new ManagedAgentException( - HttpStatus.BAD_REQUEST, "invalid_request", + 400, "invalid_request", "Local path and source Workspace must not be combined."); } return noStore(creation.localDirectories(request.localPath())); } if (request.workspaceId() == null || request.cwdRelative() == null) { throw new ManagedAgentException( - HttpStatus.BAD_REQUEST, "invalid_request", + 400, "invalid_request", "Source Workspace and directory are required."); } return noStore(creation.directories(tenant.tenantId(), actor, diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceFileController.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceFileController.java similarity index 96% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceFileController.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceFileController.java index 9739485d9b6..9ac7f5ad47f 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceFileController.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/api/WorkspaceFileController.java @@ -11,7 +11,6 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; import org.springframework.http.CacheControl; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -77,7 +76,7 @@ public class WorkspaceFileController { String actor = tenant.requireActorId(); String encoding = request.getHeader("Content-Encoding"); if (encoding != null && !"identity".equalsIgnoreCase(encoding)) { - throw new ManagedAgentException(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "invalid_request", "Encoded Workspace uploads are unsupported."); + throw new ManagedAgentException(415, "invalid_request", "Encoded Workspace uploads are unsupported."); } var locator = new WorkspaceFileStore.Locator(sessionId, workspaceId, cwdRelative); return ResponseEntity.ok().cacheControl(CacheControl.noStore()).body(uploads.upload(tenant.tenantId(), actor, diff --git a/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/AliyunToolPublicationStorageAutoConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/AliyunToolPublicationStorageAutoConfiguration.java new file mode 100644 index 00000000000..6daaabfdec9 --- /dev/null +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/AliyunToolPublicationStorageAutoConfiguration.java @@ -0,0 +1,25 @@ +package com.alibaba.qwen.code.managedagent.config; + +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; + +@AutoConfiguration +@ConditionalOnProperty(prefix = "qwen.managed-agent.tool-publication", + name = "enabled", havingValue = "true") +@ConditionalOnBean(ToolPublicationConfiguration.class) +@ConditionalOnMissingBean(ToolPublicationObjectStore.class) +public class AliyunToolPublicationStorageAutoConfiguration { + @Bean(destroyMethod = "shutdown") + public com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient toolPublicationOss(ManagedAgentProperties properties) throws Exception { + return com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient.environment(properties); + } + @Bean + public ToolPublicationObjectStore toolPublicationObjects(com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient toolPublicationOss, + ManagedAgentProperties properties) { + return toolPublicationOss.objects(properties.getToolPublication().getOssBucket()); + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/BrokerSecurity.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/BrokerSecurity.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/BrokerSecurity.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/BrokerSecurity.java diff --git a/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfiguration.java new file mode 100644 index 00000000000..4ac440a62ee --- /dev/null +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfiguration.java @@ -0,0 +1,161 @@ +package com.alibaba.qwen.code.managedagent.config; + +import com.alibaba.qwen.code.managedagent.adapters.event.LiteTopicEventTransport; +import com.alibaba.qwen.code.managedagent.adapters.event.LiteTopicPublicEventTransport; +import com.alibaba.qwen.code.managedagent.adapters.event.RocketMqEventClients; +import com.alibaba.qwen.code.managedagent.contracts.event.EventSubscriptions; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.event.EventTransportConsumer; +import com.alibaba.qwen.code.managedagent.event.MessageMaterializer; +import com.alibaba.qwen.code.managedagent.event.TransportWakeActivator; +import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore; +import com.alibaba.qwen.code.managedagent.session.OwnerSubscriptions; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.task.ThreadPoolTaskSchedulerBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +import java.util.concurrent.ScheduledFuture; + +/** + * Wires the broker EventTransport (design + * 2026-10-04-managed-agent-event-transport): the authoritative-journal + * producer (phase MQ2b), and the public-event producer and the two consumer + * lanes (phase MQ2c). Every bean here exists only when + * {@code qwen.managed-agent.event-transport.enabled=true}, so a flag-off + * deployment creates no producer, no consumer and no transport, and the + * after-commit seam's transport list stays empty — the MQ2 byte-identical + * exit check is an inspectable property of this class, not a claim. + * + *

Deployment prerequisites, all on the broker (RocketMQ 5.5.0+ with + * {@code enableLmq}): the topic is a LITE topic, and three groups are + * pre-created as LITE groups bound to it — the configured capability-probe, + * wake-bootstrap and owner groups — because subscribeLite rejects an + * auto-created (regular) group with "regular group cannot use LITE mode". + * mqadmin creates nothing, printing only its usage, unless a cluster (-c) or + * broker (-b) is named: + *

+ *   mqadmin updateTopic -c <cluster> -t <topic> -a +message.type=LITE
+ *   mqadmin updateSubGroup -c <cluster> -g <group> -o true \
+ *     --attributes +lite.bind.topic=<topic>
+ * 
+ * scripts/rocketmq-lite-broker.sh provisions exactly this for the broker + * gates. + * The wake-bootstrap group is shared by every node: the one bootstrap + * LiteTopic is consumed in order by one member at a time, so its listener + * only checks SQL and claims, and hands each load to a small pool. The owner + * group is shared too, and each node subscribes it only to the Sessions its + * Harness writes (§11). + */ +@Configuration +@ConditionalOnProperty(prefix = "qwen.managed-agent.event-transport", + name = "enabled", havingValue = "true") +public class EventTransportConfiguration { + // The lite topic only the startup capability probe uses. + static final String CAPABILITY_PROBE_LITE = "capability-probe"; + + // Replaceable, so the wiring can be exercised without a broker. + @Bean + @ConditionalOnMissingBean(RocketMqEventClients.class) + public RocketMqEventClients eventTransportClientProvider() { return new RocketMqEventClients(); } + + /** The one producer both wires share; closing it closes the connections. */ + @Bean(destroyMethod = "close") + public RocketMqEventClients.Publisher eventTransportProducer(RocketMqEventClients provider, ManagedAgentProperties properties) throws Exception { + return provider.producer(properties); + } + + @Bean + public LiteTopicEventTransport liteTopicEventTransport(RocketMqEventClients.Publisher producer, RocketMqEventClients provider, ManagedAgentProperties properties) { + return provider.journal(producer, properties); + } + + @Bean + public TransportConsumerStore transportConsumerStore(@Qualifier("managedJdbcTemplate") JdbcTemplate jdbc) { + return new TransportConsumerStore(jdbc); + } + + @Bean(destroyMethod = "close") + public LiteTopicPublicEventTransport liteTopicPublicEventTransport(RocketMqEventClients.Publisher producer, RocketMqEventClients provider, + ManagedAgentProperties properties, @Qualifier("managedAgentObjectMapper") ObjectMapper objectMapper, TransportConsumerStore store) { + return provider.publicEvents(producer, properties, objectMapper, store::workspaceOf); + } + + @Bean + public OwnerSubscriptions ownerSubscriptions(HarnessConnector harness, + TransportConsumerStore store) { + return new OwnerSubscriptions(harness, store); + } + + @Bean(destroyMethod = "close") + public TransportWakeActivator transportWakeActivator( + TransportConsumerStore store, HarnessConnector harness, + OwnerSubscriptions owners) { + return new TransportWakeActivator(store, harness, owners::subscribe); + } + + @Bean(destroyMethod = "close") + public EventTransportConsumer eventTransportConsumer( + TransportWakeActivator wakes, TransportConsumerStore store, + MessageMaterializer materializer) { + return new EventTransportConsumer(wakes, store, materializer); + } + + /** + * The owner lane: subscribed per Session by {@link OwnerSubscriptions}, + * never to a fixed LiteTopic. + */ + @Bean(destroyMethod = "close") + public EventSubscriptions eventTransportOwnerConsumer(RocketMqEventClients provider, ManagedAgentProperties properties, + EventTransportConsumer listener, OwnerSubscriptions owners) throws Exception { + return provider.owner(properties, listener, owners::bind); + } + + /** The shared wake bootstrap lane: every node subscribes the one LiteTopic. */ + @Bean(destroyMethod = "close") + public EventSubscriptions eventTransportWakeBootstrapConsumer(RocketMqEventClients provider, ManagedAgentProperties properties, + EventTransportConsumer listener) throws Exception { return provider.wake(properties, listener); } + + /** + * Owner reconciliation runs on its own thread: it calls the broker, and + * must never delay the one-thread default scheduler's recovery ticks. + */ + @Bean(defaultCandidate = false) + public ThreadPoolTaskScheduler eventTransportScheduler( + ThreadPoolTaskSchedulerBuilder builder) { + return builder.poolSize(1).threadNamePrefix("event-transport-") + .build(); + } + + @Bean(destroyMethod = "cancel") + public ScheduledFuture eventTransportOwnerReconcileTask( + @Qualifier("eventTransportScheduler") + ThreadPoolTaskScheduler eventTransportScheduler, + OwnerSubscriptions owners, + // Ensures the owner consumer is bound before the first tick. + @Qualifier("eventTransportOwnerConsumer") + EventSubscriptions ownerConsumer, + ManagedAgentProperties properties) { + return eventTransportScheduler.scheduleWithFixedDelay(owners::reconcile, + properties.getEventTransport().getOwnerReconcileInterval()); + } + + // #13200 hardening: fail fast at startup on a broker that cannot serve + // LiteTopics (needs RocketMQ 5.5.0+ with enableLmq), instead of silently + // degrading to a classic topic. This probe subscribes and unsubscribes and + // writes nothing; the send probe below writes one message. Building the + // consumer also catches an unreachable or non-gRPC (classic 4.x) broker. + + // The subscription probe passes on a NORMAL topic too — a group binds to + // a topic whatever its message type — and every envelope would then be + // refused at send time. One synchronous send to the probe's own lite + // topic is refused by a topic that is not LITE, so that misconfiguration + // fails startup instead. Nothing consumes the probe lite topic beyond + // the subscription probe's own brief subscribe, whose listener only + // acknowledges; the broker expires it. +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ExecutionConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ExecutionConfiguration.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ExecutionConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ExecutionConfiguration.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/HarnessConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/HarnessConfiguration.java similarity index 83% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/HarnessConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/HarnessConfiguration.java index 986111a4e0d..5e230e7c27a 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/HarnessConfiguration.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/HarnessConfiguration.java @@ -1,6 +1,7 @@ package com.alibaba.qwen.code.managedagent.config; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.harness.HarnessDiscovery; import com.alibaba.qwen.code.managedagent.harness.HarnessPoolConnector; import com.alibaba.qwen.code.managedagent.harness.KubernetesHarnessDiscovery; @@ -13,7 +14,6 @@ import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionSkills; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.SshWorkspaceStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -26,6 +26,11 @@ import java.util.HashSet; @Configuration public class HarnessConfiguration { + @Bean + public com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRules managedHarnessRules() { + return new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules(); + } + @Bean(destroyMethod = "close") @ConditionalOnProperty( prefix = "qwen.managed-agent.harness", @@ -67,19 +72,22 @@ public class HarnessConfiguration { throw new IllegalStateException("Harness pool discovery must be static or kubernetes"); } try { - return new HarnessPoolConnector(properties, placements, discovery, + return new com.alibaba.qwen.code.managedagent.harness.SdkHarnessBoundary(new HarnessPoolConnector(properties, placements, discovery, endpoint -> { var connector = new QwenHostedHarnessConnector(properties, sessions, workspaceExecution, actions, definitions, resources, ssh); connector.configureExtensionResources(extensionRecords, json); return connector; - }, skills); + }, skills)); } catch (RuntimeException error) { discovery.close(); throw error; } } - return new QwenHostedHarnessConnector(properties, sessions, workspaceExecution, actions, definitions, resources, ssh); + var connector = new QwenHostedHarnessConnector(properties, sessions, workspaceExecution, actions, definitions, resources, ssh); + connector.configureSessionSkills(skills); + connector.configureExtensionResources(extensionRecords, json); + return new com.alibaba.qwen.code.managedagent.harness.SdkHarnessBoundary(connector); } @Bean diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/InternalSurfaceConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/InternalSurfaceConfiguration.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/InternalSurfaceConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/InternalSurfaceConfiguration.java diff --git a/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentConfiguration.java new file mode 100644 index 00000000000..3a98d801d3e --- /dev/null +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentConfiguration.java @@ -0,0 +1,138 @@ +package com.alibaba.qwen.code.managedagent.config; + +import com.alibaba.qwen.code.managedagent.ManagedAgentServerApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.Import; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * Explicitly imports the control plane into the caller's application context, without servlet + * endpoints. + */ +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(ManagedAgentProperties.class) +@EnableScheduling +@EnableTransactionManagement(proxyTargetClass = true) +@Import({ + ManagedAgentConfiguration.PersistenceImport.class, + ExecutionConfiguration.class, + HarnessConfiguration.class, + RuntimeBrokerConfiguration.class, + RuntimeRetentionConfiguration.class, + ManagedArtifactConfiguration.class, + ToolPublicationConfiguration.class, + EventTransportConfiguration.class, + AliyunToolPublicationStorageAutoConfiguration.class +}) +@ComponentScan( + basePackages = { + "com.alibaba.qwen.code.managedagent.catalog", + "com.alibaba.qwen.code.managedagent.workspace", + "com.alibaba.qwen.code.managedagent.extensions", + "com.alibaba.qwen.code.managedagent.channel", + "com.alibaba.qwen.code.managedagent.artifact", + "com.alibaba.qwen.code.managedagent.automation", + "com.alibaba.qwen.code.managedagent.event", + "com.alibaba.qwen.code.managedagent.session", + "com.alibaba.qwen.code.managedagent.persistence", + "com.alibaba.qwen.code.managedagent.adapters.extensions" + }, + excludeFilters = { + @ComponentScan.Filter( + type = FilterType.ANNOTATION, + classes = { + Configuration.class, + org.springframework.stereotype.Controller.class, + org.springframework.web.bind.annotation.ControllerAdvice.class + }), + @ComponentScan.Filter( + type = FilterType.CUSTOM, + classes = org.springframework.boot.context.TypeExcludeFilter.class), + @ComponentScan.Filter( + type = FilterType.CUSTOM, + classes = + org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter + .class), + @ComponentScan.Filter( + type = FilterType.REGEX, + pattern = "com\\.alibaba\\.qwen\\.code\\.managedagent\\.api\\..*"), + @ComponentScan.Filter( + type = FilterType.ASSIGNABLE_TYPE, + classes = { + ManagedAgentServerApplication.class, + ManagedAgentWebConfiguration.class, + ManagedAgentInternalConfiguration.class, + ManagedSchemaConfiguration.class, + ManagedLocalConfiguration.class, + WebConfiguration.class, + ManagedSessionJsonConfiguration.class, + InternalSurfaceConfiguration.class, + InternalSurfaceConfiguration.RoutingFilter.class, + BrokerSecurity.class + }) + }) +public class ManagedAgentConfiguration { + @org.springframework.context.annotation.Bean + public com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles managedWorktreeFiles() { + return new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles(); + } + @org.springframework.context.annotation.Bean + public com.alibaba.qwen.code.managedagent.contracts.workspace.SessionWorktreeOperations.Factory managedWorktreeFactory() { + return (git, timeout) -> new com.alibaba.qwen.code.managedagent.adapters.workspace.SessionWorktreeGit( + new com.alibaba.qwen.code.managedagent.adapters.workspace.ChildWorktreeGit(git, timeout)); + } + @org.springframework.context.annotation.Bean + public com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader managedStorageIdentityReader() { + return new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader(); + } + @org.springframework.context.annotation.Bean + public com.alibaba.qwen.code.managedagent.contracts.workspace.LocalWorkspaceFiles managedLocalWorkspaceFiles() { + return new com.alibaba.qwen.code.runtimebroker.provisioning.WorkspaceFileProcess(); + } + @org.springframework.context.annotation.Bean + public com.alibaba.qwen.code.managedagent.contracts.persistence.StorageReadPolicy managedSessionReaderVersion() { + return new ManagedSessionReaderVersion(); + } + @org.springframework.context.annotation.Bean(destroyMethod = "") + public com.alibaba.qwen.code.runtimebroker.spi.SshConnections managedSshConnections(ManagedAgentProperties properties) { + return properties.getRuntimeBroker().getSsh().getNodes().isEmpty() ? null + : new com.alibaba.qwen.code.runtimebroker.provisioning.SshConnectionManager( + java.net.URI.create(properties.getToolPublication().getServiceBaseUrl())); + } + @org.springframework.context.annotation.Bean + public com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelRouteRepository managedChannelRoutes( + @org.springframework.beans.factory.annotation.Qualifier("managedJdbcTemplate") org.springframework.jdbc.core.JdbcTemplate jdbc) { + return new com.alibaba.qwen.code.managedagent.persistence.JdbcChannelRouteRepository(jdbc); + } + @org.springframework.context.annotation.Bean + public com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository managedChannelDeliveries( + @org.springframework.beans.factory.annotation.Qualifier("managedJdbcTemplate") org.springframework.jdbc.core.JdbcTemplate jdbc) { + return new com.alibaba.qwen.code.managedagent.persistence.JdbcChannelDeliveryRepository(jdbc); + } + + @org.springframework.context.annotation.Bean + public com.alibaba.qwen.code.managedagent.model.RequestDigests managedRequestDigests() { + return new com.alibaba.qwen.code.managedagent.model.RequestDigests(); + } + + @org.springframework.context.annotation.Bean + public com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy managedWriterCredentialPolicy( + ManagedAgentProperties properties) { + return new com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy(properties); + } + + public ManagedAgentConfiguration(ManagedAgentProperties properties) { + BrokerSecurity.checkPublicationTransport(properties, new java.util.ArrayList<>()); + } + + static class PersistenceImport implements org.springframework.context.annotation.DeferredImportSelector { + @Override + public String[] selectImports(org.springframework.core.type.AnnotationMetadata metadata) { + return new String[] {ManagedAgentPersistenceConfiguration.class.getName()}; + } + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalConfiguration.java similarity index 95% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalConfiguration.java index ab0568f3c7b..f3b64ccb36e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalConfiguration.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalConfiguration.java @@ -24,7 +24,7 @@ public class ManagedAgentInternalConfiguration { }; } - public ManagedAgentInternalConfiguration(com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy credentials) { + public ManagedAgentInternalConfiguration(com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy credentials) { if (!credentials.isBound()) { throw new IllegalStateException("Embedded callbacks require qwen.managed-agent.session-store.binding-key"); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentPersistenceConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentPersistenceConfiguration.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentPersistenceConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentPersistenceConfiguration.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentProperties.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentProperties.java similarity index 58% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentProperties.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentProperties.java index 76a5dcf4bb1..d5f8b5edaab 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentProperties.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentProperties.java @@ -1,11 +1,13 @@ package com.alibaba.qwen.code.managedagent.config; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalRuntimeKinds; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.protocol.ExternalRuntimeKinds; import jakarta.annotation.PostConstruct; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +38,11 @@ import java.util.Set; import java.util.regex.Pattern; @ConfigurationProperties("qwen.managed-agent") -public class ManagedAgentProperties implements EnvironmentAware { +@Getter +@Setter +public class ManagedAgentProperties + implements EnvironmentAware, + com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings { private static final Logger LOG = LoggerFactory.getLogger( ManagedAgentProperties.class); @@ -80,123 +86,34 @@ public class ManagedAgentProperties implements EnvironmentAware { // The account the server runs as; preflight checks file access as it. private String serviceUser = ""; // Validation may run more than once; the file is read only the first time. + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) private boolean harnessCredentialsApplied; + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) private String harnessBrokerUrl = ""; // Null on a plain `new`; the sweep then falls back to magnitude bands. + @Getter(AccessLevel.NONE) private Environment environment; - public Harness getHarness() { - return harness; - } - public boolean admitsPublicChildAgents() { return harness.isWorkspaceChildAgentsEnabled() && harness.isWorkspaceShellEnabled() && runtimeBroker.isDurableLocalProcess() - && LocalProcessRuntimeProvisioner.KIND.equals(runtimeBroker.getProvisioner()) + && com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(runtimeBroker.getProvisioner()) && "session".equals(runtimeBroker.getBoundSessionIsolationClass()); } - public Auth getAuth() { - return auth; - } - - public InternalServer getInternalServer() { - return internalServer; - } - - public Automation getAutomation() { - return automation; - } - - public Channels getChannels() { - return channels; - } - - public EventTransport getEventTransport() { - return eventTransport; - } - - public SessionStore getSessionStore() { - return sessionStore; - } - - public ToolPublication getToolPublication() { - return toolPublication; - } - - public Artifacts getArtifacts() { - return artifacts; - } - - public ManagedExtensions getManagedExtensions() { return managedExtensions; } - - public Extensions getExtensions() { - return extensions; - } - - public Dispatch getDispatch() { - return dispatch; - } - - public Events getEvents() { - return events; - } - - public RuntimeBroker getRuntimeBroker() { - return runtimeBroker; - } - - public String getAgentRevision() { - return agentRevision; - } - - public void setAgentRevision(String agentRevision) { - this.agentRevision = agentRevision; - } - public List getDefinitionToolProfiles() { - return definitionToolProfiles; - } - public void setDefinitionToolProfiles( - List definitionToolProfiles) { - this.definitionToolProfiles = definitionToolProfiles; - } - - public List getDefinitionModels() { - return definitionModels; - } - - public void setDefinitionModels(List definitionModels) { - this.definitionModels = definitionModels; - } - - public List getModelCatalog() { - return modelCatalog; - } - public void setModelCatalog(List modelCatalog) { this.modelCatalog = List.copyOf(modelCatalog); } - public static class ModelOption { + @Getter + @Setter + public static class ModelOption + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings + .ModelOption { private String modelId; private String label; - - public String getModelId() { return modelId; } - public void setModelId(String modelId) { this.modelId = modelId; } - public String getLabel() { return label; } - public void setLabel(String label) { this.label = label; } - } - - public String getTrustedActorHeader() { - return trustedActorHeader; - } - - public void setTrustedActorHeader(String trustedActorHeader) { - this.trustedActorHeader = trustedActorHeader; - } - - public String getServiceUser() { - return serviceUser; } public void setServiceUser(String serviceUser) { @@ -368,7 +285,7 @@ public class ManagedAgentProperties implements EnvironmentAware { if (StringUtils.isNotBlank(runtimeBroker.getWorkspaceId())) { sessionStore.setWorkspaceId(runtimeBroker.getWorkspaceId()); } else if (StringUtils.isNotBlank(runtimeBroker.getWorkspaceCwd()) - && (LocalProcessRuntimeProvisioner.KIND.equals(provisioner) + && (com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(provisioner) || "kubernetes".equals(provisioner))) { sessionStore.setWorkspaceId(EmbeddedRuntimeBroker.workspaceId(runtimeBroker)); } @@ -438,7 +355,7 @@ public class ManagedAgentProperties implements EnvironmentAware { && (!harness.isEnabled() || !sessionStore.isEnabled() || !runtimeBroker.isEnabled() - || !residentFiles && (!LocalProcessRuntimeProvisioner.KIND.equals(runtimeBroker.getProvisioner()) + || !residentFiles && (!com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(runtimeBroker.getProvisioner()) || !WorkspaceExecutionProfile.admitsIsolationClass(runtimeBroker.getIsolationClass()) || (runtimeBroker.getWorkspaceMounts().isEmpty() && runtimeBroker.getWorkspaceCreationRoot().isBlank() @@ -458,7 +375,7 @@ public class ManagedAgentProperties implements EnvironmentAware { throw new IllegalStateException("Bound-Session isolation must be session or workspace"); } if ("workspace".equals(boundSessionIsolation) - && (!LocalProcessRuntimeProvisioner.KIND.equals(runtimeBroker.getProvisioner()) + && (!com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(runtimeBroker.getProvisioner()) || !runtimeBroker.isDurableLocalProcess() || runtimeBroker.isChildWorkspacesEnabled())) { throw new IllegalStateException("Workspace-isolated bound Sessions require a durable" @@ -523,7 +440,7 @@ public class ManagedAgentProperties implements EnvironmentAware { // A merge runs once the child Session is closed (#13753 I2), and // only a durable local-process Broker can close one. if (!runtimeBroker.isEnabled() - || !LocalProcessRuntimeProvisioner.KIND.equals(runtimeBroker.getProvisioner()) + || !com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(runtimeBroker.getProvisioner()) || !"session".equals(runtimeBroker.getIsolationClass()) || !runtimeBroker.isDurableLocalProcess() || (runtimeBroker.getWorkspaceMounts().isEmpty() @@ -757,13 +674,12 @@ public class ManagedAgentProperties implements EnvironmentAware { } } - public static class Harness { + @Getter + @Setter + public static class Harness + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.Harness { private final HarnessPool pool = new HarnessPool(); - public HarnessPool getPool() { - return pool; - } - private boolean enabled; private boolean workspaceFilesEnabled; private boolean workspaceShellEnabled; @@ -778,14 +694,6 @@ public class ManagedAgentProperties implements EnvironmentAware { @DurationUnit(ChronoUnit.SECONDS) private Duration approvalTimeout = Duration.ofMinutes(10); - public Duration getApprovalTimeout() { - return approvalTimeout; - } - - public void setApprovalTimeout(Duration value) { - approvalTimeout = value; - } - @DurationUnit(ChronoUnit.SECONDS) private Duration connectTimeout = Duration.ofSeconds(5); @DurationUnit(ChronoUnit.SECONDS) @@ -802,117 +710,13 @@ public class ManagedAgentProperties implements EnvironmentAware { */ @DurationUnit(ChronoUnit.SECONDS) private Duration turnDeadline = Duration.ofMinutes(30); - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public boolean isWorkspaceFilesEnabled() { - return workspaceFilesEnabled; - } - - public void setWorkspaceFilesEnabled(boolean workspaceFilesEnabled) { - this.workspaceFilesEnabled = workspaceFilesEnabled; - } - - public boolean isWorkspaceChildAgentsEnabled() { return workspaceChildAgentsEnabled; } - - public void setWorkspaceChildAgentsEnabled(boolean value) { workspaceChildAgentsEnabled = value; } - - public boolean isWorkspaceShellEnabled() { - return workspaceShellEnabled; - } - - public void setWorkspaceShellEnabled(boolean workspaceShellEnabled) { - this.workspaceShellEnabled = workspaceShellEnabled; - } - - public String getBaseUrl() { - return baseUrl; - } - - public void setBaseUrl(String baseUrl) { - this.baseUrl = baseUrl; - } - - public String getToken() { - return token; - } - - public void setToken(String token) { - this.token = token; - } - - public String getCapabilityDigest() { - return capabilityDigest; - } - - public void setCapabilityDigest(String capabilityDigest) { - this.capabilityDigest = capabilityDigest; - } - - public String getCredentialsFile() { - return credentialsFile; - } - - public void setCredentialsFile(String credentialsFile) { - this.credentialsFile = credentialsFile; - } - - public String getApprovalMode() { - return approvalMode; - } - - public Duration getLoadTimeout() { - return loadTimeout; - } - - public void setLoadTimeout(Duration value) { - loadTimeout = value; - } - - public void setApprovalMode(String approvalMode) { - this.approvalMode = approvalMode; - } - - public Duration getConnectTimeout() { - return connectTimeout; - } - - public void setConnectTimeout(Duration connectTimeout) { - this.connectTimeout = connectTimeout; - } - - public Duration getRequestTimeout() { - return requestTimeout; - } - - public void setRequestTimeout(Duration requestTimeout) { - this.requestTimeout = requestTimeout; - } - - public Duration getHeartbeatInterval() { - return heartbeatInterval; - } - - public void setHeartbeatInterval(Duration heartbeatInterval) { - this.heartbeatInterval = heartbeatInterval; - } - - public Duration getTurnDeadline() { - return turnDeadline; - } - - public void setTurnDeadline(Duration turnDeadline) { - this.turnDeadline = turnDeadline; - } } - public static class HarnessPool { + @Getter + @Setter + public static class HarnessPool + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings + .HarnessPool { private boolean enabled; private String discovery = "static"; private List instances = new ArrayList<>(); @@ -927,141 +731,22 @@ public class ManagedAgentProperties implements EnvironmentAware { private String serviceName = ""; private String serviceUid = ""; private String portName = "https"; - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean value) { - enabled = value; - } - - public String getDiscovery() { - return discovery; - } - - public void setDiscovery(String value) { - discovery = value; - } - - public List getInstances() { - return instances; - } - - public void setInstances(List value) { - instances = value; - } - - public String getKeyStoreFile() { - return keyStoreFile; - } - - public void setKeyStoreFile(String value) { - keyStoreFile = value; - } - - public String getKeyStorePasswordFile() { - return keyStorePasswordFile; - } - - public void setKeyStorePasswordFile(String value) { - keyStorePasswordFile = value; - } - - public String getTrustStoreFile() { - return trustStoreFile; - } - - public void setTrustStoreFile(String value) { - trustStoreFile = value; - } - - public String getTrustStorePasswordFile() { - return trustStorePasswordFile; - } - - public void setTrustStorePasswordFile(String value) { - trustStorePasswordFile = value; - } - - public String getKubernetesApiUrl() { - return kubernetesApiUrl; - } - - public void setKubernetesApiUrl(String value) { - kubernetesApiUrl = value; - } - - public String getKubernetesTokenFile() { - return kubernetesTokenFile; - } - - public void setKubernetesTokenFile(String value) { - kubernetesTokenFile = value; - } - - public String getKubernetesCaFile() { - return kubernetesCaFile; - } - - public void setKubernetesCaFile(String value) { - kubernetesCaFile = value; - } - - public String getNamespace() { - return namespace; - } - - public void setNamespace(String value) { - namespace = value; - } - - public String getServiceName() { - return serviceName; - } - - public void setServiceName(String value) { - serviceName = value; - } - - public String getServiceUid() { - return serviceUid; - } - - public void setServiceUid(String value) { - serviceUid = value; - } - - public String getPortName() { - return portName; - } - - public void setPortName(String value) { - portName = value; - } } - public static class HarnessInstance { + @Getter + @Setter + public static class HarnessInstance + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings + .HarnessInstance { private String instanceId = ""; private String baseUrl = ""; - public String getInstanceId() { - return instanceId; - } - - public void setInstanceId(String value) { - instanceId = value; - } - - public String getBaseUrl() { - return baseUrl; - } - - public void setBaseUrl(String value) { - baseUrl = value; - } } - public static class SessionStore { + @Getter + @Setter + public static class SessionStore + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings + .SessionStore { private boolean enabled; private String baseUrl = ""; private String workspaceId = ""; @@ -1069,56 +754,10 @@ public class ManagedAgentProperties implements EnvironmentAware { private Duration writerLeaseDuration = Duration.ofSeconds(60); private String bindingKey = ""; private boolean allowInsecureHttp; - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public String getBaseUrl() { - return baseUrl; - } - - public void setBaseUrl(String baseUrl) { - this.baseUrl = baseUrl; - } - - public String getWorkspaceId() { - return workspaceId; - } - - public void setWorkspaceId(String workspaceId) { - this.workspaceId = workspaceId; - } - - public Duration getWriterLeaseDuration() { - return writerLeaseDuration; - } - - public void setWriterLeaseDuration(Duration writerLeaseDuration) { - this.writerLeaseDuration = writerLeaseDuration; - } - - public String getBindingKey() { - return bindingKey; - } - - public void setBindingKey(String bindingKey) { - this.bindingKey = bindingKey; - } - - public boolean isAllowInsecureHttp() { - return allowInsecureHttp; - } - - public void setAllowInsecureHttp(boolean allowInsecureHttp) { - this.allowInsecureHttp = allowInsecureHttp; - } } + @Getter + @Setter public static class Auth { private String mode = "auto"; private String signingKey = ""; @@ -1131,73 +770,27 @@ public class ManagedAgentProperties implements EnvironmentAware { private Duration allowedDrift = Duration.ofMinutes(5); private boolean allowInsecureBind; private long maxSignedBodyBytes = 10 * 1024 * 1024; - - public String getMode() { - return mode; - } - - public void setMode(String mode) { - this.mode = mode; - } - - public String getSigningKey() { - return signingKey; - } - - public void setSigningKey(String signingKey) { - this.signingKey = signingKey; - } - - public long getMaxSignedBodyBytes() { - return maxSignedBodyBytes; - } - - public void setMaxSignedBodyBytes(long maxSignedBodyBytes) { - this.maxSignedBodyBytes = maxSignedBodyBytes; - } - - public Duration getAllowedDrift() { - return allowedDrift; - } - - public void setAllowedDrift(Duration allowedDrift) { - this.allowedDrift = allowedDrift; - } - - public boolean isAllowInsecureBind() { - return allowInsecureBind; - } - - public void setAllowInsecureBind(boolean allowInsecureBind) { - this.allowInsecureBind = allowInsecureBind; - } } + @Getter + @Setter public static class InternalServer { private int port; private String address = "127.0.0.1"; - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - // A blank value comes from an unset template variable; both the // startup guard and the connector must read it as the default. public String getAddress() { return StringUtils.isBlank(address) ? "127.0.0.1" : address; } - - public void setAddress(String address) { - this.address = address; - } } - public static class ToolPublication { + @Getter + @Setter + public static class ToolPublication + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings + .ToolPublication { private boolean enabled; private String ossEndpoint = ""; private String ossRegion = ""; @@ -1220,99 +813,49 @@ public class ManagedAgentProperties implements EnvironmentAware { private int verificationConcurrency = 2; @DurationUnit(ChronoUnit.SECONDS) private Duration deletionGrace = Duration.ofHours(24); - public boolean isAsyncVerificationEnabled() { return asyncVerificationEnabled; } - public void setAsyncVerificationEnabled(boolean value) { asyncVerificationEnabled = value; } - public int getVerificationConcurrency() { return verificationConcurrency; } - public void setVerificationConcurrency(int value) { verificationConcurrency = value; } - public boolean isGcEnabled() { return gcEnabled; } - public void setGcEnabled(boolean value) { gcEnabled = value; } - public Duration getDeletionGrace() { return deletionGrace; } - public void setDeletionGrace(Duration value) { deletionGrace = value; } - - public boolean isEnabled() { return enabled; } - public void setEnabled(boolean enabled) { this.enabled = enabled; } - public String getOssEndpoint() { return ossEndpoint; } - public void setOssEndpoint(String value) { ossEndpoint = value; } - public String getOssRegion() { return ossRegion; } - public void setOssRegion(String value) { ossRegion = value; } - public String getOssBucket() { return ossBucket; } - public void setOssBucket(String value) { ossBucket = value; } - public String getServiceBaseUrl() { return serviceBaseUrl; } - public void setServiceBaseUrl(String value) { serviceBaseUrl = value; } - public Long getExecutionBytes() { return executionBytes; } - public void setExecutionBytes(Long value) { executionBytes = value; } - public Long getSessionBytes() { return sessionBytes; } - public void setSessionBytes(Long value) { sessionBytes = value; } - public Long getTenantBytes() { return tenantBytes; } - public void setTenantBytes(Long value) { tenantBytes = value; } - public Long getActiveCaptures() { return activeCaptures; } - public void setActiveCaptures(Long value) { activeCaptures = value; } - public Integer getEntryConcurrency() { return entryConcurrency; } - public void setEntryConcurrency(Integer value) { entryConcurrency = value; } - public Duration getOperationTimeout() { return operationTimeout; } - public void setOperationTimeout(Duration value) { operationTimeout = value; } - public Duration getClaimTimeout() { return claimTimeout; } - public void setClaimTimeout(Duration value) { claimTimeout = value; } - public Long getVerificationBytesPerSecond() { return verificationBytesPerSecond; } - public void setVerificationBytesPerSecond(Long value) { verificationBytesPerSecond = value; } - public Duration getMaxVerificationTimeout() { return maxVerificationTimeout; } - public void setMaxVerificationTimeout(Duration value) { maxVerificationTimeout = value; } } - public static class ManagedExtensions { + @Getter + @Setter + public static class ManagedExtensions + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings + .ManagedExtensions { private boolean enabled; private String mode = "linux-cgroup"; private String cacheDirectory = ""; private String cgroupRoot = ""; - public boolean isEnabled() { return enabled; } - public void setEnabled(boolean value) { enabled = value; } - public String getMode() { return mode; } - public void setMode(String value) { mode = value; } - public String getCacheDirectory() { return cacheDirectory; } - public void setCacheDirectory(String value) { cacheDirectory = value; } - public String getCgroupRoot() { return cgroupRoot; } - public void setCgroupRoot(String value) { cgroupRoot = value; } } - public static class Extensions { + @Getter + @Setter + public static class Extensions + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.Extensions { private boolean enabled; private String artifactDirectory = ""; private List allowedActors = List.of(); - public boolean isEnabled() { return enabled; } - public void setEnabled(boolean enabled) { this.enabled = enabled; } - public String getArtifactDirectory() { return artifactDirectory; } - public void setArtifactDirectory(String value) { artifactDirectory = value; } - public List getAllowedActors() { return allowedActors; } public void setAllowedActors(List value) { allowedActors = List.copyOf(value); } } - public static class Artifacts { + @Getter + @Setter + public static class Artifacts + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.Artifacts { private boolean enabled; private boolean publishOriginal; private boolean publishPreview; private int maxConcurrentReads = 4; @DurationUnit(ChronoUnit.SECONDS) private Duration readTimeout = Duration.ofMinutes(2); + /** How often a download re-verifies content access; PT0S checks on every chunk. */ @DurationUnit(ChronoUnit.SECONDS) private Duration readRevalidationInterval = Duration.ofSeconds(5); - - public boolean isEnabled() { return enabled; } - public void setEnabled(boolean value) { enabled = value; } - public boolean isPublishOriginal() { return publishOriginal; } - public void setPublishOriginal(boolean value) { publishOriginal = value; } - public boolean isPublishPreview() { return publishPreview; } - public void setPublishPreview(boolean value) { publishPreview = value; } - public int getMaxConcurrentReads() { return maxConcurrentReads; } - public void setMaxConcurrentReads(int value) { maxConcurrentReads = value; } - public Duration getReadTimeout() { return readTimeout; } - public void setReadTimeout(Duration value) { readTimeout = value; } - /** How often a download re-verifies content access; PT0S checks on every chunk. */ - public Duration getReadRevalidationInterval() { return readRevalidationInterval; } - public void setReadRevalidationInterval(Duration value) { readRevalidationInterval = value; } } - public static class Dispatch { + @Getter + @Setter + public static class Dispatch + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.Dispatch { // No typed scan-delay field. The deployed default comes from // application.yml (`dispatch.scan-delay: '1s'`); the same // "${...scan-delay:1s}" fallback is repeated by four @Scheduled @@ -1332,71 +875,22 @@ public class ManagedAgentProperties implements EnvironmentAware { private int maxPreAdmissionRetries = 5; private int maxPostAdmissionRetries = 10; private int maxOperationRetries = 10; - - public Duration getLeaseDuration() { - return leaseDuration; - } - - public void setLeaseDuration(Duration leaseDuration) { - this.leaseDuration = leaseDuration; - } - - public Duration getLeaseRenewInterval() { - return leaseRenewInterval; - } - - public void setLeaseRenewInterval(Duration leaseRenewInterval) { - this.leaseRenewInterval = leaseRenewInterval; - } - - public Duration getRetryInitialDelay() { - return retryInitialDelay; - } - - public void setRetryInitialDelay(Duration retryInitialDelay) { - this.retryInitialDelay = retryInitialDelay; - } - - public Duration getRetryMaxDelay() { - return retryMaxDelay; - } - - public void setRetryMaxDelay(Duration retryMaxDelay) { - this.retryMaxDelay = retryMaxDelay; - } - - public int getMaxPreAdmissionRetries() { - return maxPreAdmissionRetries; - } - - public void setMaxPreAdmissionRetries(int maxPreAdmissionRetries) { - this.maxPreAdmissionRetries = maxPreAdmissionRetries; - } - - public int getMaxPostAdmissionRetries() { - return maxPostAdmissionRetries; - } - - public void setMaxPostAdmissionRetries(int maxPostAdmissionRetries) { - this.maxPostAdmissionRetries = maxPostAdmissionRetries; - } - - public int getMaxOperationRetries() { - return maxOperationRetries; - } - - public void setMaxOperationRetries(int maxOperationRetries) { - this.maxOperationRetries = maxOperationRetries; - } } - public static class Events { + @Getter + @Setter + public static class Events + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.Events { @DurationUnit(ChronoUnit.SECONDS) private Duration pollInterval = Duration.ofSeconds(5); @DurationUnit(ChronoUnit.SECONDS) private Duration heartbeatInterval = Duration.ofSeconds(15); @DurationUnit(ChronoUnit.SECONDS) private Duration streamTimeout = Duration.ofMinutes(30); + /** + * How often a stream re-verifies the subscriber's read grant; PT0S + * checks before every event. + */ @DurationUnit(ChronoUnit.SECONDS) private Duration readGrantRecheckInterval = Duration.ofSeconds(5); @DurationUnit(ChronoUnit.MILLIS) @@ -1405,104 +899,28 @@ public class ManagedAgentProperties implements EnvironmentAware { private Duration materializeInterval = Duration.ofMillis(100); private int batchMaxEvents = 64; private int batchMaxBytes = 65536; - private boolean replayFloorEnabled; - - public Duration getPollInterval() { - return pollInterval; - } - - public void setPollInterval(Duration pollInterval) { - this.pollInterval = pollInterval; - } - - /** - * How often a stream re-verifies the subscriber's read grant; PT0S - * checks before every event. - */ - public Duration getReadGrantRecheckInterval() { - return readGrantRecheckInterval; - } - - public void setReadGrantRecheckInterval( - Duration readGrantRecheckInterval) { - this.readGrantRecheckInterval = readGrantRecheckInterval; - } - - public Duration getHeartbeatInterval() { - return heartbeatInterval; - } - - public void setHeartbeatInterval(Duration heartbeatInterval) { - this.heartbeatInterval = heartbeatInterval; - } - - public Duration getStreamTimeout() { - return streamTimeout; - } - - public void setStreamTimeout(Duration streamTimeout) { - this.streamTimeout = streamTimeout; - } - - public Duration getBatchInterval() { - return batchInterval; - } - - public void setBatchInterval(Duration batchInterval) { - this.batchInterval = batchInterval; - } - - public Duration getMaterializeInterval() { - return materializeInterval; - } - - public void setMaterializeInterval(Duration materializeInterval) { - this.materializeInterval = materializeInterval; - } - - public int getBatchMaxEvents() { - return batchMaxEvents; - } - - public void setBatchMaxEvents(int batchMaxEvents) { - this.batchMaxEvents = batchMaxEvents; - } - - public int getBatchMaxBytes() { - return batchMaxBytes; - } - - public void setBatchMaxBytes(int batchMaxBytes) { - this.batchMaxBytes = batchMaxBytes; - } - /** * Whether the scheduled pass raises each Session's replay floor as * far as its Snapshot proves safe. Disabled by default; events are * never deleted here either way. */ - public boolean isReplayFloorEnabled() { - return replayFloorEnabled; - } - - public void setReplayFloorEnabled(boolean replayFloorEnabled) { - this.replayFloorEnabled = replayFloorEnabled; - } + private boolean replayFloorEnabled; } - public static class RuntimeBroker { + @Getter + @Setter + public static class RuntimeBroker + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings + .RuntimeBroker { private final SshRuntimeSettings ssh = new SshRuntimeSettings(); - public SshRuntimeSettings getSsh() { - return ssh; - } private boolean enabled; private final Retention retention = new Retention(); private String host = "127.0.0.1"; private int port = 4182; private boolean allowNonLoopback; private String token = ""; - private String provisioner = LocalProcessRuntimeProvisioner.KIND; + private String provisioner = com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS; @DurationUnit(ChronoUnit.SECONDS) private Duration v3ResultWindow = Duration.ofMinutes(30); private String workspaceId = ""; @@ -1544,16 +962,22 @@ public class ManagedAgentProperties implements EnvironmentAware { private Map environment = new LinkedHashMap<>(); private boolean childWorkspacesEnabled; private boolean sessionWorktreesEnabled; + /** + * Whether hosted Sessions may launch read-only snapshot children + * (#13753 I3). A relay from before snapshots would create one as a + * shared writer, so this is turned on only after every control-plane + * replica runs this version. + */ private boolean childSnapshotsEnabled; private String childWorkspaceGit = "git"; @DurationUnit(ChronoUnit.MILLIS) private Duration childWorkspaceGitTimeout = Duration.ofMinutes(2); - public Retention getRetention() { - return retention; - } - - public static class Retention { + @Getter + @Setter + public static class Retention + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings + .RuntimeBroker.Retention { private boolean enabled; @DurationUnit(ChronoUnit.DAYS) private Duration maxAge = Duration.ofDays(30); @@ -1561,15 +985,6 @@ public class ManagedAgentProperties implements EnvironmentAware { @DurationUnit(ChronoUnit.MILLIS) private Duration scanDelay = Duration.ofMinutes(1); - public boolean isEnabled() { return enabled; } - public void setEnabled(boolean value) { enabled = value; } - public Duration getMaxAge() { return maxAge; } - public void setMaxAge(Duration value) { maxAge = value; } - public int getBatchSize() { return batchSize; } - public void setBatchSize(int value) { batchSize = value; } - public Duration getScanDelay() { return scanDelay; } - public void setScanDelay(Duration value) { scanDelay = value; } - void validate() { if (maxAge == null || maxAge.isZero() || maxAge.isNegative() || maxAge.compareTo(Duration.ofMillis(1)) < 0 @@ -1581,138 +996,21 @@ public class ManagedAgentProperties implements EnvironmentAware { } } - public boolean isEnabled() { - return enabled; - } + public record WorkspaceMount(String tenantId, String storageId, String root) + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings + .RuntimeBroker.WorkspaceMount {} - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public boolean isAllowNonLoopback() { - return allowNonLoopback; - } - - public void setAllowNonLoopback(boolean allowNonLoopback) { - this.allowNonLoopback = allowNonLoopback; - } - - public Duration getV3ResultWindow() { - return v3ResultWindow; - } - - public void setV3ResultWindow(Duration v3ResultWindow) { - this.v3ResultWindow = v3ResultWindow; - } - - public String getToken() { - return token; - } - - public void setToken(String token) { - this.token = token; - } - - public String getProvisioner() { - return provisioner; - } - - public void setProvisioner(String provisioner) { - this.provisioner = provisioner; - } - - public String getWorkspaceId() { - return workspaceId; - } - - public void setWorkspaceId(String workspaceId) { - this.workspaceId = workspaceId; - } - - public String getWorkspaceGeneration() { - return workspaceGeneration; - } - - public void setWorkspaceGeneration(String workspaceGeneration) { - this.workspaceGeneration = workspaceGeneration; - } - - public String getWorkspaceCwd() { - return workspaceCwd; - } - - public void setWorkspaceCwd(String workspaceCwd) { - this.workspaceCwd = workspaceCwd; - } - - public String getIsolationClass() { - return isolationClass; - } - - public String getBoundSessionIsolationClass() { - return boundSessionIsolationClass; - } - - public void setBoundSessionIsolationClass(String boundSessionIsolationClass) { - this.boundSessionIsolationClass = boundSessionIsolationClass; - } - - public boolean isSessionCreateWarm() { - return sessionCreateWarm; - } - - public void setSessionCreateWarm(boolean sessionCreateWarm) { - this.sessionCreateWarm = sessionCreateWarm; - } - - public List getWorkspaceMounts() { - return workspaceMounts; - } - - public void setWorkspaceMounts(List workspaceMounts) { - this.workspaceMounts = workspaceMounts; - } - - public String getWorkspaceCreationRoot() { - return workspaceCreationRoot; - } - - public void setWorkspaceCreationRoot(String workspaceCreationRoot) { - this.workspaceCreationRoot = workspaceCreationRoot; - } - - public boolean isLocalDirectoryImportEnabled() { - return localDirectoryImportEnabled; - } - - public void setLocalDirectoryImportEnabled(boolean localDirectoryImportEnabled) { - this.localDirectoryImportEnabled = localDirectoryImportEnabled; - } - - public record WorkspaceMount(String tenantId, String storageId, String root) { - } - - public record ResidentWorkspace(String workspaceId, long workspaceGeneration, - com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRegistration registration, - com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceCsiResourceGuard.ProtectionIdentity protection, - String image, List command, - List artifacts) { + public record ResidentWorkspace( + String workspaceId, + long workspaceGeneration, + com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration + registration, + com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity protection, + String image, + List command, + List artifacts) + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings + .RuntimeBroker.ResidentWorkspace { public ResidentWorkspace { if (registration == null || protection == null || workspaceId == null || workspaceId.isBlank() || workspaceGeneration < 1 || image == null || !image.matches("[^\\s]+@sha256:[0-9a-f]{64}") @@ -1721,7 +1019,7 @@ public class ManagedAgentProperties implements EnvironmentAware { || value.length() > 8192 || value.indexOf('\0') >= 0) || artifacts == null || artifacts.size() > 48 || artifacts.stream().anyMatch(java.util.Objects::isNull) - || artifacts.stream().map(com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceCsiRuntimeProvisioner.WorkerArtifact::configMapName) + || artifacts.stream().map(com.alibaba.qwen.code.managedagent.contracts.runtime.WorkerArtifact::configMapName) .distinct().count() != artifacts.size()) { throw new IllegalArgumentException("Invalid resident Workspace configuration"); } @@ -1733,238 +1031,14 @@ public class ManagedAgentProperties implements EnvironmentAware { } } - public List getResidentWorkspaces() { return residentWorkspaces; } public void setResidentWorkspaces(List value) { residentWorkspaces = List.copyOf(value); } - public String getResidentKubernetesApiUrl() { return residentKubernetesApiUrl; } - public void setResidentKubernetesApiUrl(String value) { residentKubernetesApiUrl = value; } - public String getResidentKubernetesTokenFile() { return residentKubernetesTokenFile; } - public void setResidentKubernetesTokenFile(String value) { residentKubernetesTokenFile = value; } - public String getResidentKubernetesCaFile() { return residentKubernetesCaFile; } - public void setResidentKubernetesCaFile(String value) { residentKubernetesCaFile = value; } - - public void setIsolationClass(String isolationClass) { - this.isolationClass = isolationClass; - } - - public boolean isDurableLocalProcess() { - return durableLocalProcess; - } - - public void setDurableLocalProcess(boolean durableLocalProcess) { - this.durableLocalProcess = durableLocalProcess; - } - - public boolean isTrustedLocalRebootRecovery() { - return trustedLocalRebootRecovery; - } - - public void setTrustedLocalRebootRecovery(boolean trustedLocalRebootRecovery) { - this.trustedLocalRebootRecovery = trustedLocalRebootRecovery; - } - - public boolean isOperatorRecoveryEnabled() { - return operatorRecoveryEnabled; - } - - public void setOperatorRecoveryEnabled(boolean operatorRecoveryEnabled) { - this.operatorRecoveryEnabled = operatorRecoveryEnabled; - } - - public boolean isVerifiedWorkspaceRecoveryEnabled() { - return verifiedWorkspaceRecoveryEnabled; - } - - public boolean isChildWorkspacesEnabled() { - return childWorkspacesEnabled; - } - - public boolean isSessionWorktreesEnabled() { - return sessionWorktreesEnabled; - } - - public void setSessionWorktreesEnabled(boolean sessionWorktreesEnabled) { - this.sessionWorktreesEnabled = sessionWorktreesEnabled; - } - - public void setChildWorkspacesEnabled(boolean childWorkspacesEnabled) { - this.childWorkspacesEnabled = childWorkspacesEnabled; - } - - /** - * Whether hosted Sessions may launch read-only snapshot children - * (#13753 I3). A relay from before snapshots would create one as a - * shared writer, so this is turned on only after every control-plane - * replica runs this version. - */ - public boolean isChildSnapshotsEnabled() { - return childSnapshotsEnabled; - } - - public void setChildSnapshotsEnabled(boolean childSnapshotsEnabled) { - this.childSnapshotsEnabled = childSnapshotsEnabled; - } - - public String getChildWorkspaceGit() { - return childWorkspaceGit; - } - - public void setChildWorkspaceGit(String childWorkspaceGit) { - this.childWorkspaceGit = childWorkspaceGit; - } - - public Duration getChildWorkspaceGitTimeout() { - return childWorkspaceGitTimeout; - } - - public void setChildWorkspaceGitTimeout(Duration childWorkspaceGitTimeout) { - this.childWorkspaceGitTimeout = childWorkspaceGitTimeout; - } - - public void setVerifiedWorkspaceRecoveryEnabled(boolean verifiedWorkspaceRecoveryEnabled) { - this.verifiedWorkspaceRecoveryEnabled = verifiedWorkspaceRecoveryEnabled; - } - - public String getStateDirectory() { - return stateDirectory; - } - - public void setStateDirectory(String stateDirectory) { - this.stateDirectory = stateDirectory; - } - - public String getCredentialKeyId() { - return credentialKeyId; - } - - public void setCredentialKeyId(String credentialKeyId) { - this.credentialKeyId = credentialKeyId; - } - - public String getCredentialKey() { - return credentialKey; - } - - public void setCredentialKey(String credentialKey) { - this.credentialKey = credentialKey; - } - - public Map getPreviousCredentialKeys() { - return previousCredentialKeys; - } - - public void setPreviousCredentialKeys( - Map previousCredentialKeys) { - this.previousCredentialKeys = previousCredentialKeys; - } - - public String getNodeExecutable() { - return nodeExecutable; - } - - public void setNodeExecutable(String nodeExecutable) { - this.nodeExecutable = nodeExecutable; - } - - public String getWorkerEntry() { - return workerEntry; - } - - public void setWorkerEntry(String workerEntry) { - this.workerEntry = workerEntry; - } - - public String getStaticEndpoint() { - return staticEndpoint; - } - - public void setStaticEndpoint(String staticEndpoint) { - this.staticEndpoint = staticEndpoint; - } - - public String getStaticToken() { - return staticToken; - } - - public void setStaticToken(String staticToken) { - this.staticToken = staticToken; - } - - public String getExternalProviderUrl() { - return externalProviderUrl; - } - - public void setExternalProviderUrl(String externalProviderUrl) { - this.externalProviderUrl = externalProviderUrl; - } - - public String getExternalProviderToken() { - return externalProviderToken; - } - - public void setExternalProviderToken(String externalProviderToken) { - this.externalProviderToken = externalProviderToken; - } - - public String getExternalProviderId() { - return externalProviderId; - } - - public void setExternalProviderId(String externalProviderId) { - this.externalProviderId = externalProviderId; - } - - public Duration getExternalRequestTimeout() { - return externalRequestTimeout; - } - - public void setExternalRequestTimeout(Duration externalRequestTimeout) { - this.externalRequestTimeout = externalRequestTimeout; - } - - public Duration getExternalStartupTimeout() { - return externalStartupTimeout; - } - - public void setExternalStartupTimeout(Duration externalStartupTimeout) { - this.externalStartupTimeout = externalStartupTimeout; - } - - public String getStaticRuntimeInstanceId() { - return staticRuntimeInstanceId; - } - - public void setStaticRuntimeInstanceId( - String staticRuntimeInstanceId) { - this.staticRuntimeInstanceId = staticRuntimeInstanceId; - } - - public String getStaticLeaseId() { - return staticLeaseId; - } - - public void setStaticLeaseId(String staticLeaseId) { - this.staticLeaseId = staticLeaseId; - } - - public long getStaticEpoch() { - return staticEpoch; - } - - public void setStaticEpoch(long staticEpoch) { - this.staticEpoch = staticEpoch; - } - - public Map getEnvironment() { - return environment; - } - - public void setEnvironment(Map environment) { - this.environment = environment; - } } /** H6b/H6c: the automation scanner, its lease and the slot window. */ - public static class Automation { + @Getter + @Setter + public static class Automation + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.Automation { private boolean enabled; @DurationUnit(ChronoUnit.MILLIS) private Duration scanDelay = Duration.ofSeconds(10); @@ -1977,74 +1051,13 @@ public class ManagedAgentProperties implements EnvironmentAware { private int maxSlotsPerTick = 1000; private int concurrency = 4; private int scannerThreads = 4; - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public Duration getScanDelay() { - return scanDelay; - } - - public void setScanDelay(Duration scanDelay) { - this.scanDelay = scanDelay; - } - - public Duration getLease() { - return lease; - } - - public void setLease(Duration lease) { - this.lease = lease; - } - - public Duration getLateTolerance() { - return lateTolerance; - } - - public void setLateTolerance(Duration lateTolerance) { - this.lateTolerance = lateTolerance; - } - - public Duration getLookback() { - return lookback; - } - - public void setLookback(Duration lookback) { - this.lookback = lookback; - } - - public int getMaxSlotsPerTick() { - return maxSlotsPerTick; - } - - public void setMaxSlotsPerTick(int maxSlotsPerTick) { - this.maxSlotsPerTick = maxSlotsPerTick; - } - - public int getConcurrency() { - return concurrency; - } - - public void setConcurrency(int concurrency) { - this.concurrency = concurrency; - } - - public int getScannerThreads() { - return scannerThreads; - } - - public void setScannerThreads(int scannerThreads) { - this.scannerThreads = scannerThreads; - } } /** H5b/H5c: the trusted channel adapter surface and its claim lease. */ - public static class Channels { + @Getter + @Setter + public static class Channels + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.Channels { // No typed scan-delay field. The deployed default comes from // application.yml (`channels.scan-delay: '30s'`); the same // "${...channels.scan-delay:30s}" fallback drives @@ -2053,36 +1066,21 @@ public class ManagedAgentProperties implements EnvironmentAware { private boolean enabled; @DurationUnit(ChronoUnit.SECONDS) private Duration claimLease = Duration.ofMinutes(10); - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public Duration getClaimLease() { - return claimLease; - } - - public void setClaimLease(Duration claimLease) { - this.claimLease = claimLease; - } } /** - * Broker-backed EventTransport (design - * 2026-10-04-managed-agent-event-transport): the authoritative journal - * wire (phase MQ2b), and the public-event wire and the wake and - * materialization consumers (phase MQ2c). Default off, so a flag-off - * deployment creates no transport, producer or consumer bean and stays - * byte-identical. The topic must be a LITE topic, and the - * capability-probe, wake-bootstrap and owner groups must be pre-created - * as LITE groups bound to it — see EventTransportConfiguration for the - * deployment prerequisites. + * Broker-backed EventTransport (design 2026-10-04-managed-agent-event-transport): the + * authoritative journal wire (phase MQ2b), and the public-event wire and the wake and + * materialization consumers (phase MQ2c). Default off, so a flag-off deployment creates no + * transport, producer or consumer bean and stays byte-identical. The topic must be a LITE + * topic, and the capability-probe, wake-bootstrap and owner groups must be pre-created as LITE + * groups bound to it — see EventTransportConfiguration for the deployment prerequisites. */ - public static class EventTransport { + @Getter + @Setter + public static class EventTransport + implements com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings + .EventTransport { private boolean enabled; // The RocketMQ proxy gRPC endpoint (e.g. "host:8081"). private String endpoints = ""; @@ -2105,70 +1103,5 @@ public class ManagedAgentProperties implements EnvironmentAware { // Unit-less operator overrides must bind in the annotated unit. @DurationUnit(ChronoUnit.SECONDS) private Duration ownerReconcileInterval = Duration.ofSeconds(1); - - public String getCapabilityProbeGroup() { - return capabilityProbeGroup; - } - - public void setCapabilityProbeGroup(String capabilityProbeGroup) { - this.capabilityProbeGroup = capabilityProbeGroup; - } - - public String getWakeBootstrapLiteTopic() { - return wakeBootstrapLiteTopic; - } - - public void setWakeBootstrapLiteTopic(String wakeBootstrapLiteTopic) { - this.wakeBootstrapLiteTopic = wakeBootstrapLiteTopic; - } - - public String getWakeBootstrapGroup() { - return wakeBootstrapGroup; - } - - public void setWakeBootstrapGroup(String wakeBootstrapGroup) { - this.wakeBootstrapGroup = wakeBootstrapGroup; - } - - public String getOwnerGroup() { - return ownerGroup; - } - - public void setOwnerGroup(String ownerGroup) { - this.ownerGroup = ownerGroup; - } - - public Duration getOwnerReconcileInterval() { - return ownerReconcileInterval; - } - - public void setOwnerReconcileInterval( - Duration ownerReconcileInterval) { - this.ownerReconcileInterval = ownerReconcileInterval; - } - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public String getEndpoints() { - return endpoints; - } - - public void setEndpoints(String endpoints) { - this.endpoints = endpoints; - } - - public String getTopic() { - return topic; - } - - public void setTopic(String topic) { - this.topic = topic; - } } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentWebConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentWebConfiguration.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentWebConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentWebConfiguration.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedArtifactConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedArtifactConfiguration.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedArtifactConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedArtifactConfiguration.java index f172c31500d..bc7be2cbcc6 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedArtifactConfiguration.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedArtifactConfiguration.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.config; -import com.alibaba.qwen.code.managedagent.artifact.ManagedArtifactPolicy; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ManagedArtifactPolicy; import com.alibaba.qwen.code.managedagent.event.MessageMaterializer; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Qualifier; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedLocalConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedLocalConfiguration.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedLocalConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedLocalConfiguration.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSchemaConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSchemaConfiguration.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSchemaConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSchemaConfiguration.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfiguration.java similarity index 94% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfiguration.java index 303463ea73b..ccc271c09d4 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfiguration.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfiguration.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.config; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionReaderVersion.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionReaderVersion.java similarity index 69% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionReaderVersion.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionReaderVersion.java index 7dfac3b1ebb..2cb52ca4110 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionReaderVersion.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionReaderVersion.java @@ -1,19 +1,17 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.config; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.security.PublicSurface; -import org.springframework.http.HttpStatus; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; /** The ceiling of a remote Store reader; in-process readers support both formats. */ -final class ManagedSessionReaderVersion { +@org.springframework.stereotype.Component +public final class ManagedSessionReaderVersion implements com.alibaba.qwen.code.managedagent.contracts.persistence.StorageReadPolicy { static final String HEADER = "X-Qwen-Managed-Max-Readable-Storage-Version"; - static final int SUPPORTED = 2; + static final int SUPPORTED = com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedStorageVersions.SUPPORTED; - private ManagedSessionReaderVersion() {} - - static void requireReadable(int floor) { + public void requireReadable(int floor) { int ceiling = SUPPORTED; if (RequestContextHolder.getRequestAttributes() instanceof ServletRequestAttributes attributes) { var request = attributes.getRequest(); @@ -22,14 +20,14 @@ final class ManagedSessionReaderVersion { || path.startsWith("/internal/managed-tool-publications/")) { String header = request.getHeader(HEADER); if (header != null && !header.matches("[12]")) { - throw new ManagedAgentException(HttpStatus.BAD_REQUEST, "invalid_managed_session_reader_version", + throw new ManagedAgentException(400, "invalid_managed_session_reader_version", "The Managed Session reader version is invalid."); } ceiling = header == null ? 1 : Integer.parseInt(header); } } if (floor > ceiling) { - throw new ManagedAgentException(HttpStatus.CONFLICT, "managed_session_reader_version_unsupported", + throw new ManagedAgentException(409, "managed_session_reader_version_unsupported", "This reader cannot read the Managed Session storage version."); } } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfiguration.java similarity index 90% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfiguration.java index 580e58b19b7..06aa38cce74 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfiguration.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfiguration.java @@ -1,18 +1,18 @@ package com.alibaba.qwen.code.managedagent.config; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.security.SecretProtector; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Qualifier; @@ -37,7 +37,7 @@ public class RuntimeBrokerConfiguration { || !(sessions instanceof JdbcRuntimeSessionRepository jdbcSessions)) { throw new IllegalStateException("Original JDBC repositories are required for CSI retirement"); } - return new com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationStore(jdbc, manager, nativeStore, jdbcBindings, jdbcSessions); + return new com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationStore(jdbc, manager, nativeStore, jdbcBindings, jdbcSessions, new com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport()); } @Bean @@ -113,7 +113,7 @@ public class RuntimeBrokerConfiguration { @Bean(destroyMethod = "") @ConditionalOnProperty(prefix = "qwen.managed-agent.runtime-broker", name = "enabled", havingValue = "true") - public com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService runtimeBrokerService( + public com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService runtimeBrokerService( EmbeddedRuntimeBroker broker) { return broker.service(); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfiguration.java similarity index 87% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfiguration.java index 860259459f7..8b4d3676bfb 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfiguration.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfiguration.java @@ -2,13 +2,13 @@ package com.alibaba.qwen.code.managedagent.config; import com.alibaba.qwen.code.managedagent.persistence.ManagedRuntimeRetentionGuard; import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeRetentionJob; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeRetention; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeRetention; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.task.ThreadPoolTaskSchedulerBuilder; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/SshRuntimeSettings.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/SshRuntimeSettings.java similarity index 58% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/SshRuntimeSettings.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/SshRuntimeSettings.java index b8f5a6576dc..a07838d4b9c 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/SshRuntimeSettings.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/SshRuntimeSettings.java @@ -1,36 +1,36 @@ package com.alibaba.qwen.code.managedagent.config; -import com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeNode; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeNode; +import lombok.Getter; +import lombok.Setter; import java.util.List; -public class SshRuntimeSettings { +@Getter +@Setter +public class SshRuntimeSettings implements com.alibaba.qwen.code.managedagent.settings.SshSettings { private boolean enabled; private List nodes = List.of(); - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public List getNodes() { - return nodes; - } - public void setNodes(List nodes) { this.nodes = List.copyOf(nodes); } - public record Access(String tenantId, String actorId) { - } + public record Access(String tenantId, String actorId) + implements com.alibaba.qwen.code.managedagent.settings.SshSettings.Access {} - public record Node(String nodeId, String displayName, String sshHost, - String nodeExecutable, String qwenEntry, String stateDirectory, - List allowedRoots, int publicationPort, String qwenVersion, - List access) { + public record Node( + String nodeId, + String displayName, + String sshHost, + String nodeExecutable, + String qwenEntry, + String stateDirectory, + List allowedRoots, + int publicationPort, + String qwenVersion, + List access) + implements com.alibaba.qwen.code.managedagent.settings.SshSettings.Node { public Node { allowedRoots = allowedRoots == null ? List.of() : List.copyOf(allowedRoots); access = access == null ? List.of() : List.copyOf(access); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationConfiguration.java similarity index 97% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationConfiguration.java index defc9f5ab05..4d5deb881cb 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationConfiguration.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationConfiguration.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.managedagent.config; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationRetentionObserver; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.SessionResourceCollectionCollector; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationAdmissionStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationCollector; -import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationRetentionStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/WebConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/WebConfiguration.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/WebConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/WebConfiguration.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/EmbeddedRuntimeBroker.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/runtime/EmbeddedRuntimeBroker.java similarity index 68% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/EmbeddedRuntimeBroker.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/runtime/EmbeddedRuntimeBroker.java index de7a05c2ef1..b668406c288 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/EmbeddedRuntimeBroker.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/config/runtime/EmbeddedRuntimeBroker.java @@ -1,38 +1,40 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.config.runtime; +import com.alibaba.qwen.code.managedagent.adapters.runtime.ResidentWorkspaceProvisioner; +import com.alibaba.qwen.code.managedagent.adapters.workspace.ChildWorktreeGit; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorkspaceProvider; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.managedagent.persistence.ExternalStorageRegistrationStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.managedagent.workspace.ChildWorkspaceProvider; -import com.alibaba.qwen.code.managedagent.workspace.ChildWorktreeGit; import com.alibaba.qwen.code.managedagent.workspace.SshWorkspaceService; +import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeRecoveryCoordinator; +import com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceRuntimeProvisioner; +import com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceRuntimeResolver; +import com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceRuntimeTransport; import com.alibaba.qwen.code.runtimebroker.HarnessSessionResolver; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; -import com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.RuntimePublicationGrant; import com.alibaba.qwen.code.runtimebroker.RuntimePublicationVerifier; -import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.http.RuntimeBrokerHttpServer; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.protocol.ExternalRuntimeKinds; import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalHttpRuntimeClient; -import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalRuntimeKinds; import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalRuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalWorkspaceProvisioner; import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.provisioning.StaticRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; -import com.alibaba.qwen.code.runtimebroker.transport.RuntimeBrokerHttpServer; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; @@ -49,9 +51,7 @@ import java.time.Duration; import java.util.List; import java.util.Set; import java.util.UUID; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -import java.util.concurrent.ConcurrentHashMap; public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger( @@ -63,9 +63,9 @@ public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { private final AgentStateStore store; private final WorkspaceRuntimeResolver workspaces; private final ChildWorkspaceProvider childWorkspaces; - private final com.alibaba.qwen.code.managedagent.persistence.ResidentWorkspaces residents; + private final com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentWorkspaceCatalog residents; private final boolean resident; - private final Set retired = ConcurrentHashMap.newKeySet(); + private final com.alibaba.qwen.code.managedagent.workspace.runtime.ManagedRuntimeCoordinator coordinator; private static final Set LIFECYCLE_FENCED = Set.of("CLOSING", "CLOSED", "ARCHIVING", "ARCHIVED", "DELETING", "DELETED"); private final SshWorkspaceService ssh; @@ -159,7 +159,7 @@ public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { this.ssh = ssh; ManagedAgentProperties.RuntimeBroker broker = properties.getRuntimeBroker(); - this.residents = new com.alibaba.qwen.code.managedagent.persistence.ResidentWorkspaces(broker.getResidentWorkspaces()); + this.residents = new com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentWorkspaceCatalog(broker.getResidentWorkspaces()); this.resident = "kubernetes-workspace".equals(broker.getProvisioner()); require(broker.getToken(), "Runtime Broker token"); require(broker.getWorkspaceGeneration(), @@ -197,7 +197,7 @@ public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { bindingRepository, sessionRepository); RuntimeProvisioner baseProvisioner; if (resident) { - if (!(bindingRepository instanceof com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository original) + if (!(bindingRepository instanceof com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository original) || jdbc == null || manager == null || workspaceExecutionStore == null || !original.usesDataSource(jdbc.getDataSource())) { throw new IllegalStateException("Resident Workspace requires original JDBC authority"); @@ -207,7 +207,7 @@ public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { throw new IllegalArgumentException("Resident provisioner kind differs"); } baseProvisioner = residentProvisioner == null - ? new ResidentWorkspaceProvisioner(properties, jdbc, manager, original) + ? new ResidentWorkspaceProvisioner(properties, new com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore(jdbc, manager, new ObjectMapper()), original) : residentProvisioner; } else { if (residentProvisioner != null) { @@ -232,93 +232,18 @@ public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { remoteProvisioner.withManagedExtensionCacheDirectory(properties.getManagedExtensions().getCacheDirectory(), properties.getManagedExtensions().getCgroupRoot()); } baseProvisioner = new com.alibaba.qwen.code.runtimebroker.provisioning.RoutingRuntimeProvisioner( - java.util.Map.of(local.kind(), local, com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeProvisioner.KIND, remoteProvisioner), - scope -> ssh.runtimeStorage(scope) == null ? local.kind() : com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeProvisioner.KIND, + java.util.Map.of(local.kind(), local, com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS, remoteProvisioner), + scope -> ssh.runtimeStorage(scope) == null ? local.kind() : com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS, (kind, lease) -> { - if (!com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeProvisioner.KIND.equals(kind)) { + if (!com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS.equals(kind)) { ssh.connections().registerDirect(lease); } }, ssh.connections()::forget); } RuntimeProvisioner provisioner = workspaces == null || resident ? baseProvisioner : new WorkspaceRuntimeProvisioner(baseProvisioner, workspaces, workspaceExecutionStore); - HarnessSessionResolver resolver = new HarnessSessionResolver() { - @Override - public CompletionStage resolve(String sessionId) { - return resolveScope(sessionId, false, null); - } - - @Override - public CompletionStage resolve(String sessionId, - RuntimeLifecycleAuthority authority) { - return resolveScope(sessionId, false, authority); - } - - @Override - public CompletionStage resolveAdmission( - String sessionId) { - return resolveScope(sessionId, true, null); - } - - @Override - public CompletionStage resolveAdmission( - String sessionId, RuntimeLifecycleAuthority authority) { - return resolveScope(sessionId, true, authority); - } - - @Override - public CompletionStage resolveTenant(String sessionId) { - return store.findSessionById(sessionId) - .map(session -> CompletableFuture.completedFuture( - session.tenantId())) - .orElseGet(() -> CompletableFuture.failedFuture( - new IllegalArgumentException( - "Session is not owned by this" - + " service"))); - } - - private CompletionStage resolveScope( - String sessionId, boolean admission, - RuntimeLifecycleAuthority authority) { - SessionRecord session = store.findSessionById(sessionId) - .orElse(null); - if (session == null) { - CompletableFuture failed = - new CompletableFuture<>(); - failed.completeExceptionally(new IllegalArgumentException( - "Session is not owned by this service")); - return failed; - } - if (session.workspace() != null) { - if (workspaces != null) { - return CompletableFuture.completedFuture(workspaces.resolve(sessionId, authority).scope()); - } - return CompletableFuture.failedFuture( - new RuntimeBrokerException(409, - "workspace_unavailable", - "Hosted Workspace execution is not available.", - false)); - } - // The durable row is the admission fence for closing/closed, - // archived and deleted Sessions: unlike the in-process - // retired set it survives restarts and never accumulates in - // memory. Only the admission resolve fences: release and the - // unknown-outcome reconcile run after a close and must still - // resolve the scope to settle the binding. - if (admission && LIFECYCLE_FENCED.contains(session.status())) { - return CompletableFuture.failedFuture( - new RuntimeBrokerException(409, - "runtime_broker_session_closed", - "Harness Session is closed.", false)); - } - return CompletableFuture.completedFuture(new RuntimeScope( - session.tenantId(), workspaceId, - broker.getWorkspaceGeneration(), - workspaceCwd, - properties.getHarness().getCapabilityDigest(), - broker.getIsolationClass())); - } - }; + HarnessSessionResolver resolver = com.alibaba.qwen.code.managedagent.workspace.runtime.ManagedRuntimeCoordinator.resolver( + store, workspaces, properties, workspaceId, workspaceCwd); ObjectMapper mapper = new ObjectMapper(); RuntimePublicationVerifier verifier = publications == null || publicationData == null ? null : new RuntimePublicationVerifier() { @@ -369,6 +294,8 @@ public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { } this.recovery = broker.isTrustedLocalRebootRecovery() ? new RuntimeRecoveryCoordinator(service, bindingRepository) : null; + this.coordinator = new com.alibaba.qwen.code.managedagent.workspace.runtime.ManagedRuntimeCoordinator( + service, store, workspaces, childWorkspaces, residents, resident, ssh, localProvisioner, extensionBindings, extensionTransport); // Log the resolved window: a suffix-less config value binds as // seconds, so a stale milliseconds-style "1800000" meant as 30 // minutes shows up here as PT500H. @@ -378,101 +305,16 @@ public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { @Override public boolean isEnabled() { - return true; + return coordinator.isEnabled(); } public void configureExtensions(com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackageService packages) { - extensionTransport.configureExtensionPreparation((binding, sessionId, operation) -> { - String tenant = binding.getRequest().getScope().getTenantId(); - @SuppressWarnings("unchecked") - var selected = (List>) operation.get("extensions"); - CompletionStage>> delivery = - CompletableFuture.completedFuture(new java.util.LinkedHashMap<>()); - java.util.Map manifests = new java.util.LinkedHashMap<>(); - for (var pin : selected) { - delivery = delivery.thenCompose(settings -> { - var parsed = new com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.Pin( - (String) pin.get("extensionId"), ((Number) pin.get("packageRevision")).longValue(), - (String) pin.get("packageDigest"), pin.get("settingsRevision") == null ? null - : ((Number) pin.get("settingsRevision")).longValue()); - try { - var resolved = packages.resolvePin(tenant, binding.getRequest().getScope().getWorkspaceId(), parsed); - var manifest = resolved.descriptor().manifest(); - java.util.Map metadata = new java.util.LinkedHashMap<>(java.util.Map.of( - "schemaVersion", manifest.schemaVersion(), "compilerVersion", manifest.compilerVersion(), - "name", manifest.name(), "version", manifest.version(), - "target", java.util.Map.of("platform", manifest.target().platform(), "arch", manifest.target().arch()), - "components", manifest.components())); - if (manifest.sourceType() != null) { - metadata.put("sourceType", manifest.sourceType()); - metadata.put("sourceRevision", manifest.sourceRevision()); - } - manifests.put(parsed.extensionId(), metadata); - return installExtensionPackage(tenant, sessionId, binding, parsed.packageDigest(), resolved.archive()) - .thenApply(ignored -> { - settings.put(parsed.extensionId(), resolved.settings()); - return settings; - }); - } catch (IOException error) { - return CompletableFuture.failedFuture(error); - } - }); - } - return delivery.thenApply(settings -> { - java.util.Map prepared = new java.util.LinkedHashMap<>(operation); - prepared.put("settings", settings); - prepared.put("packageManifests", manifests); - prepared.put("toolProfile", store.requireSession(tenant, sessionId).toolProfile()); - return prepared; - }); - }); - } - - private CompletionStage> installExtensionPackage(String tenantId, - String sessionId, com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord expected, String digest, Path archive) { - SessionRecord session = store.requireSession(tenantId, sessionId); - var binding = extensionBindings.findById(expected.getBindingId()); - if (binding == null || binding.getGeneration() != expected.getGeneration() - || !binding.getRequest().equals(expected.getRequest()) - || !java.util.Objects.equals(binding.getProvisionSeed(), expected.getProvisionSeed()) - || !sameExtensionLease(binding.getLease(), expected.getLease()) - || binding.getState() != com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord.State.READY - || binding.isDrainRequested() || binding.getLease() == null || session.workspace() == null - || !tenantId.equals(binding.getRequest().getScope().getTenantId()) - || !session.workspace().getWorkspaceId().equals(binding.getRequest().getScope().getWorkspaceId()) - || !"session".equals(binding.getRequest().getScope().getIsolationClass()) - || !sessionId.equals(binding.getRequest().getIsolationKey())) { - return CompletableFuture.failedFuture(WorkspaceExecutionStore.unavailable()); - } - return extensionTransport.installExtensionPackage(binding, digest, archive).thenApply(result -> { - var current = extensionBindings.findById(expected.getBindingId()); - if (current == null || current.getGeneration() != binding.getGeneration() - || !current.getRequest().equals(binding.getRequest()) - || !java.util.Objects.equals(current.getProvisionSeed(), binding.getProvisionSeed()) - || current.getState() != com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord.State.READY - || current.isDrainRequested() || !sameExtensionLease(current.getLease(), binding.getLease())) { - throw WorkspaceExecutionStore.unavailable(); - } - return result; - }); - } - - private static boolean sameExtensionLease(RuntimeLease left, RuntimeLease right) { - return left != null && right != null && left.getRuntimeInstanceId().equals(right.getRuntimeInstanceId()) - && left.getLeaseId().equals(right.getLeaseId()) && left.getEpoch() == right.getEpoch() - && left.getEndpoint().equals(right.getEndpoint()) && left.getToken().equals(right.getToken()); + coordinator.configureExtensions(packages); } @Override public CompletionStage warm(String sessionId) { - if (retired.contains(sessionId)) { - CompletableFuture failed = new CompletableFuture<>(); - failed.completeExceptionally(new RuntimeBrokerException(409, - "runtime_broker_session_closed", - "Harness Session is closed.", false)); - return failed; - } - return service.warm(sessionId).thenApply(ignored -> null); + return coordinator.warm(sessionId); } /** @@ -482,75 +324,55 @@ public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { */ @Override public CompletionStage drain(String sessionId) { - // drain() runs while the row still reads CLOSING/DELETING; the - // admission resolve fences every lifecycle status durably, so only - // a vanished row needs the in-process entry. - if (store.findSessionById(sessionId).isEmpty()) { - retired.add(sessionId); - } - return CompletableFuture.completedFuture(null); + return coordinator.drain(sessionId); } @Override public boolean supportsWorkspaceClose() { - return service.supportsDrainedStop() || resident; + return coordinator.supportsWorkspaceClose(); } @Override public boolean supportsWorkspaceContextChange(ContextBinding binding) { - return residents.find(binding) == null; + return coordinator.supportsWorkspaceContextChange(binding); } @Override public boolean supportsWorkspaceClose(com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding binding) { - if (resident && residents.find(binding) != null) { - return true; - } - if (binding != null && ssh != null && ssh.runtimeStorage(binding) != null) { - ssh.node(ssh.runtimeStorage(binding).nodeId()); - return true; - } - return localProvisioner.supportsDrainedStop(); + return coordinator.supportsWorkspaceClose(binding); } @Override public void requestWorkspaceClose(String tenantId, String sessionId) { - service.requestHarnessDrain(tenantId, sessionId); + coordinator.requestWorkspaceClose(tenantId, sessionId); } @Override public CompletionStage closeWorkspace(String tenantId, String sessionId) { - var session = store.findSessionById(sessionId).orElse(null); - if (resident) { - if (session == null || !tenantId.equals(session.tenantId()) || residents.find(session.workspace()) == null) { - return CompletableFuture.failedFuture(com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore.unavailable()); - } - return service.closeResidentHarnessSession(residents.request(session.workspace()), sessionId); - } - return service.drainHarnessSession(tenantId, sessionId); + return coordinator.closeWorkspace(tenantId, sessionId); } @Override public CompletionStage drainWorkspaceResources(String tenantId, String sessionId, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority) { - return service.drainHarnessResources(tenantId, sessionId, authority, false); + return coordinator.drainWorkspaceResources(tenantId, sessionId, authority); } @Override public CompletionStage prepareWorkspaceResources(String tenantId, String sessionId, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority) { - return service.prepareHarnessResources(tenantId, sessionId, authority); + return coordinator.prepareWorkspaceResources(tenantId, sessionId, authority); } @Override public CompletionStage recoverStoppedWorkspaceResources(String tenantId, String sessionId, com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority authority) { - return service.drainHarnessResources(tenantId, sessionId, authority, true); + return coordinator.recoverStoppedWorkspaceResources(tenantId, sessionId, authority); } @Override public ChildWorkspaceProvider childWorkspaces() { - return childWorkspaces; + return coordinator.childWorkspaces(); } private static ChildWorkspaceProvider childWorkspaces(WorkspaceRuntimeResolver workspaces, @@ -582,10 +404,7 @@ public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { @Override public void verifyWorkspaceCwdTarget(ContextBinding binding, String targetCwdRelative) { - if (workspaces == null) { - throw WorkspaceExecutionStore.unavailable(); - } - workspaces.verifyInstallable(binding, targetCwdRelative); + coordinator.verifyWorkspaceCwdTarget(binding, targetCwdRelative); } public URI getBaseUri() { @@ -624,9 +443,9 @@ public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { static RuntimeProvisioner provisioner( ManagedAgentProperties.RuntimeBroker broker, HttpRuntimeTransport transport, - com.alibaba.qwen.code.runtimebroker.provisioning.ExternalStorageRegistry registry) { + com.alibaba.qwen.code.runtimebroker.spi.ExternalStorageRegistry registry) { if (broker.isTrustedLocalRebootRecovery() - && (!broker.isDurableLocalProcess() || !LocalProcessRuntimeProvisioner.KIND.equals(broker.getProvisioner()))) { + && (!broker.isDurableLocalProcess() || !com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(broker.getProvisioner()))) { if (!broker.isDurableLocalProcess()) { throw new IllegalStateException("Trusted reboot recovery requires durable local-process" + " provisioning; enable durable local-process" @@ -640,7 +459,7 @@ public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { + " (QWEN_MANAGED_AGENT_RUNTIME_TRUSTED_LOCAL_REBOOT_RECOVERY=false)" + " for non-durable provisioners"); } - if (LocalProcessRuntimeProvisioner.KIND.equals(broker.getProvisioner())) { + if (com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(broker.getProvisioner())) { require(broker.getStateDirectory(), "Runtime Broker state directory"); require(broker.getNodeExecutable(), "Node.js executable"); @@ -749,7 +568,7 @@ public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { } return broker.getWorkspaceCwd(); } - if (!LocalProcessRuntimeProvisioner.KIND.equals(broker.getProvisioner())) { + if (!com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(broker.getProvisioner())) { return broker.getWorkspaceCwd(); } try { @@ -765,7 +584,7 @@ public class EmbeddedRuntimeBroker implements RuntimeWarmer, AutoCloseable { ManagedAgentProperties.RuntimeBroker broker, String workspaceCwd) { String configured = broker.getWorkspaceId(); - if (!LocalProcessRuntimeProvisioner.KIND.equals(broker.getProvisioner()) + if (!com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(broker.getProvisioner()) && !"kubernetes".equals(broker.getProvisioner())) { require(configured, "Runtime Broker workspace ID"); return configured; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMain.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/ResidentWorkspaceRetirementMain.java similarity index 84% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMain.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/ResidentWorkspaceRetirementMain.java index 048a9fd5d53..582364ca3a2 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMain.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/ResidentWorkspaceRetirementMain.java @@ -1,11 +1,12 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; import com.alibaba.qwen.code.maintenance.WorkspaceRecoveryApplication; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.persistence.ResidentWorkspaceRetirementStore; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesHttpRuntimeClient; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; @@ -63,13 +64,13 @@ public final class ResidentWorkspaceRetirementMain { var result = switch (command.getFirst()) { case "inspect" -> store.inspect(request); case "begin" -> store.begin(request); - case "delete-pod" -> store.deletePod(request, command.get(2), new KubernetesHttpRuntimeClient( + case "delete-pod" -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(store).deletePod(request, command.get(2), new KubernetesHttpRuntimeClient( URI.create(options.getResidentKubernetesApiUrl()), Path.of(options.getResidentKubernetesTokenFile()), Path.of(options.getResidentKubernetesCaFile()))); - case "prepare-reclamation" -> store.prepareReclamation(request, command.get(2), new KubernetesHttpRuntimeClient( + case "prepare-reclamation" -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(store).prepareReclamation(request, command.get(2), new KubernetesHttpRuntimeClient( URI.create(options.getResidentKubernetesApiUrl()), Path.of(options.getResidentKubernetesTokenFile()), Path.of(options.getResidentKubernetesCaFile()))); - case "reclaim" -> store.reclaim(request, new KubernetesHttpRuntimeClient( + case "reclaim" -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(store).reclaim(request, new KubernetesHttpRuntimeClient( URI.create(options.getResidentKubernetesApiUrl()), Path.of(options.getResidentKubernetesTokenFile()), Path.of(options.getResidentKubernetesCaFile()))); default -> throw new IllegalArgumentException("Unknown maintenance action"); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiHostedMain.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiHostedMain.java similarity index 88% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiHostedMain.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiHostedMain.java index afc5a9b3ef1..7b7665c275e 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiHostedMain.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiHostedMain.java @@ -1,12 +1,15 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; -import com.alibaba.qwen.code.daemon.HostedHarnessClient; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; +import com.alibaba.qwen.code.managedagent.harness.HarnessNegotiation; +import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRuntimeAccess; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; @@ -61,12 +64,12 @@ final class WorkspaceCsiHostedMain { var manager = new DataSourceTransactionManager(source); var access = new WorkspaceCsiRuntimeAccess(jdbc, manager, json, properties, request.registration(), request.sessionId(), request.runtimeRequestKey(), - bindings, new JdbcRuntimeSessionRepository(source), new HttpRuntimeTransport()); + bindings, new JdbcRuntimeSessionRepository(source), new HttpRuntimeTransport(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); if (retiring) { UUID.fromString(args[2]); var ids = jdbc.queryForList("SELECT target_id FROM ma_operation WHERE operation_key = ?" + " AND operation_kind = 'CSI_RETIREMENT' AND tenant_id = ? AND operation_id = ?", String.class, - WorkspaceOperations.key(WorkspaceOperations.Kind.CSI_RETIREMENT, request.registration().tenantId(), args[2]), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("CSI_RETIREMENT", request.registration().tenantId(), args[2]), request.registration().tenantId(), args[2]); if (ids.size() != 1) { throw new IllegalArgumentException("Original retirement is unavailable"); @@ -79,8 +82,7 @@ final class WorkspaceCsiHostedMain { } URI baseUri = hostedUri(required("K2_HOSTED_URL")); String token = required("K2_HOSTED_TOKEN"); - try (var negotiation = HostedHarnessClient.builder().baseUri(baseUri).bearerToken(token) - .capabilityDigest(required("QWEN_HOSTED_HARNESS_CAPABILITY_DIGEST")).build(); + try (var negotiation = new HarnessNegotiation(baseUri, token, required("QWEN_HOSTED_HARNESS_CAPABILITY_DIGEST")); var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).followRedirects(HttpClient.Redirect.NEVER).build()) { JsonNode result = post(client, baseUri.resolve("/session/" + request.sessionId() + "/internal-csi/retire"), token, negotiation.capabilities().getBootId(), Map.of("tenantId", request.registration().tenantId(), @@ -96,8 +98,7 @@ final class WorkspaceCsiHostedMain { var scope = access.resolve(request.sessionId()).toCompletableFuture().join(); URI baseUri = hostedUri(required("K2_HOSTED_URL")); String token = required("K2_HOSTED_TOKEN"); - try (var negotiation = HostedHarnessClient.builder().baseUri(baseUri).bearerToken(token) - .capabilityDigest(required("QWEN_HOSTED_HARNESS_CAPABILITY_DIGEST")).build(); + try (var negotiation = new HarnessNegotiation(baseUri, token, required("QWEN_HOSTED_HARNESS_CAPABILITY_DIGEST")); var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)) .followRedirects(HttpClient.Redirect.NEVER).build()) { String boot = negotiation.capabilities().getBootId(); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiProtectionMain.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiProtectionMain.java similarity index 90% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiProtectionMain.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiProtectionMain.java index 69443066cbc..52bc2895d8d 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiProtectionMain.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiProtectionMain.java @@ -1,6 +1,7 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.maintenance; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.adapters.runtime.WorkspaceCsiResourceGuard; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.json.JsonMapper; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRegistrationMain.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiRegistrationMain.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRegistrationMain.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiRegistrationMain.java index 99cca7c40a6..92962cf3a41 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRegistrationMain.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiRegistrationMain.java @@ -1,5 +1,7 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetirementMain.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiRetirementMain.java similarity index 91% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetirementMain.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiRetirementMain.java index 28e942e463f..cd9f1572d12 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetirementMain.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiRetirementMain.java @@ -1,8 +1,10 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeMain.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiRuntimeMain.java similarity index 82% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeMain.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiRuntimeMain.java index be58cbd28e3..a4a4f73a5a4 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeMain.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiRuntimeMain.java @@ -1,17 +1,23 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; +import com.alibaba.qwen.code.managedagent.adapters.runtime.WorkspaceCsiResourceGuard; +import com.alibaba.qwen.code.managedagent.adapters.runtime.WorkspaceCsiRuntimeProvisioner; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceCsiResourceGuard; -import com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceCsiRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; +import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationStore; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRuntimeAccess; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.http.RuntimeBrokerHttpServer; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesHttpRuntimeClient; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiFilesProtocol; -import com.alibaba.qwen.code.runtimebroker.transport.RuntimeBrokerHttpServer; import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; @@ -37,8 +43,8 @@ public final class WorkspaceCsiRuntimeMain { } public record Request(WorkspaceCsiRegistration registration, String sessionId, String runtimeRequestKey, - String image, List command, List artifacts, - WorkspaceCsiResourceGuard.ProtectionIdentity protection) { + String image, List command, List artifacts, + com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity protection) { public Request { if (registration == null || protection == null || sessionId == null || !UUID.fromString(sessionId).toString().equals(sessionId) @@ -49,7 +55,7 @@ public final class WorkspaceCsiRuntimeMain { || value.length() > 8192 || value.indexOf('\0') >= 0) || artifacts == null || artifacts.size() > 48 || artifacts.stream().anyMatch(java.util.Objects::isNull) - || artifacts.stream().map(WorkspaceCsiRuntimeProvisioner.WorkerArtifact::configMapName) + || artifacts.stream().map(com.alibaba.qwen.code.managedagent.contracts.runtime.WorkerArtifact::configMapName) .distinct().count() != artifacts.size()) { throw new IllegalArgumentException("Invalid private CSI operator request"); } @@ -99,9 +105,9 @@ public final class WorkspaceCsiRuntimeMain { var http = new HttpRuntimeTransport(); var nativeStore = new ManagedSessionStore(jdbc); nativeStore.setCsiBindings(bindings); - var application = new WorkspaceCsiApplicationStore(jdbc, manager, nativeStore, bindings, sessions); + var application = new WorkspaceCsiApplicationStore(jdbc, manager, nativeStore, bindings, sessions, new com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport()); var access = new WorkspaceCsiRuntimeAccess(jdbc, manager, json, properties, request.registration(), - request.sessionId(), request.runtimeRequestKey(), bindings, sessions, http); + request.sessionId(), request.runtimeRequestKey(), bindings, sessions, http, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); access.resolve(request.sessionId()).toCompletableFuture().join(); var api = new KubernetesHttpRuntimeClient(URI.create(required("K2_KUBERNETES_API_URL")), Path.of(required("K2_KUBERNETES_TOKEN_FILE")), Path.of(required("K2_KUBERNETES_CA_FILE"))); diff --git a/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiSessionMain.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiSessionMain.java new file mode 100644 index 00000000000..1b2811c78d4 --- /dev/null +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiSessionMain.java @@ -0,0 +1,71 @@ +package com.alibaba.qwen.code.managedagent.maintenance; + +import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Created; +import com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request; +import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; +import com.fasterxml.jackson.core.StreamReadFeature; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import org.apache.commons.lang3.StringUtils; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.transaction.PlatformTransactionManager; + +import java.nio.file.Files; +import java.nio.file.Path; + +/** Offline private CREATE only; no worker, Harness, or public CSI selection. */ +public final class WorkspaceCsiSessionMain { + private WorkspaceCsiSessionMain() { + } + + public static void main(String[] args) throws Exception { + if (args.length != 2 || !"create".equals(args[0])) { + throw new IllegalArgumentException("Usage: create "); + } + ObjectMapper json = JsonMapper.builder().enable(StreamReadFeature.STRICT_DUPLICATE_DETECTION) + .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS).build(); + Request request = readRequest(Path.of(args[1]), json); + var source = new DriverManagerDataSource(required("K2_JDBC_URL"), required("K2_JDBC_USER"), + required("K2_JDBC_PASSWORD")); + var properties = new ManagedAgentProperties(); + properties.setAgentRevision(required("K2_AGENT_REVISION")); + Created created = create(new ManagedSchemaJdbcTemplate(source), new DataSourceTransactionManager(source), + json, properties, request); + System.out.println(json.writeValueAsString(created)); + } + + static Request readRequest(Path path, ObjectMapper json) { + try (var input = Files.newInputStream(path)) { + byte[] bytes = input.readNBytes(32 * 1024 + 1); + if (bytes.length > 32 * 1024) { + throw new IllegalArgumentException("CSI Session request exceeds its size limit"); + } + Request request = json.readValue(bytes, Request.class); + if (request == null) { + throw new IllegalArgumentException("CSI Session request is required"); + } + return request; + } catch (Exception error) { + throw new IllegalArgumentException("CSI Session request could not be read"); + } + } + + static Created create(JdbcTemplate jdbc, PlatformTransactionManager manager, + ObjectMapper json, ManagedAgentProperties properties, Request request) { + return com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiSessionCommands.create( + jdbc, manager, json, properties, request, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); + } + + private static String required(String name) { + String value = System.getenv(name); + if (StringUtils.isBlank(value)) { + throw new IllegalStateException(name + " is required"); + } + return value; + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckMain.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiWorkerAckMain.java similarity index 76% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckMain.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiWorkerAckMain.java index a2b7eebb214..38ea1768a82 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckMain.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiWorkerAckMain.java @@ -1,17 +1,17 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; -import com.alibaba.qwen.code.managedagent.artifact.AliyunToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; -import com.aliyun.oss.ClientBuilderConfiguration; -import com.aliyun.oss.OSS; -import com.aliyun.oss.OSSClientBuilder; -import com.aliyun.oss.common.auth.CredentialsProviderFactory; -import com.aliyun.oss.common.comm.SignVersion; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; +import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; +import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationAdmissionStore; +import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; +import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiWorkerAckStore; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -51,14 +51,14 @@ public final class WorkspaceCsiWorkerAckMain { var runtimeSessions = new JdbcRuntimeSessionRepository(source); var executions = new JdbcToolExecutionRepository(source); var sessionStore = new ManagedSessionStore(jdbc); + sessionStore.configureHarnessRules(new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); var grants = new ToolPublicationStore(jdbc, manager, sessionStore, executions, bindings, new ToolPublicationStore.Capacity(16L * 1024 * 1024, 64L * 1024 * 1024, 256L * 1024 * 1024, 64)); - OSS oss = "inspect".equals(args[0]) ? null : oss(); + com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient oss = "inspect".equals(args[0]) ? null : oss(); try { ToolPublicationObjectStore objects = oss == null ? inlineObjects() - : new AliyunToolPublicationObjectStore(oss, - required("QWEN_MANAGED_AGENT_TOOL_PUBLICATION_OSS_BUCKET")); + : oss.objects(required("QWEN_MANAGED_AGENT_TOOL_PUBLICATION_OSS_BUCKET")); var data = new ToolPublicationDataStore(jdbc, manager, grants, sessionStore, objects, Duration.ofSeconds(30), Duration.ofSeconds(10), new ToolPublicationDataStore.VerificationBudget(1024 * 1024, Duration.ofMinutes(25))); @@ -75,7 +75,7 @@ public final class WorkspaceCsiWorkerAckMain { } } - private static OSS oss() throws Exception { + private static com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient oss() throws Exception { String endpointValue = System.getenv("QWEN_MANAGED_AGENT_TOOL_PUBLICATION_OSS_ENDPOINT"); if (endpointValue == null && System.getenv("QWEN_MANAGED_AGENT_TOOL_PUBLICATION_OSS_REGION") == null && System.getenv("QWEN_MANAGED_AGENT_TOOL_PUBLICATION_OSS_BUCKET") == null) { @@ -90,12 +90,7 @@ public final class WorkspaceCsiWorkerAckMain { || !endpoint.getHost().equals("oss-" + region + ".aliyuncs.com")) { throw new IllegalArgumentException("Private regional OSS endpoint is required"); } - var configuration = new ClientBuilderConfiguration(); - configuration.setSignatureVersion(SignVersion.V4); - AliyunToolPublicationObjectStore.configureClientRetries(configuration); - return OSSClientBuilder.create().endpoint(endpoint.toString()).region(region) - .credentialsProvider(CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider()) - .clientConfiguration(configuration).build(); + return com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient.regional(endpoint, region); } private static ToolPublicationObjectStore inlineObjects() { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceRecoveryMain.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceMaintenanceRecoveryMain.java similarity index 86% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceRecoveryMain.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceMaintenanceRecoveryMain.java index 4be63359129..91ff55bf6e5 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceRecoveryMain.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceMaintenanceRecoveryMain.java @@ -1,7 +1,10 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount; +import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceMaintenanceRecovery; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.fasterxml.jackson.databind.ObjectMapper; @@ -28,7 +31,7 @@ public final class WorkspaceMaintenanceRecoveryMain { var properties = new ManagedAgentProperties(); properties.getRuntimeBroker().setVerifiedWorkspaceRecoveryEnabled(true); properties.getRuntimeBroker().setWorkspaceMounts(List.of(new WorkspaceMount(args[1], args[4], args[5]))); - var guard = reconcile ? new WorkspaceStorageGuard(jdbc, manager, properties) : null; + var guard = reconcile ? new WorkspaceStorageGuard(jdbc, manager, properties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()) : null; var recovery = new WorkspaceMaintenanceRecovery(jdbc, manager, guard); var binding = new ContextBinding(args[1], args[2], Long.parseLong(args[3]), args[4], ".", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1); diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceMigrationMain.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceMigrationMain.java similarity index 84% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceMigrationMain.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceMigrationMain.java index 0ad6f9b884b..eb50d46a131 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceMigrationMain.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceMigrationMain.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.maintenance; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount; @@ -7,14 +7,16 @@ import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceMigrationStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; +import com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceRuntimeProvisioner; +import com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DriverManagerDataSource; @@ -56,8 +58,8 @@ public final class WorkspaceMigrationMain { request.path("storageId").asText(), request.path("sourceRoot").asText()))); var bindings = "inspect".equals(args[0]) ? null : new JdbcRuntimeBindingRepository(source, AesGcmSecretProtector.fromBase64(required("W1_RUNTIME_CREDENTIAL_KEY_ID"), required("W1_RUNTIME_CREDENTIAL_KEY"))); - var store = new WorkspaceMigrationStore(jdbc, manager, new WorkspaceStorageGuard(jdbc, manager, properties), - bindings, bytes, "retire".equals(args[0])); + var store = new WorkspaceMigrationStore(jdbc, manager, new WorkspaceStorageGuard(jdbc, manager, properties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()), + bindings, bytes, "retire".equals(args[0]), new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalRecoveryWorker()); if ("inspect".equals(args[0])) { System.out.println(store.inspect()); return; @@ -84,7 +86,7 @@ public final class WorkspaceMigrationMain { (row, index) -> ManagedAgentStore.readBinding(row), request.path("tenantId").asText(), sessionId); if (found.size() != 1 || found.getFirst() == null) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return found.getFirst(); }); @@ -100,7 +102,7 @@ public final class WorkspaceMigrationMain { } } catch (Exception error) { try { - store.failed(com.alibaba.qwen.code.managedagent.persistence.WorkspaceRecoveryStore.errorCode(error)); + store.failed(com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.errorCode(error)); } catch (Exception recordingFailure) { error.addSuppressed(recordingFailure); } diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRecoveryCommand.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceRecoveryCommand.java similarity index 92% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRecoveryCommand.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceRecoveryCommand.java index 75cc75d468d..fcd3d95f701 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRecoveryCommand.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceRecoveryCommand.java @@ -1,18 +1,19 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.maintenance; import com.alibaba.qwen.code.maintenance.WorkspaceRecoveryApplication; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceOperatorRecoveryStore; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings; +import com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceRuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeObservation; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.boot.WebApplicationType; @@ -46,11 +47,11 @@ public final class WorkspaceRecoveryCommand { if (args.length == 0) { throw new IllegalArgumentException("Expected inspect, prepare or complete."); } - ManagedAgentProperties.RuntimeBroker options = context.getBean( - ManagedAgentProperties.class).getRuntimeBroker(); + ManagedAgentSettings.RuntimeBroker options = context.getBean( + ManagedAgentSettings.class).getRuntimeBroker(); if (!options.isOperatorRecoveryEnabled() || !options.isDurableLocalProcess() - || !LocalProcessRuntimeProvisioner.KIND.equals(options.getProvisioner()) + || !com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS.equals(options.getProvisioner()) || options.getStateDirectory() == null || options.getStateDirectory().isBlank()) { throw new IllegalStateException("Operator recovery requires enabled durable local workers."); } diff --git a/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceRecoveryMain.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceRecoveryMain.java new file mode 100644 index 00000000000..990d801cb16 --- /dev/null +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceRecoveryMain.java @@ -0,0 +1,88 @@ +package com.alibaba.qwen.code.managedagent.maintenance; + +import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceRecoveryStore; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.jdbc.datasource.SingleConnectionDataSource; + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; + +import static com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.check; +import static com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.text; + +/** Private offline entry; it never boots the application or a Runtime. */ +public final class WorkspaceRecoveryMain { + private static final com.fasterxml.jackson.databind.ObjectMapper JSON = new com.fasterxml.jackson.databind.ObjectMapper(); + + private WorkspaceRecoveryMain() { + } + + public static void main(String[] args) throws Exception { + check(args.length >= 2 && Set.of("capture", "verify", "inspect").contains(args[0]) + && ("inspect".equals(args[0]) ? args.length == 2 + : args.length == 3 && "--offline-confirmed".equals(args[2])), "invalid_arguments"); + Path input = Path.of(args[1]); + check(Files.isRegularFile(input, LinkOption.NOFOLLOW_LINKS) && Files.size(input) <= 1024 * 1024, + "invalid_request_file"); + byte[] bytes = Files.readAllBytes(input); + JsonNode request = com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.parse(bytes); + com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient oss = null; + // The private RPC loop is single-threaded; transactions still commit separately. + try (var source = new SingleConnectionDataSource(required("W1_JDBC_URL"), required("W1_JDBC_USER"), + requiredPresent("W1_JDBC_PASSWORD"), true)) { + var jdbc = new ManagedSchemaJdbcTemplate(source); + var manager = new DataSourceTransactionManager(source); + var properties = new ManagedAgentProperties(); + properties.getRuntimeBroker().setVerifiedWorkspaceRecoveryEnabled(true); + if (!"inspect".equals(args[0])) { + properties.getRuntimeBroker().setWorkspaceMounts(List.of( + new WorkspaceMount(text(request, "tenantId"), text(request, "storageId"), text(request, "sourceRoot")))); + } + ToolPublicationObjectStore objects = null; + if ("capture".equals(args[0]) && System.getenv("W1_OSS_ENDPOINT") != null) { + URI endpoint = URI.create(required("W1_OSS_ENDPOINT")); + String region = required("W1_OSS_REGION"); + check("https".equals(endpoint.getScheme()) && ("oss-" + region + ".aliyuncs.com").equals(endpoint.getHost()) + && (endpoint.getRawPath() == null || endpoint.getRawPath().isEmpty()) + && endpoint.getRawQuery() == null && endpoint.getRawUserInfo() == null, "invalid_oss_endpoint"); + oss = com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient.regional(endpoint, region); + objects = oss.objects(required("W1_OSS_BUCKET")); + } + var store = new WorkspaceRecoveryStore(jdbc, manager, new WorkspaceStorageGuard(jdbc, manager, properties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()), + objects, args[0], bytes); + JsonNode existing = store.inspect(); + if ("inspect".equals(args[0]) || Set.of("SEALED", "VERIFIED").contains(existing.path("state").asText())) { + System.out.println(existing); + return; + } + new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalRecoveryWorker().execute(request, store, store::call); + System.out.println(store.inspect()); + } finally { + if (oss != null) { + oss.shutdown(); + } + } + } + + private static String required(String name) { + String value = requiredPresent(name); + check(!value.isBlank(), "missing_environment"); + return value; + } + + private static String requiredPresent(String name) { + String value = System.getenv(name); + check(value != null, "missing_environment"); + return value; + } +} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageRegistrationMain.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceStorageRegistrationMain.java similarity index 92% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageRegistrationMain.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceStorageRegistrationMain.java index 3187cb5d1d9..c1fdc14d35a 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageRegistrationMain.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceStorageRegistrationMain.java @@ -1,7 +1,8 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; import org.apache.commons.lang3.StringUtils; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -38,7 +39,7 @@ public final class WorkspaceStorageRegistrationMain { properties.getRuntimeBroker().setWorkspaceMounts(List.of( new WorkspaceMount(args[1], args[2], args[3]))); WorkspaceStorageGuard guard = new WorkspaceStorageGuard(new JdbcTemplate(dataSource), - new DataSourceTransactionManager(dataSource), properties); + new DataSourceTransactionManager(dataSource), properties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()); if (inspect) { System.out.println(guard.inspect(args[1], args[2])); } else if (register) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/HarnessBrokerUrl.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/HarnessBrokerUrl.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/HarnessBrokerUrl.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/HarnessBrokerUrl.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ManagedAgentPreflight.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ManagedAgentPreflight.java similarity index 98% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ManagedAgentPreflight.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ManagedAgentPreflight.java index 2c08d276f1e..674f68bf8a8 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ManagedAgentPreflight.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ManagedAgentPreflight.java @@ -1,18 +1,18 @@ package com.alibaba.qwen.code.managedagent.preflight; -import com.alibaba.qwen.code.daemon.DaemonClientCapacityException; -import com.alibaba.qwen.code.daemon.DaemonException; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.daemon.DaemonTransportException; -import com.alibaba.qwen.code.daemon.HostedHarnessCapabilities; -import com.alibaba.qwen.code.daemon.HostedHarnessCapabilityMismatchException; -import com.alibaba.qwen.code.daemon.HostedHarnessClient; import com.alibaba.qwen.code.managedagent.config.BrokerSecurity; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; +import com.alibaba.qwen.code.managedagent.harness.HarnessNegotiation; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessCapabilities; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessCapabilityFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessCapacityFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessTransportFailure; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalRuntimeKinds; +import com.alibaba.qwen.code.runtimebroker.protocol.ExternalRuntimeKinds; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; @@ -647,17 +647,12 @@ public final class ManagedAgentPreflight { ? "install the Qwen Code version the distribution manifest names" : "install Qwen Code " + expectedQwenCodeVersion; String retry = "check that the Harness is reachable, then run --check again"; - HostedHarnessCapabilities capabilities; + HarnessCapabilities capabilities; // The same client and rules the first Session uses. - try (HostedHarnessClient negotiation = HostedHarnessClient.builder() - .baseUri(origin) - .bearerToken(harness.getToken()) - .capabilityDigest(harness.getCapabilityDigest()) - .connectTimeout(Duration.ofMillis(TIMEOUT_MILLIS)) - .requestTimeout(Duration.ofMillis(TIMEOUT_MILLIS)) - .build()) { + try (HarnessNegotiation negotiation = new HarnessNegotiation(origin, harness.getToken(), + harness.getCapabilityDigest(), Duration.ofMillis(TIMEOUT_MILLIS))) { capabilities = negotiation.capabilities(); - } catch (HostedHarnessCapabilityMismatchException mismatch) { + } catch (HarnessCapabilityFailure mismatch) { // Without the credentials file, each process has its own variable. return new Check("harness", Status.FAIL, url + " advertises another capability digest", @@ -667,11 +662,11 @@ public final class ManagedAgentPreflight { + " the same value" : "give the Harness and the control plane the same " + digestSetting); - } catch (DaemonTransportException | DaemonClientCapacityException unanswered) { + } catch (HarnessTransportFailure | HarnessCapacityFailure unanswered) { return new Check("harness", Status.FAIL, url + " did not answer the compatibility request: " + printable(rootMessage(unanswered)), retry); - } catch (DaemonHttpException refused) { + } catch (HarnessHttpFailure refused) { int status = refused.getStatusCode(); // The status alone: the response body is the Harness's text. return status == 401 || status == 403 @@ -682,7 +677,7 @@ public final class ManagedAgentPreflight { : new Check("harness", Status.FAIL, url + " answered the compatibility request with HTTP " + status, retry); - } catch (DaemonException incompatible) { + } catch (HarnessFailure incompatible) { return new Check("harness", Status.FAIL, url + " is not a compatible Hosted Harness: " + printable(rootMessage(incompatible)), install); @@ -731,7 +726,7 @@ public final class ManagedAgentPreflight { // Each Workspace profile the deployment may admit needs its lifecycle, by // the rule the connector applies (WorkspaceToolProfiles.lifecycleProtocol). private static Check lifecycleCheck(String url, ManagedAgentProperties properties, - HostedHarnessCapabilities capabilities, String install) { + HarnessCapabilities capabilities, String install) { ManagedAgentProperties.Harness harness = properties.getHarness(); Set profiles = new LinkedHashSet<>(); if (harness.isWorkspaceFilesEnabled()) { diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ResolvedConfiguration.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ResolvedConfiguration.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ResolvedConfiguration.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ResolvedConfiguration.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ServiceAccount.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ServiceAccount.java similarity index 100% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ServiceAccount.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/managedagent/preflight/ServiceAccount.java diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/RuntimeBrokerHttpServer.java b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/runtimebroker/http/RuntimeBrokerHttpServer.java similarity index 99% rename from packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/RuntimeBrokerHttpServer.java rename to packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/runtimebroker/http/RuntimeBrokerHttpServer.java index e6999ef03ec..30f4412ce07 100644 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/runtimebroker/transport/RuntimeBrokerHttpServer.java +++ b/packages/managed-agent-server/server/src/main/java/com/alibaba/qwen/code/runtimebroker/http/RuntimeBrokerHttpServer.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.transport; +package com.alibaba.qwen.code.runtimebroker.http; import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; @@ -6,12 +6,15 @@ import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; import com.alibaba.qwen.code.runtimebroker.ExecutionReconciliation; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; -import com.alibaba.qwen.code.runtimebroker.provisioning.ProviderRuntimeProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ProviderRuntimeProtocol; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; +import com.alibaba.qwen.code.runtimebroker.transport.CsiFileFinalizeProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.CsiFileHistoryProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.CsiNativeReadbackProtocol; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpServer; diff --git a/packages/managed-agent-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/packages/managed-agent-server/server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports similarity index 100% rename from packages/managed-agent-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports rename to packages/managed-agent-server/server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/packages/managed-agent-server/src/main/resources/application-managed-local.yml b/packages/managed-agent-server/server/src/main/resources/application-managed-local.yml similarity index 100% rename from packages/managed-agent-server/src/main/resources/application-managed-local.yml rename to packages/managed-agent-server/server/src/main/resources/application-managed-local.yml diff --git a/packages/managed-agent-server/src/main/resources/application.yml b/packages/managed-agent-server/server/src/main/resources/application.yml similarity index 100% rename from packages/managed-agent-server/src/main/resources/application.yml rename to packages/managed-agent-server/server/src/main/resources/application.yml diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactPolicy.java b/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactPolicy.java deleted file mode 100644 index 583cd0eadb1..00000000000 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactPolicy.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.alibaba.qwen.code.managedagent.artifact; - -public interface ManagedArtifactPolicy { - /** A stable configuration fingerprint that changes whenever a policy decision changes. */ - String version(); - - boolean publishOriginal(String tenantId, String workspaceId, - String sessionId); - - boolean publishPreview(String tenantId, String workspaceId, - String sessionId); - - boolean readOriginal(String tenantId, String actorId, String workspaceId, - String sessionId); -} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfiguration.java b/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfiguration.java deleted file mode 100644 index 469cbac6eef..00000000000 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfiguration.java +++ /dev/null @@ -1,278 +0,0 @@ -package com.alibaba.qwen.code.managedagent.config; - -import com.alibaba.qwen.code.managedagent.event.EventTransportConsumer; -import com.alibaba.qwen.code.managedagent.event.LiteTopicEventTransport; -import com.alibaba.qwen.code.managedagent.event.LiteTopicPublicEventTransport; -import com.alibaba.qwen.code.managedagent.event.MessageMaterializer; -import com.alibaba.qwen.code.managedagent.event.TransportWakeActivator; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore; -import com.alibaba.qwen.code.managedagent.session.OwnerSubscriptions; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.rocketmq.client.apis.ClientConfiguration; -import org.apache.rocketmq.client.apis.ClientException; -import org.apache.rocketmq.client.apis.ClientServiceProvider; -import org.apache.rocketmq.client.apis.consumer.ConsumeResult; -import org.apache.rocketmq.client.apis.consumer.LitePushConsumer; -import org.apache.rocketmq.client.apis.producer.Producer; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.task.ThreadPoolTaskSchedulerBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; - -import java.util.concurrent.ScheduledFuture; - -/** - * Wires the broker EventTransport (design - * 2026-10-04-managed-agent-event-transport): the authoritative-journal - * producer (phase MQ2b), and the public-event producer and the two consumer - * lanes (phase MQ2c). Every bean here exists only when - * {@code qwen.managed-agent.event-transport.enabled=true}, so a flag-off - * deployment creates no producer, no consumer and no transport, and the - * after-commit seam's transport list stays empty — the MQ2 byte-identical - * exit check is an inspectable property of this class, not a claim. - * - *

Deployment prerequisites, all on the broker (RocketMQ 5.5.0+ with - * {@code enableLmq}): the topic is a LITE topic, and three groups are - * pre-created as LITE groups bound to it — the configured capability-probe, - * wake-bootstrap and owner groups — because subscribeLite rejects an - * auto-created (regular) group with "regular group cannot use LITE mode". - * mqadmin creates nothing, printing only its usage, unless a cluster (-c) or - * broker (-b) is named: - *

- *   mqadmin updateTopic -c <cluster> -t <topic> -a +message.type=LITE
- *   mqadmin updateSubGroup -c <cluster> -g <group> -o true \
- *     --attributes +lite.bind.topic=<topic>
- * 
- * scripts/rocketmq-lite-broker.sh provisions exactly this for the broker - * gates. - * The wake-bootstrap group is shared by every node: the one bootstrap - * LiteTopic is consumed in order by one member at a time, so its listener - * only checks SQL and claims, and hands each load to a small pool. The owner - * group is shared too, and each node subscribes it only to the Sessions its - * Harness writes (§11). - */ -@Configuration -@ConditionalOnProperty(prefix = "qwen.managed-agent.event-transport", - name = "enabled", havingValue = "true") -public class EventTransportConfiguration { - // The lite topic only the startup capability probe uses. - static final String CAPABILITY_PROBE_LITE = "capability-probe"; - - // Replaceable, so the wiring can be exercised without a broker. - @Bean - @ConditionalOnMissingBean(ClientServiceProvider.class) - public ClientServiceProvider eventTransportClientProvider() { - return ClientServiceProvider.loadService(); - } - - /** The one producer both wires share; closing it closes the connections. */ - @Bean(destroyMethod = "close") - public Producer eventTransportProducer(ClientServiceProvider provider, - ManagedAgentProperties properties) throws ClientException { - var config = properties.getEventTransport(); - ClientConfiguration clientConfig = clientConfiguration(config); - verifyLiteTopicCapability(provider, clientConfig, config.getTopic(), - config.getCapabilityProbeGroup()); - Producer producer = provider.newProducerBuilder() - .setClientConfiguration(clientConfig) - .setTopics(config.getTopic()) - .build(); - try { - verifyTopicAcceptsLiteMessages(provider, producer, - config.getTopic()); - } catch (RuntimeException failure) { - closeQuietly(producer, failure); - throw failure; - } - return producer; - } - - @Bean - public LiteTopicEventTransport liteTopicEventTransport(Producer producer, - ClientServiceProvider provider, ManagedAgentProperties properties) { - var config = properties.getEventTransport(); - return new LiteTopicEventTransport(producer, provider, - config.getTopic(), config.getWakeBootstrapLiteTopic()); - } - - @Bean - public TransportConsumerStore transportConsumerStore(@Qualifier("managedJdbcTemplate") JdbcTemplate jdbc) { - return new TransportConsumerStore(jdbc); - } - - @Bean(destroyMethod = "close") - public LiteTopicPublicEventTransport liteTopicPublicEventTransport( - Producer producer, ClientServiceProvider provider, - ManagedAgentProperties properties, @Qualifier("managedAgentObjectMapper") ObjectMapper objectMapper, - TransportConsumerStore store) { - return new LiteTopicPublicEventTransport(producer, provider, - properties.getEventTransport().getTopic(), objectMapper, - store::workspaceOf); - } - - @Bean - public OwnerSubscriptions ownerSubscriptions(HarnessConnector harness, - TransportConsumerStore store) { - return new OwnerSubscriptions(harness, store); - } - - @Bean(destroyMethod = "close") - public TransportWakeActivator transportWakeActivator( - TransportConsumerStore store, HarnessConnector harness, - OwnerSubscriptions owners) { - return new TransportWakeActivator(store, harness, owners::subscribe); - } - - @Bean(destroyMethod = "close") - public EventTransportConsumer eventTransportConsumer( - TransportWakeActivator wakes, TransportConsumerStore store, - MessageMaterializer materializer) { - return new EventTransportConsumer(wakes, store, materializer); - } - - /** - * The owner lane: subscribed per Session by {@link OwnerSubscriptions}, - * never to a fixed LiteTopic. - */ - @Bean(destroyMethod = "close") - public LitePushConsumer eventTransportOwnerConsumer( - ClientServiceProvider provider, ManagedAgentProperties properties, - EventTransportConsumer listener, OwnerSubscriptions owners) - throws ClientException { - var config = properties.getEventTransport(); - LitePushConsumer consumer = provider.newLitePushConsumerBuilder() - .setClientConfiguration(clientConfiguration(config)) - .setConsumerGroup(config.getOwnerGroup()) - .bindTopic(config.getTopic()) - .setMessageListener(listener::consume) - .build(); - owners.bind(consumer); - return consumer; - } - - /** The shared wake bootstrap lane: every node subscribes the one LiteTopic. */ - @Bean(destroyMethod = "close") - public LitePushConsumer eventTransportWakeBootstrapConsumer( - ClientServiceProvider provider, ManagedAgentProperties properties, - EventTransportConsumer listener) throws ClientException { - var config = properties.getEventTransport(); - LitePushConsumer consumer = provider.newLitePushConsumerBuilder() - .setClientConfiguration(clientConfiguration(config)) - .setConsumerGroup(config.getWakeBootstrapGroup()) - .bindTopic(config.getTopic()) - .setMessageListener(listener::consume) - .build(); - try { - consumer.subscribeLite(config.getWakeBootstrapLiteTopic()); - } catch (ClientException | RuntimeException failure) { - closeQuietly(consumer, failure); - throw new IllegalStateException("the event-transport wake" - + " bootstrap group " + config.getWakeBootstrapGroup() - + " could not subscribe " - + config.getWakeBootstrapLiteTopic() + " on topic " - + config.getTopic() + ": pre-create it as a LITE group" - + " bound to the topic (mqadmin updateSubGroup -c" - + " -g " + config.getWakeBootstrapGroup() - + " -o true --attributes +lite.bind.topic=" - + config.getTopic() + ")", failure); - } - return consumer; - } - - /** - * Owner reconciliation runs on its own thread: it calls the broker, and - * must never delay the one-thread default scheduler's recovery ticks. - */ - @Bean(defaultCandidate = false) - public ThreadPoolTaskScheduler eventTransportScheduler( - ThreadPoolTaskSchedulerBuilder builder) { - return builder.poolSize(1).threadNamePrefix("event-transport-") - .build(); - } - - @Bean(destroyMethod = "cancel") - public ScheduledFuture eventTransportOwnerReconcileTask( - @Qualifier("eventTransportScheduler") - ThreadPoolTaskScheduler eventTransportScheduler, - OwnerSubscriptions owners, - // Ensures the owner consumer is bound before the first tick. - @Qualifier("eventTransportOwnerConsumer") - LitePushConsumer ownerConsumer, - ManagedAgentProperties properties) { - return eventTransportScheduler.scheduleWithFixedDelay(owners::reconcile, - properties.getEventTransport().getOwnerReconcileInterval()); - } - - private static ClientConfiguration clientConfiguration( - ManagedAgentProperties.EventTransport config) { - return ClientConfiguration.newBuilder() - .setEndpoints(config.getEndpoints()) - .build(); - } - - private static void closeQuietly(java.io.Closeable closeable, - Throwable failure) { - try { - closeable.close(); - } catch (Exception suppressed) { - failure.addSuppressed(suppressed); - } - } - - // #13200 hardening: fail fast at startup on a broker that cannot serve - // LiteTopics (needs RocketMQ 5.5.0+ with enableLmq), instead of silently - // degrading to a classic topic. This probe subscribes and unsubscribes and - // writes nothing; the send probe below writes one message. Building the - // consumer also catches an unreachable or non-gRPC (classic 4.x) broker. - private static void verifyLiteTopicCapability(ClientServiceProvider provider, - ClientConfiguration clientConfig, String topic, String group) { - try (LitePushConsumer probe = provider.newLitePushConsumerBuilder() - .setClientConfiguration(clientConfig) - .setConsumerGroup(group) - .bindTopic(topic) - .setMessageListener(messageView -> ConsumeResult.SUCCESS) - .build()) { - probe.subscribeLite(CAPABILITY_PROBE_LITE); - probe.unsubscribeLite(CAPABILITY_PROBE_LITE); - } catch (Exception failure) { - throw new IllegalStateException("the event-transport LiteTopic" - + " capability probe failed on topic " + topic + ": the" - + " broker must serve LiteTopics (RocketMQ 5.5.0+ with" - + " enableLmq) and the probe group " + group - + " must be pre-created as a LITE group bound to the topic" - + " (mqadmin updateSubGroup -c -g " + group - + " -o true --attributes +lite.bind.topic=" + topic - + "); check qwen.managed-agent.event-transport.endpoints", - failure); - } - } - - // The subscription probe passes on a NORMAL topic too — a group binds to - // a topic whatever its message type — and every envelope would then be - // refused at send time. One synchronous send to the probe's own lite - // topic is refused by a topic that is not LITE, so that misconfiguration - // fails startup instead. Nothing consumes the probe lite topic beyond - // the subscription probe's own brief subscribe, whose listener only - // acknowledges; the broker expires it. - private static void verifyTopicAcceptsLiteMessages( - ClientServiceProvider provider, Producer producer, String topic) { - try { - producer.send(provider.newMessageBuilder() - .setTopic(topic) - .setLiteTopic(CAPABILITY_PROBE_LITE) - .setKeys(CAPABILITY_PROBE_LITE) - .setBody("{}".getBytes(java.nio.charset.StandardCharsets.UTF_8)) - .build()); - } catch (ClientException | RuntimeException failure) { - throw new IllegalStateException("the event-transport topic " - + topic + " refused a LiteTopic message: create it as a" - + " LITE topic (mqadmin updateTopic -c -t " - + topic + " -a +message.type=LITE)", failure); - } - } -} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentConfiguration.java b/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentConfiguration.java deleted file mode 100644 index 8338a927634..00000000000 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentConfiguration.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.alibaba.qwen.code.managedagent.config; - -import com.alibaba.qwen.code.managedagent.ManagedAgentServerApplication; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.context.annotation.Import; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.transaction.annotation.EnableTransactionManagement; - -/** Explicitly imports the control plane into the caller's application context, without servlet endpoints. */ -@Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties(ManagedAgentProperties.class) -@EnableScheduling -@EnableTransactionManagement(proxyTargetClass = true) -@Import({ManagedAgentConfiguration.PersistenceImport.class, ExecutionConfiguration.class, HarnessConfiguration.class, - RuntimeBrokerConfiguration.class, RuntimeRetentionConfiguration.class, ManagedArtifactConfiguration.class, - ToolPublicationConfiguration.class, EventTransportConfiguration.class, AliyunToolPublicationStorageAutoConfiguration.class}) -@ComponentScan(basePackages = "com.alibaba.qwen.code.managedagent", excludeFilters = { - @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Configuration.class, - org.springframework.stereotype.Controller.class, org.springframework.web.bind.annotation.ControllerAdvice.class}), - @ComponentScan.Filter(type = FilterType.CUSTOM, classes = org.springframework.boot.context.TypeExcludeFilter.class), - @ComponentScan.Filter(type = FilterType.CUSTOM, classes = org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter.class), - @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com\\.alibaba\\.qwen\\.code\\.managedagent\\.api\\..*"), - @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = { - ManagedAgentServerApplication.class, ManagedAgentWebConfiguration.class, - ManagedAgentInternalConfiguration.class, ManagedSchemaConfiguration.class, - ManagedLocalConfiguration.class, WebConfiguration.class, ManagedSessionJsonConfiguration.class, - InternalSurfaceConfiguration.class, InternalSurfaceConfiguration.RoutingFilter.class, - BrokerSecurity.class - }) -}) -public class ManagedAgentConfiguration { - public ManagedAgentConfiguration(ManagedAgentProperties properties) { - BrokerSecurity.checkPublicationTransport(properties, new java.util.ArrayList<>()); - } - - static class PersistenceImport implements org.springframework.context.annotation.DeferredImportSelector { - @Override - public String[] selectImports(org.springframework.core.type.AnnotationMetadata metadata) { - return new String[] {ManagedAgentPersistenceConfiguration.class.getName()}; - } - } -} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionMain.java b/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionMain.java deleted file mode 100644 index a405b376b1b..00000000000 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionMain.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.alibaba.qwen.code.managedagent.persistence; - -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.model.RequestDigests; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.fasterxml.jackson.core.StreamReadFeature; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.json.JsonMapper; -import org.apache.commons.lang3.StringUtils; -import org.springframework.jdbc.core.ConnectionCallback; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.datasource.DataSourceTransactionManager; -import org.springframework.jdbc.datasource.DataSourceUtils; -import org.springframework.jdbc.datasource.DriverManagerDataSource; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.support.TransactionSynchronizationManager; -import org.springframework.transaction.support.TransactionTemplate; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.Clock; -import java.util.Map; - -/** Offline private CREATE only; no worker, Harness, or public CSI selection. */ -public final class WorkspaceCsiSessionMain { - private WorkspaceCsiSessionMain() { - } - - public record Request(WorkspaceCsiRegistration registration, String actorId, - String idempotencyKey, String requestedRevision, String title, WorkspaceSelection workspace) { - public Request { - if (registration == null || workspace == null || !".".equals(workspace.cwdRelative()) - || actorId == null || actorId.isEmpty() - || idempotencyKey == null || !idempotencyKey.matches("[\\x21-\\x7e]{1,128}") - || requestedRevision != null && (requestedRevision.isBlank() || requestedRevision.length() > 128) - || title != null && title.length() > 512) { - throw new IllegalArgumentException("Invalid private CSI Session creation request"); - } - ManagedWorkspaceRegistry.actorKey(registration.tenantId(), actorId); - } - } - - public record Created(String sessionId, String runtimeRequestKey, boolean replayed) { - } - - public static void main(String[] args) throws Exception { - if (args.length != 2 || !"create".equals(args[0])) { - throw new IllegalArgumentException("Usage: create "); - } - ObjectMapper json = JsonMapper.builder().enable(StreamReadFeature.STRICT_DUPLICATE_DETECTION) - .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) - .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS).build(); - Request request = readRequest(Path.of(args[1]), json); - var source = new DriverManagerDataSource(required("K2_JDBC_URL"), required("K2_JDBC_USER"), - required("K2_JDBC_PASSWORD")); - var properties = new ManagedAgentProperties(); - properties.setAgentRevision(required("K2_AGENT_REVISION")); - Created created = create(new ManagedSchemaJdbcTemplate(source), new DataSourceTransactionManager(source), - json, properties, request); - System.out.println(json.writeValueAsString(created)); - } - - static Request readRequest(Path path, ObjectMapper json) { - try (var input = Files.newInputStream(path)) { - byte[] bytes = input.readNBytes(32 * 1024 + 1); - if (bytes.length > 32 * 1024) { - throw new IllegalArgumentException("CSI Session request exceeds its size limit"); - } - Request request = json.readValue(bytes, Request.class); - if (request == null) { - throw new IllegalArgumentException("CSI Session request is required"); - } - return request; - } catch (Exception error) { - throw new IllegalArgumentException("CSI Session request could not be read"); - } - } - - static Created create(JdbcTemplate jdbc, PlatformTransactionManager manager, - ObjectMapper json, ManagedAgentProperties properties, Request request) { - if (TransactionSynchronizationManager.isActualTransactionActive()) { - throw new IllegalStateException("Private CSI CREATE requires a fresh transaction"); - } - var transaction = new TransactionTemplate(manager); - transaction.setTimeout(10); - var reservations = new WorkspaceCsiReservationStore(jdbc, manager, json); - var sessions = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); - String digest = new RequestDigests().digest(Map.of("profile", CsiFilesRetirementProfile.PROFILE, - "request", request)); - return transaction.execute(status -> { - jdbc.execute((ConnectionCallback) connection -> { - if (connection.getAutoCommit() || !DataSourceUtils.isConnectionTransactional( - DataSourceUtils.getTargetConnection(connection), jdbc.getDataSource())) { - throw new IllegalStateException("Private CSI CREATE requires its original transaction connection"); - } - JdbcRuntimeBindingRepository.lockPlacementDomain(connection, request.registration().tenantId(), 10); - return null; - }); - ToolPublicationRetentionStore.lockTenant(jdbc, request.registration().tenantId()); - reservations.verifyRegistration(request.registration()); - var admission = sessions.insertCsiSessionCommand(request.registration(), request.actorId(), - request.idempotencyKey(), digest, request.requestedRevision(), request.title(), request.workspace()); - var original = sessions.requireCsiRequest(request.registration(), admission.sessionId()); - return new Created(admission.sessionId(), original.requestKey(), admission.replayed()); - }); - } - - private static String required(String name) { - String value = System.getenv(name); - if (StringUtils.isBlank(value)) { - throw new IllegalStateException(name + " is required"); - } - return value; - } -} diff --git a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryMain.java b/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryMain.java deleted file mode 100644 index db9065b051f..00000000000 --- a/packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryMain.java +++ /dev/null @@ -1,163 +0,0 @@ -package com.alibaba.qwen.code.managedagent.persistence; - -import com.alibaba.qwen.code.managedagent.artifact.AliyunToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount; -import com.aliyun.oss.ClientBuilderConfiguration; -import com.aliyun.oss.OSS; -import com.aliyun.oss.OSSClientBuilder; -import com.aliyun.oss.common.auth.CredentialsProviderFactory; -import com.aliyun.oss.common.comm.SignVersion; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.springframework.jdbc.datasource.DataSourceTransactionManager; -import org.springframework.jdbc.datasource.SingleConnectionDataSource; - -import java.io.BufferedInputStream; -import java.io.BufferedWriter; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.OutputStreamWriter; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.Path; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceRecoveryStore.JSON; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceRecoveryStore.check; -import static com.alibaba.qwen.code.managedagent.persistence.WorkspaceRecoveryStore.text; - -/** Private offline entry; it never boots the application or a Runtime. */ -public final class WorkspaceRecoveryMain { - private WorkspaceRecoveryMain() { - } - - public static void main(String[] args) throws Exception { - check(args.length >= 2 && Set.of("capture", "verify", "inspect").contains(args[0]) - && ("inspect".equals(args[0]) ? args.length == 2 - : args.length == 3 && "--offline-confirmed".equals(args[2])), "invalid_arguments"); - Path input = Path.of(args[1]); - check(Files.isRegularFile(input, LinkOption.NOFOLLOW_LINKS) && Files.size(input) <= 1024 * 1024, - "invalid_request_file"); - byte[] bytes = Files.readAllBytes(input); - JsonNode request = WorkspaceRecoveryStore.parse(bytes); - OSS oss = null; - // The private RPC loop is single-threaded; transactions still commit separately. - try (var source = new SingleConnectionDataSource(required("W1_JDBC_URL"), required("W1_JDBC_USER"), - requiredPresent("W1_JDBC_PASSWORD"), true)) { - var jdbc = new ManagedSchemaJdbcTemplate(source); - var manager = new DataSourceTransactionManager(source); - var properties = new ManagedAgentProperties(); - properties.getRuntimeBroker().setVerifiedWorkspaceRecoveryEnabled(true); - if (!"inspect".equals(args[0])) { - properties.getRuntimeBroker().setWorkspaceMounts(List.of( - new WorkspaceMount(text(request, "tenantId"), text(request, "storageId"), text(request, "sourceRoot")))); - } - ToolPublicationObjectStore objects = null; - if ("capture".equals(args[0]) && System.getenv("W1_OSS_ENDPOINT") != null) { - URI endpoint = URI.create(required("W1_OSS_ENDPOINT")); - String region = required("W1_OSS_REGION"); - check("https".equals(endpoint.getScheme()) && ("oss-" + region + ".aliyuncs.com").equals(endpoint.getHost()) - && (endpoint.getRawPath() == null || endpoint.getRawPath().isEmpty()) - && endpoint.getRawQuery() == null && endpoint.getRawUserInfo() == null, "invalid_oss_endpoint"); - var settings = new ClientBuilderConfiguration(); - settings.setSignatureVersion(SignVersion.V4); - AliyunToolPublicationObjectStore.configureClientRetries(settings); - oss = OSSClientBuilder.create().endpoint(endpoint.toString()).region(region) - .credentialsProvider(CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider()) - .clientConfiguration(settings).build(); - objects = new AliyunToolPublicationObjectStore(oss, required("W1_OSS_BUCKET")); - } - var store = new WorkspaceRecoveryStore(jdbc, manager, new WorkspaceStorageGuard(jdbc, manager, properties), - objects, args[0], bytes); - JsonNode existing = store.inspect(); - if ("inspect".equals(args[0]) || Set.of("SEALED", "VERIFIED").contains(existing.path("state").asText())) { - System.out.println(existing); - return; - } - executeWorker(request, store, store::call); - System.out.println(store.inspect()); - } finally { - if (oss != null) { - oss.shutdown(); - } - } - } - - public static void executeWorker(JsonNode request, WorkspaceRecoveryStore store, - java.util.function.BiFunction calls) throws Exception { - var command = new ProcessBuilder(text(request, "nodeExecutable"), text(request, "cliEntry"), - "--workspace-recovery-worker").redirectError(ProcessBuilder.Redirect.INHERIT); - command.environment().keySet().removeIf(key -> key.startsWith("W1_") || key.startsWith("OSS_")); - Process worker; - try { - worker = command.start(); - } catch (java.io.IOException error) { - store.workerFailed(); - throw error; - } - try (var output = new BufferedInputStream(worker.getInputStream()); - var replies = new BufferedWriter(new OutputStreamWriter(worker.getOutputStream(), StandardCharsets.UTF_8))) { - long lastId = 0; - byte[] line; - while ((line = message(output)) != null) { - JsonNode call = WorkspaceRecoveryStore.parse(line); - long callId = WorkspaceRecoveryStore.positive(call, "id"); - check(callId == lastId + 1, "worker_protocol_error"); - lastId = callId; - ObjectNode response = JSON.createObjectNode().put("id", callId); - try { - response.set("result", calls.apply(text(call, "method"), call.path("params"))); - } catch (RuntimeException error) { - String code = error instanceof WorkspaceRecoveryStore.RecoveryFailure failure - ? failure.code : "recovery_read_failed"; - if (!(error instanceof WorkspaceRecoveryStore.RecoveryFailure)) { - store.workerFailed(); - } - response.putObject("error").put("code", code).put("message", "Workspace recovery: " + code); - } - replies.write(response.toString()); - replies.newLine(); - replies.flush(); - } - check(worker.waitFor(30, TimeUnit.SECONDS) && worker.exitValue() == 0, "worker_failed"); - JsonNode receipt = store.inspect(); - check(Set.of("SEALED", "VERIFIED").contains(receipt.path("state").asText()), "worker_incomplete"); - } catch (Exception error) { - store.workerFailed(); - throw error; - } finally { - if (worker.isAlive()) { - worker.destroyForcibly(); - } - } - } - - private static byte[] message(InputStream input) throws java.io.IOException { - var bytes = new ByteArrayOutputStream(); - int next; - while ((next = input.read()) != -1 && next != '\n') { - check(bytes.size() < 16 * 1024 * 1024, "worker_message_too_large"); - bytes.write(next); - } - check(next != -1 || bytes.size() == 0, "worker_protocol_error"); - return next == -1 ? null : bytes.toByteArray(); - } - - private static String required(String name) { - String value = requiredPresent(name); - check(!value.isBlank(), "missing_environment"); - return value; - } - - private static String requiredPresent(String name) { - String value = System.getenv(name); - check(value != null, "missing_environment"); - return value; - } -} diff --git a/packages/managed-agent-server/verification/pom.xml b/packages/managed-agent-server/verification/pom.xml new file mode 100644 index 00000000000..ee261e9387a --- /dev/null +++ b/packages/managed-agent-server/verification/pom.xml @@ -0,0 +1,731 @@ + + 4.0.0 + + com.alibaba + qwen-managed-agent-parent + 0.1.0-alpha + ../pom.xml + + qwen-managed-agent-verification + Qwen Managed Agent Verification + + ${project.basedir}/.. + ${managed.module.root}/../../dist/cli.js + true + true + + + + com.alibaba + qwen-managed-agent-contracts + + + com.alibaba + qwen-managed-agent-control-plane + + + com.alibaba + qwen-managed-agent-runtime-broker + + + com.alibaba + qwen-managed-agent-persistence + + + com.alibaba + qwen-managed-agent-adapters + + + com.alibaba + qwen-managed-agent-server + + + com.aliyun.oss + aliyun-sdk-oss + test + + + org.apache.rocketmq + rocketmq-client-java + test + + + com.alibaba + qwencode-sdk + test + + + com.alibaba.fastjson2 + fastjson2 + test + + + com.alibaba + druid + test + + + com.mysql + mysql-connector-j + test + + + com.h2database + h2 + test + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.platform + junit-platform-launcher + test + + + com.networknt + json-schema-validator + test + + + com.tngtech.archunit + archunit + test + + + org.flywaydb + flyway-core + test + + + org.flywaydb + flyway-mysql + test + + + + + + src/test/resources + + + ../server/src/main/resources + + application*.yml + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + true + fault-gate + + ${managed.module.root} + ${qwen.cli.entry} + + + present + present + present + present + present + present + + true + + + + artifact-packaging + integration-test + + test + + + + **/ManagedAgentPackagingIT.java + + + ${project.version} + ${managed.operator-tools.attach} + ${managed.module.root}/server/target + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + ${managed.module.root} + ${qwen.cli.entry} + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + + prepare-agent + + + + report + verify + + report-aggregate + + + + + + + + + fault-gates + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + 600 + fault-gate + + ${managed.module.root} + ${qwen.cli.entry} + + + + + + + + mysql-integration + + 1 + + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/Hosted*IT.java + **/ManagedAgentPackagingIT.java + + ${mysql.integration.forks} + true + + ${mysql.integration.forks} + 0${surefire.forkNumber} + + + + junit.jupiter.execution.parallel.enabled = false + + + + + + + integration-test + verify + + + + + + + + + hosted-harness-mysql + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/Hosted*IT.java + + true + 1 + + + junit.jupiter.extensions.autodetection.enabled = true + junit.jupiter.execution.parallel.enabled = false + qwen.hosted.mysql.isolation = true + + + 11444 + + + + + integration-test + verify + + + + + + + + + hosted-harness-gates + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/Hosted*Gate.java + + true + + true + + 2400 + + + + + integration-test + verify + + + + + + + + + hosted-process-crashes + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/HostedProcessCrashIT.java + + true + 720 + + + + + integration-test + verify + + + + + + + + + hosted-workspace-tools + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/HostedWorkspaceToolTurnIT.java + + true + 300 + + + + + integration-test + verify + + + + + + + + + hosted-channel-gates + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/HostedChannel*IT.java + + true + 1200 + + + + + integration-test + verify + + + + + + + + + hosted-monitor-run-gates + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/HostedMonitorRun*IT.java + + true + 3600 + + + + + integration-test + verify + + + + + + + + + hosted-session-relay-gates + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/HostedSessionRelay*IT.java + + true + 2100 + + + + + integration-test + verify + + + + + + + + + hosted-workflow-gates + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/HostedWorkflow*IT.java + + true + 1800 + + + + + integration-test + verify + + + + + + + + + hosted-mcp-gates + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/HostedMcp*IT.java + + true + 1200 + + + + + integration-test + verify + + + + + + + + + hosted-automation-gates + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/HostedAutomation*IT.java + + true + 1200 + + + + + integration-test + verify + + + + + + + + + o4-mysql-gates + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/O4MySqlGate.java + + true + true + @{argLine} -Xmx256m + + true + + 600 + + + + + integration-test + verify + + + + + + + + + o4-oss-gates + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/O4OssGate.java + + true + true + @{argLine} -Xmx256m + + true + + 5400 + + + + + integration-test + verify + + + + + + + + + event-transport-broker-gates + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/EventTransportBrokerGate.java + + true + true + 900 + + + + + integration-test + verify + + + + + + + + + event-transport-multi-node-gates + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + **/EventTransportMultiNodeGate.java + + true + true + 3000 + + + + + integration-test + verify + + + + + + + + + central-release + + + + org.sonatype.central + central-publishing-maven-plugin + + true + + + + org.apache.maven.plugins + maven-source-plugin + + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + org.apache.maven.plugins + maven-gpg-plugin + + true + + + + + + + diff --git a/packages/managed-agent-server/src/test/embedding-host/pom.xml b/packages/managed-agent-server/verification/src/test/embedding-host/pom.xml similarity index 100% rename from packages/managed-agent-server/src/test/embedding-host/pom.xml rename to packages/managed-agent-server/verification/src/test/embedding-host/pom.xml diff --git a/packages/managed-agent-server/src/test/embedding-host/src/test/java/example/EmbeddingHostTest.java b/packages/managed-agent-server/verification/src/test/embedding-host/src/test/java/example/EmbeddingHostTest.java similarity index 100% rename from packages/managed-agent-server/src/test/embedding-host/src/test/java/example/EmbeddingHostTest.java rename to packages/managed-agent-server/verification/src/test/embedding-host/src/test/java/example/EmbeddingHostTest.java diff --git a/packages/managed-agent-server/src/test/embedding-host/src/test/java/example/EnabledHostTest.java b/packages/managed-agent-server/verification/src/test/embedding-host/src/test/java/example/EnabledHostTest.java similarity index 100% rename from packages/managed-agent-server/src/test/embedding-host/src/test/java/example/EnabledHostTest.java rename to packages/managed-agent-server/verification/src/test/embedding-host/src/test/java/example/EnabledHostTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/browserfixture/WorkspaceBrowserFixtureMain.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/browserfixture/WorkspaceBrowserFixtureMain.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/browserfixture/WorkspaceBrowserFixtureMain.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/browserfixture/WorkspaceBrowserFixtureMain.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/maintenance/WorkspaceRecoveryApplicationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/maintenance/WorkspaceRecoveryApplicationTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/maintenance/WorkspaceRecoveryApplicationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/maintenance/WorkspaceRecoveryApplicationTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ActionJournal.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ActionJournal.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ActionJournal.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ActionJournal.java index a4c1840a366..b402dec553a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ActionJournal.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ActionJournal.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/AliyunToolPublicationObjectStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/AliyunToolPublicationObjectStoreTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/AliyunToolPublicationObjectStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/AliyunToolPublicationObjectStoreTest.java index 8e012c6b6fb..05020e78f38 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/AliyunToolPublicationObjectStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/AliyunToolPublicationObjectStoreTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.artifact.AliyunToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationStorageException; +import com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationStorageException; import com.aliyun.oss.ClientException; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSException; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/AutomationHarnessFake.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/AutomationHarnessFake.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/AutomationHarnessFake.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/AutomationHarnessFake.java index ab76fc36163..0dec7938d48 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/AutomationHarnessFake.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/AutomationHarnessFake.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.daemon.DaemonHttpException; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -8,7 +8,6 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.support.TransactionTemplate; -import java.lang.reflect.Constructor; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.ArrayDeque; @@ -246,7 +245,7 @@ public final class AutomationHarnessFake { throw failure; } String occurrenceKey = String.valueOf(body.get("occurrenceKey")); - String runId = AutomationLedgerStore.automationRunId(scheduleId, + String runId = com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationRunId(scheduleId, occurrenceKey); // The order the funnel keeps: a committed run answers // first — whatever the definition did since. @@ -258,8 +257,7 @@ public final class AutomationHarnessFake { // delivery again. boolean gated = Boolean.TRUE.equals(answered.remove("gated")); result.put("run", answered); - result.put("inputId", AutomationLedgerStore - .automationInputId(runId)); + result.put("inputId", com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationInputId(runId)); result.put("replayed", true); if (gated) { result.put("gate", Map.of("outcome", "delivered")); @@ -326,8 +324,7 @@ public final class AutomationHarnessFake { Map answered = new LinkedHashMap<>(run); answered.remove("gated"); result.put("run", answered); - result.put("inputId", AutomationLedgerStore - .automationInputId(runId)); + result.put("inputId", com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationInputId(runId)); result.put("replayed", false); if (gate != null) { result.put("gate", gate); @@ -340,7 +337,7 @@ public final class AutomationHarnessFake { throw failure; } String occurrenceKey = String.valueOf(body.get("occurrenceKey")); - String runId = AutomationLedgerStore.automationRunId(scheduleId, + String runId = com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationRunId(scheduleId, occurrenceKey); Map run = runs.get(sessionId + "|" + runId); boolean repaired = false; @@ -448,16 +445,9 @@ public final class AutomationHarnessFake { } /** The daemon client's HTTP refusal, as the hosted route answers it. */ - public static DaemonHttpException refusal(int status, String code) { - try { - Constructor constructor = DaemonHttpException.class - .getDeclaredConstructor(String.class, int.class, String.class); - constructor.setAccessible(true); - return constructor.newInstance( - "POST /session/:id/automations/operations", status, - "{\"error\":\"" + code + "\",\"code\":\"" + code + "\"}"); - } catch (ReflectiveOperationException error) { - throw new IllegalStateException(error); - } + public static HarnessHttpFailure refusal(int status, String code) { + String body = "{\"error\":\"" + code + "\",\"code\":\"" + code + "\"}"; + return new HarnessHttpFailure("POST /session/:id/automations/operations failed with HTTP " + status + + ": " + body, null, status, body, Map.of("error", code, "code", code)); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/BackfillWitnessesTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/BackfillWitnessesTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/BackfillWitnessesTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/BackfillWitnessesTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/BrokerSignatures.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/BrokerSignatures.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/BrokerSignatures.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/BrokerSignatures.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ChannelContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ChannelContractTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ChannelContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ChannelContractTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ChildResultRelayStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ChildResultRelayStoreTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ChildResultRelayStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ChildResultRelayStoreTest.java index d7ae455e1de..11c50a0370b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ChildResultRelayStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ChildResultRelayStoreTest.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels; import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore; -import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore.PendingChild; -import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore.RelayRow; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.repository.ChildResultRelayStore.PendingChild; +import com.alibaba.qwen.code.managedagent.repository.ChildResultRelayStore.RelayRow; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -273,8 +273,8 @@ class ChildResultRelayStoreTest { + " delivery_state, gmt_create)" + " VALUES (?, ?, ?, 'workspace', ?, 'child_run', ?," + " 'h', 1, ?, ?, ?, ?, ?, 1)", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey(TENANT, sessionId), - com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection.recordKey(sessionId, "child_run", recordId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, sessionId), + com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.recordKey(sessionId, "child_run", recordId), TENANT, sessionId, recordId, "resource-" + recordId, "shell".equals(kind) ? "background_shell" : kind, taskState, @@ -591,10 +591,10 @@ class ChildResultRelayStoreTest { } private void resource(String sessionId, String resourceId, String kind, String body) { - String scope = com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey(TENANT, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, sessionId); byte[] bytes = body.getBytes(java.nio.charset.StandardCharsets.UTF_8); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create) VALUES (?, ?, ?, 'workspace', ?, ?, ?, 1, ?, ?, 'MYSQL_INLINE', ?, 'command', 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ")", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(scope, resourceId), scope, TENANT, sessionId, + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scope, resourceId), scope, TENANT, sessionId, resourceId, kind, bytes.length, "a".repeat(64), bytes); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ContainerArtifactSelectionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ContainerArtifactSelectionTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ContainerArtifactSelectionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ContainerArtifactSelectionTest.java index 2c4f53a5136..f7d64c278e0 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ContainerArtifactSelectionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ContainerArtifactSelectionTest.java @@ -33,7 +33,7 @@ class ContainerArtifactSelectionTest { } private int select() throws Exception { - String dockerfile = Files.readString(Path.of("Dockerfile")); + String dockerfile = Files.readString(VerificationFixtures.moduleRoot().resolve("Dockerfile")); String start = "main=\"\" && count=0"; String end = "&& cp \"$main\" /tmp/qwen-managed-agent-server.jar"; int from = dockerfile.indexOf(start); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/EmbeddedRuntimeBrokerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/EmbeddedRuntimeBrokerTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/EmbeddedRuntimeBrokerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/EmbeddedRuntimeBrokerTest.java index c0a9541056a..11290d86473 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/EmbeddedRuntimeBrokerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/EmbeddedRuntimeBrokerTest.java @@ -1,15 +1,11 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; @@ -17,7 +13,10 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalRuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.Test; import java.net.HttpURLConnection; @@ -44,7 +43,7 @@ class EmbeddedRuntimeBrokerTest { assertThat(stepCallTimeoutMillis) .isGreaterThan(com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner.READY_TIMEOUT.toMillis()); assertThat(stepCallTimeoutMillis) - .isGreaterThan(com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport.REQUEST_TIMEOUT.toMillis()); + .isGreaterThan(com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport.REQUEST_TIMEOUT.toMillis()); } private static final String SESSION_ID = @@ -450,7 +449,7 @@ class EmbeddedRuntimeBrokerTest { void constructsAnExternalScratchProvisionerForAValidConfiguration() throws Exception { ManagedAgentProperties properties = properties(); - properties.getRuntimeBroker().setProvisioner(ExternalRuntimeProvisioner.KIND); + properties.getRuntimeBroker().setProvisioner(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH); properties.getRuntimeBroker().setIsolationClass("session"); properties.getRuntimeBroker().setExternalProviderUrl("http://127.0.0.1:9"); properties.getRuntimeBroker().setExternalProviderToken("provider-token-for-tests"); @@ -458,7 +457,7 @@ class EmbeddedRuntimeBrokerTest { try (EmbeddedRuntimeBroker broker = broker(mock(ManagedAgentStore.class), properties)) { assertThat(provisionerOf(broker).kind()) - .isEqualTo(ExternalRuntimeProvisioner.KIND); + .isEqualTo(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH); } } @@ -466,7 +465,7 @@ class EmbeddedRuntimeBrokerTest { void refusesAnExternalScratchConfigurationWithoutAProviderUrl() throws Exception { ManagedAgentProperties properties = properties(); - properties.getRuntimeBroker().setProvisioner(ExternalRuntimeProvisioner.KIND); + properties.getRuntimeBroker().setProvisioner(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH); properties.getRuntimeBroker().setIsolationClass("session"); assertThatThrownBy(() -> broker(mock(ManagedAgentStore.class), @@ -590,7 +589,7 @@ class EmbeddedRuntimeBrokerTest { .isInstanceOf(IllegalStateException.class) .hasMessageContaining("need Git"); properties.getRuntimeBroker().setChildWorkspaceGit("git"); - var probe = new com.alibaba.qwen.code.managedagent.workspace.ChildWorktreeGit("git", + var probe = new com.alibaba.qwen.code.managedagent.adapters.workspace.ChildWorktreeGit("git", java.time.Duration.ofSeconds(30)); try { probe.requireSupportedVersion(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGate.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGate.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGate.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGate.java index 132becab201..d399a7ea94d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGate.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGate.java @@ -2,23 +2,24 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.EventTransportConfiguration; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.event.CommittedEventPublisher; +import com.alibaba.qwen.code.managedagent.contracts.event.CommittedEventPublisher; +import com.alibaba.qwen.code.managedagent.contracts.event.EventSubscriptions; +import com.alibaba.qwen.code.managedagent.contracts.event.JournalEventTransport; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelope; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelopes; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.AttachedSession; import com.alibaba.qwen.code.managedagent.event.CompositeCommittedEventPublisher; import com.alibaba.qwen.code.managedagent.event.EventTransportConsumer; -import com.alibaba.qwen.code.managedagent.event.JournalEventTransport; -import com.alibaba.qwen.code.managedagent.event.ManagedEventEnvelope; -import com.alibaba.qwen.code.managedagent.event.ManagedEventEnvelopes; import com.alibaba.qwen.code.managedagent.event.MessageMaterializer; import com.alibaba.qwen.code.managedagent.event.TransportWakeActivator; import com.alibaba.qwen.code.managedagent.event.TransportWakeActivator.Outcome; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.AttachedSession; import com.alibaba.qwen.code.managedagent.model.StoreModels.EventRecord; import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore; import com.alibaba.qwen.code.managedagent.session.SessionEventHub; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import jdk.jfr.Recording; @@ -142,11 +143,9 @@ class EventTransportBrokerGate { @Autowired private ManagedAgentProperties properties; - @Autowired private Producer producer; - @Autowired - private ClientServiceProvider provider; + private final ClientServiceProvider provider = ClientServiceProvider.loadService(); @Autowired private JournalEventTransport journalTransport; @@ -159,7 +158,7 @@ class EventTransportBrokerGate { @Autowired @Qualifier("eventTransportOwnerConsumer") - private LitePushConsumer ownerConsumer; + private EventSubscriptions ownerConsumer; private final Map> outcomes = new ConcurrentHashMap<>(); @@ -178,7 +177,12 @@ class EventTransportBrokerGate { } @BeforeEach - void recordWakeOutcomes() { + void recordWakeOutcomes() throws Exception { + producer = provider.newProducerBuilder() + .setClientConfiguration(clientConfiguration()) + .setTopics(properties.getEventTransport().getTopic()) + .build(); + opened.add(producer); when(harness.isAvailable()).thenReturn(true); when(harness.activateForWake(anyString(), anyString())).thenReturn(true); doAnswer(invocation -> { @@ -365,7 +369,7 @@ class EventTransportBrokerGate { LitePushConsumer stale = subscribe(owners, session, message -> { // Recorded after the node handled it, so the checks below see a // finished consume. - ConsumeResult answer = staleNode.consume(message); + ConsumeResult answer = com.alibaba.qwen.code.managedagent.adapters.event.RocketMqMessages.consume(staleNode, message); staleAnswers.add(answer); staleSaw.add(body(message)); return answer; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGateStatisticsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGateStatisticsTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGateStatisticsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGateStatisticsTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportMultiNodeGate.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportMultiNodeGate.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportMultiNodeGate.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportMultiNodeGate.java index 4e36c8a46be..6b0faf0555e 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportMultiNodeGate.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportMultiNodeGate.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelope; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelopes; import com.alibaba.qwen.code.managedagent.event.EventTransportNodeMain; -import com.alibaba.qwen.code.managedagent.event.ManagedEventEnvelope; -import com.alibaba.qwen.code.managedagent.event.ManagedEventEnvelopes; import com.alibaba.qwen.code.managedagent.persistence.IsolatedManagedSchemaMySqlTestDatabase; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; @@ -80,7 +80,7 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue; * alone: the nodes join the server's default groups and the oracle reads * the gate group, so a second run against it would split their lite topics. */ -class EventTransportMultiNodeGate { +public class EventTransportMultiNodeGate { private static final ObjectMapper JSON = new ObjectMapper(); private static final String TOKEN = "mq3-harness-token"; private static final String DIGEST = "sha256:" + "a".repeat(64); @@ -124,7 +124,7 @@ class EventTransportMultiNodeGate { private String tenant; @BeforeAll - static void startSharedServices() throws Exception { + public static void startSharedServices() throws Exception { for (String name : List.of("mysql.url", "mysql.user", "qwen.cli.entry", "qwen.gate.rocketmq.endpoints", "qwen.gate.rocketmq.topic", "qwen.gate.rocketmq.work-dir")) { @@ -180,7 +180,7 @@ class EventTransportMultiNodeGate { } @AfterAll - static void stopSharedServices() throws Exception { + public static void stopSharedServices() throws Exception { if (oracle != null) { oracle.close(); } @@ -197,7 +197,7 @@ class EventTransportMultiNodeGate { } @BeforeEach - void prepareCase(TestInfo test) throws IOException { + public void prepareCase(TestInfo test) throws IOException { tenant = "mq3-" + UUID.randomUUID(); caseRoot = Path.of("target", "mq3-multi-node", test.getTestMethod().orElseThrow().getName()).toAbsolutePath(); @@ -207,7 +207,7 @@ class EventTransportMultiNodeGate { } @AfterEach - void stopNodes() throws InterruptedException { + public void stopNodes() throws InterruptedException { if (!nodes.isEmpty()) { // Let acknowledgements leave first: a node killed holding an // unacknowledged message holds its lite topic for the broker's @@ -223,18 +223,16 @@ class EventTransportMultiNodeGate { // --- rows --- /** - * §8 duplicate, reorder and redelivery after a lost acknowledgement, with - * both nodes in the shared bootstrap group: a Session's committed wakes, - * re-sent to the lane after its writer lapsed — the newer one twice, then - * the older — activate it once, through a real Harness load. The load is - * held until every copy is decided, so each refusal is the claim - * watermark's, never a live writer's. Which node a copy lands on is the - * broker's choice; the concurrent claim race itself is pinned at the SQL - * level (EventTransportConsumerSqlTest). + * §8 duplicate, reorder and redelivery after a lost acknowledgement, with both nodes in the + * shared bootstrap group: a Session's committed wakes, re-sent to the lane after its writer + * lapsed — the newer one twice, then the older — activate it once, through a real Harness load. + * The load is held until every copy is decided, so each refusal is the claim watermark's, never + * a live writer's. Which node a copy lands on is the broker's choice; the concurrent claim race + * itself is pinned at the SQL level (EventTransportConsumerSqlTest). */ @Test @Timeout(240) - void wakesDeliveredAgainActivateTheSessionOnce() throws Exception { + public void wakesDeliveredAgainActivateTheSessionOnce() throws Exception { // The second node joins after the Turns: any node's dispatcher may // claim a fresh Turn, and the row needs a's Harness to hold the // Session it then frees. @@ -281,15 +279,14 @@ class EventTransportMultiNodeGate { } /** - * §8 consumer crash before processing: the node the broker hands the - * wake to halts before it reads anything, so nothing is claimed or - * acknowledged; the broker re-dispatches to the other member of the - * shared bootstrap group, which activates the Session. + * §8 consumer crash before processing: the node the broker hands the wake to halts before it + * reads anything, so nothing is claimed or acknowledged; the broker re-dispatches to the other + * member of the shared bootstrap group, which activates the Session. */ @Test @Timeout(360) - void aWakeWhoseReceiverCrashesBeforeProcessingActivatesOnTheOtherNode() - throws Exception { + public void + aWakeWhoseReceiverCrashesBeforeProcessingActivatesOnTheOtherNode() throws Exception { Node a = node("a").start(true); String session = createSession(a); turn(a, session, "MQ3_FIRST"); @@ -325,16 +322,16 @@ class EventTransportMultiNodeGate { } /** - * §8 consumer crash after a local effect, before the acknowledgement: - * the receiver claims the wake and halts before its listener returns. - * The redelivery reaches the other node and is refused on the watermark, - * so the Session is never loaded twice for one wake, and new input still - * runs on the survivor through its Turn. (That a claim never blocks a - * later wake is pinned at the SQL level, EventTransportConsumerSqlTest.) + * §8 consumer crash after a local effect, before the acknowledgement: the receiver claims the + * wake and halts before its listener returns. The redelivery reaches the other node and is + * refused on the watermark, so the Session is never loaded twice for one wake, and new input + * still runs on the survivor through its Turn. (That a claim never blocks a later wake is + * pinned at the SQL level, EventTransportConsumerSqlTest.) */ @Test @Timeout(360) - void aWakeWhoseReceiverCrashesAfterClaimingItIsNotLoadedTwice() throws Exception { + public void aWakeWhoseReceiverCrashesAfterClaimingItIsNotLoadedTwice() + throws Exception { Node a = node("a").start(true); String session = createSession(a); turn(a, session, "MQ3_FIRST"); @@ -373,16 +370,16 @@ class EventTransportMultiNodeGate { } /** - * §8 producer crash between the SQL commit and the publish, on the - * journal wire: the node whose Harness commits the Turn's input halts - * after the commit and before its wake reaches the broker. The wake is - * committed, never sent on either lane and never claimed; the Turn - * dispatcher — that input's own ledger — picks the Turn up on the other - * node once the dead node's leases lapse and settles it there, once. + * §8 producer crash between the SQL commit and the publish, on the journal wire: the node whose + * Harness commits the Turn's input halts after the commit and before its wake reaches the + * broker. The wake is committed, never sent on either lane and never claimed; the Turn + * dispatcher — that input's own ledger — picks the Turn up on the other node once the dead + * node's leases lapse and settles it there, once. */ @Test @Timeout(240) - void aWakeLostWithItsProducerLeavesTheTurnToItsLedger() throws Exception { + public void aWakeLostWithItsProducerLeavesTheTurnToItsLedger() + throws Exception { Node a = node("a").start(true); Node b = node("b").start(true); String session = createSession(a); @@ -410,17 +407,17 @@ class EventTransportMultiNodeGate { } /** - * §8 producer crash between the SQL commit and the publish, on the public - * wire: the node recording a Turn's last batch — the one carrying - * {@code turn.completed} — halts after committing it and before handing - * it to the broker. No later notice of the Session follows, so only the - * scheduled materialization pass, on the other node, can project that - * batch: the committed events are served with no gap, the projection - * covers them, and the items carry the input and the reply. + * §8 producer crash between the SQL commit and the publish, on the public wire: the node + * recording a Turn's last batch — the one carrying {@code turn.completed} — halts after + * committing it and before handing it to the broker. No later notice of the Session follows, so + * only the scheduled materialization pass, on the other node, can project that batch: the + * committed events are served with no gap, the projection covers them, and the items carry the + * input and the reply. */ @Test @Timeout(240) - void publicEventsLostWithTheirProducerAreStillServedFromSql() throws Exception { + public void publicEventsLostWithTheirProducerAreStillServedFromSql() + throws Exception { Node a = node("a").start(true); Node b = node("b").start(true); String session = createSession(a); @@ -458,14 +455,13 @@ class EventTransportMultiNodeGate { } /** - * §8 broker short outage: the proxy every client talks to is frozen while - * a Turn runs. The Turn completes, both hand-offs on the committing - * thread stay bounded — the broker is never on the commit path — and - * once the proxy is back the transport carries the next Turn's wake. + * §8 broker short outage: the proxy every client talks to is frozen while a Turn runs. The Turn + * completes, both hand-offs on the committing thread stay bounded — the broker is never on the + * commit path — and once the proxy is back the transport carries the next Turn's wake. */ @Test @Timeout(240) - void aShortBrokerOutageNeverReachesTheCommitPath() throws Exception { + public void aShortBrokerOutageNeverReachesTheCommitPath() throws Exception { Node a = node("a").start(true); Node b = node("b").start(true); String session = createSession(a); @@ -489,17 +485,15 @@ class EventTransportMultiNodeGate { } /** - * §8 broker long outage and flag rollback: with the proxy frozen past - * the client's own attempts, a running node keeps serving Turns with - * both hand-offs bounded; a node restarted with the flag on refuses to - * start (its startup probes need the broker), and the same node — the - * only one left — restarted with the flag off starts and serves. Once - * the broker is back, it restarts with the flag on and carries wakes - * again. + * §8 broker long outage and flag rollback: with the proxy frozen past the client's own + * attempts, a running node keeps serving Turns with both hand-offs bounded; a node restarted + * with the flag on refuses to start (its startup probes need the broker), and the same node — + * the only one left — restarted with the flag off starts and serves. Once the broker is back, + * it restarts with the flag on and carries wakes again. */ @Test @Timeout(360) - void aLongBrokerOutageLeavesTheFlagAsTheWayBack() throws Exception { + public void aLongBrokerOutageLeavesTheFlagAsTheWayBack() throws Exception { Node a = node("a").start(true); String session = createSession(a); turn(a, session, "MQ3_BEFORE_LONG_OUTAGE"); @@ -555,14 +549,13 @@ class EventTransportMultiNodeGate { } /** - * §8 flag rollback across the fleet: with every node restarted flag-off, - * Turns run exactly as before and nothing reaches the broker or the - * consumers; turned back on, the next Turn's wake is published again and - * the Session is served as before. + * §8 flag rollback across the fleet: with every node restarted flag-off, Turns run exactly as + * before and nothing reaches the broker or the consumers; turned back on, the next Turn's wake + * is published again and the Session is served as before. */ @Test @Timeout(300) - void theFleetRollsTheFlagBackAndForth() throws Exception { + public void theFleetRollsTheFlagBackAndForth() throws Exception { Node a = node("a").start(true); Node b = node("b").start(true); String session = createSession(a); @@ -609,14 +602,13 @@ class EventTransportMultiNodeGate { } /** - * §8 stale wake after a re-route: the Session moved from the node that - * committed a wake to another node, whose Harness now holds its journal. - * The old wake, delivered again, finds that live writer on whichever node - * it lands: no claim, no load, the writer unchanged. + * §8 stale wake after a re-route: the Session moved from the node that committed a wake to + * another node, whose Harness now holds its journal. The old wake, delivered again, finds that + * live writer on whichever node it lands: no claim, no load, the writer unchanged. */ @Test @Timeout(240) - void aStaleWakeAfterAReRouteIsANoOp() throws Exception { + public void aStaleWakeAfterAReRouteIsANoOp() throws Exception { Node a = node("a").start(true); String session = createSession(a); turn(a, session, "MQ3_ON_A"); @@ -649,22 +641,20 @@ class EventTransportMultiNodeGate { } /** - * §8 clock skew between Java nodes: the wake decisions read the database - * clock. A node whose JVM wall clock runs ten minutes ahead still sees - * another node's live writer as live, and one whose clock runs ten - * minutes behind still sees a lapsed writer as lapsed and activates the - * Session. The skew is libfaketime's, on the application JVM (its - * Harness keeps the host clock), and each skewed node proves it took - * effect; without libfaketime the row cannot test anything — the wake - * path reads no Clock bean — and is skipped. The node that holds the - * Session runs with the flag off, so the skewed node is the only member - * of the bootstrap lane, and no Turn runs while a skewed node is up: the - * Turn dispatch lease is stamped with the JVM clock, which the MQ3 design - * records as outside this row. + * §8 clock skew between Java nodes: the wake decisions read the database clock. A node whose + * JVM wall clock runs ten minutes ahead still sees another node's live writer as live, and one + * whose clock runs ten minutes behind still sees a lapsed writer as lapsed and activates the + * Session. The skew is libfaketime's, on the application JVM (its Harness keeps the host + * clock), and each skewed node proves it took effect; without libfaketime the row cannot test + * anything — the wake path reads no Clock bean — and is skipped. The node that holds the + * Session runs with the flag off, so the skewed node is the only member of the bootstrap lane, + * and no Turn runs while a skewed node is up: the Turn dispatch lease is stamped with the JVM + * clock, which the MQ3 design records as outside this row. */ @Test @Timeout(300) - void wakeDecisionsFollowTheDatabaseClockNotTheNode() throws Exception { + public void wakeDecisionsFollowTheDatabaseClockNotTheNode() + throws Exception { assumeTrue(!System.getProperty("qwen.gate.faketime.library", "").isBlank(), "the clock-skew row needs libfaketime (qwen.gate.faketime.library)"); Node a = node("a").start(false); @@ -1067,7 +1057,7 @@ class EventTransportMultiNodeGate { private interface Frozen extends AutoCloseable { @Override - void close() throws Exception; + public void close() throws Exception; } private static void signal(long pid, String signal) throws Exception { @@ -1150,17 +1140,17 @@ class EventTransportMultiNodeGate { /** One control-plane node: a packaged Hosted Harness and the application. */ private final class Node { - final String name; - final Path root; - Process harness; - Process spring; - int harnessPort; - int springPort; - String bootId; + public final String name; + public final Path root; + public Process harness; + public Process spring; + public int harnessPort; + public int springPort; + public String bootId; private Duration skew = Duration.ZERO; private final StringBuilder harnessOutput = new StringBuilder(); - Node(String name, Path root) { + public Node(String name, Path root) { this.name = name; this.root = root; } @@ -1171,19 +1161,19 @@ class EventTransportMultiNodeGate { * process's wall clock moves, monotonic time does not, and the * node's Harness keeps the host clock. */ - Node skewed(Duration skew) { + public Node skewed(Duration skew) { this.skew = skew; return this; } - Node start(boolean transport) throws Exception { + public Node start(boolean transport) throws Exception { Files.createDirectories(root); startHarness(); startSpring(transport); return this; } - void startHarness() throws Exception { + public void startHarness() throws Exception { Path home = Files.createDirectories(root.resolve("home")); Path config = Files.createDirectories(home.resolve(".qwen")); Path workspace = Files.createDirectories(root.resolve("workspace")); @@ -1288,7 +1278,7 @@ class EventTransportMultiNodeGate { } } - void startSpring(boolean transport) throws Exception { + public void startSpring(boolean transport) throws Exception { launch(transport); // Only the first boot of the class migrates the schema. Duration boot = Duration.ofSeconds(schemaReady ? 90 : 180); @@ -1310,7 +1300,7 @@ class EventTransportMultiNodeGate { } /** Starts the application and returns the log of a start that fails. */ - String startSpringExpectingFailure(boolean transport, Duration within) + public String startSpringExpectingFailure(boolean transport, Duration within) throws Exception { long offset = Files.exists(root.resolve("spring.log")) ? Files.size(root.resolve("spring.log")) : 0; @@ -1387,12 +1377,12 @@ class EventTransportMultiNodeGate { STARTED.add(spring); } - void crashSpring() { + public void crashSpring() { kill(spring); } /** Restarts the whole node: a new Harness, the same address. */ - void restart(boolean transport) throws Exception { + public void restart(boolean transport) throws Exception { crash(); synchronized (harnessOutput) { harnessOutput.setLength(0); @@ -1400,7 +1390,7 @@ class EventTransportMultiNodeGate { start(transport); } - void crash() { + public void crash() { kill(spring); kill(harness); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ExtensionRecordJournal.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ExtensionRecordJournal.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ExtensionRecordJournal.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ExtensionRecordJournal.java index 10a8996a0aa..e34f3c4df5a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ExtensionRecordJournal.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ExtensionRecordJournal.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitReceipt; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitReceipt; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -29,7 +29,7 @@ public final class ExtensionRecordJournal { private static final ObjectMapper JSON = new ObjectMapper(); private static final String WRITER = "writer-extension"; private static final String TOKEN = "extension-writer-token-0123456789"; - static final String OPERATION = "commitMonitorRun"; + public static final String OPERATION = "commitMonitorRun"; private final ManagedSessionStore store; private final String tenantId; private final String workspaceId; @@ -59,7 +59,7 @@ public final class ExtensionRecordJournal { } /** Acquires the writer and leaves the genesis to the test. */ - ExtensionRecordJournal acquire() { + public ExtensionRecordJournal acquire() { writerGeneration = store.acquireWriter(tenantId, sessionId, TOKEN, new AcquireWriterRequest(workspaceId, WRITER, 60_000L)) .writerGeneration(); @@ -67,7 +67,7 @@ public final class ExtensionRecordJournal { } /** The genesis transaction with two record lines. */ - CommitTransactionRequest genesis(String records, + public CommitTransactionRequest genesis(String records, List resources) { return new CommitTransactionRequest(workspaceId, WRITER, writerGeneration, 0, 0, "transaction-genesis", @@ -76,7 +76,7 @@ public final class ExtensionRecordJournal { sha256(records), resources); } - CommitReceipt commitMonitor(String commandId, JsonNode monitor, + public CommitReceipt commitMonitor(String commandId, JsonNode monitor, long occurredAt) { CommitTransactionRequest request = request(commandId, monitor, occurredAt); @@ -93,7 +93,7 @@ public final class ExtensionRecordJournal { * The commit request for one revision; committing the same request again * replays it. */ - CommitTransactionRequest request(String commandId, JsonNode monitor, + public CommitTransactionRequest request(String commandId, JsonNode monitor, long occurredAt) { return request(OPERATION, commandId, bytes(monitor), occurredAt, event -> {}, records -> records); @@ -104,7 +104,7 @@ public final class ExtensionRecordJournal { * lines a test may change, as a writer that does not follow the contract * would. */ - CommitTransactionRequest request(String operation, String commandId, + public CommitTransactionRequest request(String operation, String commandId, byte[] body, long occurredAt, Consumer editEvent, UnaryOperator editRecords) { return request(operation, commandId, body, occurredAt, editEvent, @@ -115,7 +115,7 @@ public final class ExtensionRecordJournal { * A commit request that declares {@code extraEvents} more events after * the Stage H one, whose lines {@code editRecords} supplies. */ - CommitTransactionRequest request(String operation, String commandId, + public CommitTransactionRequest request(String operation, String commandId, byte[] body, long occurredAt, Consumer editEvent, UnaryOperator editRecords, int extraEvents) { return request(operation, commandId, body, occurredAt, editEvent, @@ -145,7 +145,7 @@ public final class ExtensionRecordJournal { * does not follow the contract might bundle more than one. A repeated * id still gets its own event and turn. */ - CommitTransactionRequest requestDomainWithInputs(String commandId, + public CommitTransactionRequest requestDomainWithInputs(String commandId, String domain, JsonNode body, List resources, List inputIds, CommitResource input) { ObjectNode ref = JSON.createObjectNode() @@ -203,14 +203,14 @@ public final class ExtensionRecordJournal { * An ordinary body-less domain commit, open to the same edits as a * Stage H one, as a writer that does not follow the contract would. */ - CommitTransactionRequest requestOrdinary(String commandId, String domain, + public CommitTransactionRequest requestOrdinary(String commandId, String domain, JsonNode body, Consumer editEvent, UnaryOperator editRecords, int extraEvents) { return request("commitDomainRecord", commandId, bytes(body), 1_000, editEvent, editRecords, extraEvents, domain, List.of()); } - CommitTransactionRequest request(String operation, String commandId, + public CommitTransactionRequest request(String operation, String commandId, byte[] body, long occurredAt, Consumer editEvent, UnaryOperator editRecords, int extraEvents, String domain, List resources) { @@ -300,16 +300,16 @@ public final class ExtensionRecordJournal { domainEvents++; } - long committedSequence() { + public long committedSequence() { return sequence; } /** The resource that holds a body; equal bodies share one. */ - static String resourceId(byte[] body) { + public static String resourceId(byte[] body) { return UUID.nameUUIDFromBytes(body).toString(); } - static byte[] bytes(JsonNode node) { + public static byte[] bytes(JsonNode node) { try { return JSON.writeValueAsBytes(node); } catch (JsonProcessingException error) { @@ -319,7 +319,7 @@ public final class ExtensionRecordJournal { /** One record line in the envelope the authority writes; shared by * every test that composes a line, so helpers cannot drift apart. */ - static String line(String sessionId, String subtype, JsonNode body) { + public static String line(String sessionId, String subtype, JsonNode body) { ObjectNode record = JSON.createObjectNode() .put("uuid", UUID.randomUUID().toString()) .putNull("parentUuid") @@ -338,11 +338,11 @@ public final class ExtensionRecordJournal { value.getBytes(StandardCharsets.UTF_8)); } - static String sha256(String value) { + public static String sha256(String value) { return sha256(value.getBytes(StandardCharsets.UTF_8)); } - static String sha256(byte[] value) { + public static String sha256(byte[] value) { try { return HexFormat.of().formatHex( MessageDigest.getInstance("SHA-256").digest(value)); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HarnessEventProjectorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HarnessEventProjectorTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HarnessEventProjectorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HarnessEventProjectorTest.java index 211a4a20d6d..5f30a400b6f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HarnessEventProjectorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HarnessEventProjectorTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.SourceEvent; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.SourceEvent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.EventIdentity; import com.alibaba.qwen.code.managedagent.model.StoreModels.ProjectedEvent; -import com.alibaba.qwen.code.managedagent.persistence.EventIdentity; import com.alibaba.qwen.code.managedagent.session.HarnessEventProjector; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HookAdmissionHistory.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HookAdmissionHistory.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HookAdmissionHistory.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HookAdmissionHistory.java index e34eeb926f6..afa081b873a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HookAdmissionHistory.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HookAdmissionHistory.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -66,7 +66,7 @@ final class HookAdmissionHistory { inTransaction( () -> new ExtensionRecordJournal( - new ManagedSessionStore(jdbc), + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)), tenantId, workspaceId, sessionId) diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationCrashFixtureMain.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationCrashFixtureMain.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationCrashFixtureMain.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationCrashFixtureMain.java index 373c9174685..5ca58d8f2b2 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationCrashFixtureMain.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationCrashFixtureMain.java @@ -7,7 +7,7 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.automation.AutomationScanner; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationFaultGatesIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationFaultGatesIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationFaultGatesIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationFaultGatesIT.java index 59b84f26b6e..a6ab2b07b39 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationFaultGatesIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationFaultGatesIT.java @@ -443,7 +443,7 @@ class HostedAutomationFaultGatesIT { String session = createSession("claim-crash"); String id = create(session, "FG11C_CLAIM", "0 0 1 1 *", "claim-crash-base"); String key = "claim-crash-run"; - String runId = com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.automationRunId(id, + String runId = com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationRunId(id, "manual:" + key); Hold hold = storeHold("claim-crash", body -> (runId + ":2").equals(body.path("commandId").asText())); var answer = java.util.concurrent.CompletableFuture.supplyAsync(() -> { @@ -509,7 +509,7 @@ class HostedAutomationFaultGatesIT { ThreadPoolTaskScheduler.class), Duration.ofSeconds(30)); String id = create(session, "FG11C_SETTLED", "* * * * *", "settled-crash-base"); String key = "settled-crash-run"; - String runId = com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.automationRunId(id, + String runId = com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationRunId(id, "manual:" + key); // Arm the hold before the POST: the Harness kicks the wake pump before answering // fire_run, so revision 3 can otherwise reach the store before the hold exists. @@ -592,7 +592,7 @@ class HostedAutomationFaultGatesIT { String session = createSession("transport"); String id = create(session, "FG11B_TRANSPORT", "0 0 1 1 *", "transport-base"); String key = "transport-run"; - String runId = com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.automationRunId(id, + String runId = com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationRunId(id, "manual:" + key); storeDrop.set(runId + ":1"); JsonNode run = request("POST", "/v1/agent-automations/" + id + "/runs", null, key, 202); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationSpringCrashIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationSpringCrashIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationSpringCrashIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedAutomationSpringCrashIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedBackgroundPublicationGate.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedBackgroundPublicationGate.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedBackgroundPublicationGate.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedBackgroundPublicationGate.java index 355269c226d..d2f4e431c6e 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedBackgroundPublicationGate.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedBackgroundPublicationGate.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.managedprobes.ProbeAdvice; import com.alibaba.qwen.managedprobes.PublicationProbeWiring; @@ -84,13 +84,13 @@ import static org.assertj.core.api.Assertions.assertThat; * the 400 envelope and the test log (production's handler is deliberately * generic; the wedge witness observes it as-is). */ -class HostedBackgroundPublicationGate { +public class HostedBackgroundPublicationGate { @TempDir private Path temporary; @Test @Timeout(1800) - void backgroundAndMonitorReservesAnswerOpen() throws Exception { + public void backgroundAndMonitorReservesAnswerOpen() throws Exception { org.junit.jupiter.api.Assumptions.assumeTrue( "true".equals(System.getProperty("qwen.wedge.probe")), "background publication witness: skipped unless -Dqwen.wedge.probe=true"); @@ -106,7 +106,7 @@ class HostedBackgroundPublicationGate { .toAbsolutePath().normalize(); assertThat(cli).as("CLI entry -Dqwen.cli.entry=%s", cli).isRegularFile(); Path moduleDir = Path.of(System.getProperty("user.dir")).toAbsolutePath(); - Path repoRoot = moduleDir.resolve("../..").normalize(); + Path repoRoot = VerificationFixtures.repositoryRoot(); assertThat(repoRoot.resolve("integration-tests/helpers/hosted-recovery-blocked-wedge-driver.ts")) .isRegularFile(); Path evidence = (System.getProperty("qwen.wedge.evidence") != null @@ -231,7 +231,7 @@ class HostedBackgroundPublicationGate { } registration.getRuntimeBroker().setWorkspaceMounts(mounts); var guard = new WorkspaceStorageGuard(jdbc, - new DataSourceTransactionManager(jdbc.getDataSource()), registration); + new DataSourceTransactionManager(jdbc.getDataSource()), registration, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()); for (int index = 0; index < workspaces.size(); index++) { guard.register(tenant, "storage-" + index, UUID.randomUUID().toString()); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedCancellationProbe.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedCancellationProbe.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedCancellationProbe.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedCancellationProbe.java index 65bc6a73bef..1cba5a2f0a3 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedCancellationProbe.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedCancellationProbe.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; @@ -102,7 +102,7 @@ final class HostedCancellationProbe implements AutoCloseable { private Map owner(Map session) throws Exception { String storage = "storage-" + sessions.indexOf(session); var physical = jdbc.queryForMap("SELECT " + ManagedSchemaProjection.STORAGE_OWNER.columns("o") + " FROM ma_storage_owner o JOIN ma_storage s ON s.physical_key = o.storage_key WHERE s.alias_key = ? AND s.tenant_id = ? AND s.storage_id = ?", - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), tenant, storage); var member = jdbc.queryForMap("SELECT binding_id, runtime_generation, runtime_session_id, execution_storage_key" + " FROM ma_runtime_session WHERE tenant_id = ? AND harness_session_id = ?", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelCrashFixtureMain.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelCrashFixtureMain.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelCrashFixtureMain.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelCrashFixtureMain.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelFaultGatesIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelFaultGatesIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelFaultGatesIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelFaultGatesIT.java index 245101c2339..bcee0ba0b41 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelFaultGatesIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelFaultGatesIT.java @@ -1,8 +1,7 @@ package com.alibaba.qwen.code.managedagent; - import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; import com.alibaba.qwen.code.managedagent.channel.ManagedChannelService; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelGateSupport.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelGateSupport.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelGateSupport.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelGateSupport.java index 6ab21c2e26a..1d9caf45a33 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelGateSupport.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelGateSupport.java @@ -1,6 +1,5 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.jdbc.core.JdbcTemplate; @@ -298,7 +297,7 @@ class HostedChannelGateSupport { + " ma_resource WHERE session_scope_key = ?" + " AND resource_id = ? AND tenant_id = ?" + " AND session_id = ?", - ManagedSessionStore.sessionScopeKey(tenant, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId), projection.get("record_resource_id"), tenant, sessionId); assertThat(resources).hasSize(1); return JSON.readTree(resources.getFirst().get("body").toString()); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelProcessCrashIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelProcessCrashIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelProcessCrashIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedChannelProcessCrashIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedCleanupRetentionMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedCleanupRetentionMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedCleanupRetentionMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedCleanupRetentionMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedCleanupRetentionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedCleanupRetentionTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedCleanupRetentionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedCleanupRetentionTest.java index 389aa099e0f..4074f7bbd94 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedCleanupRetentionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedCleanupRetentionTest.java @@ -1,16 +1,11 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedRuntimeRetentionGuard; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeRetention; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeDrainReceipt; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; @@ -21,8 +16,13 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeRetention; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.h2.jdbcx.JdbcDataSource; @@ -74,7 +74,7 @@ class HostedCleanupRetentionTest { transactions = new TransactionTemplate(new DataSourceTransactionManager(source)); bindings = new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("key", new byte[32])); runtimeSessions = new JdbcRuntimeSessionRepository(source); - journal = transactions.execute(ignored -> new ExtensionRecordJournal(new ManagedSessionStore(jdbc), + journal = transactions.execute(ignored -> new ExtensionRecordJournal(com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)), "tenant", "workspace", "harness").open()); scope = new RuntimeScope("tenant", "workspace", "1", "/workspace", "sha256:" + "a".repeat(64), "session"); original = retire(new RuntimeProvisionRequest(scope, "harness", "local-process", "storage")); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedCommittedEventLineReplayIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedCommittedEventLineReplayIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedCommittedEventLineReplayIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedCommittedEventLineReplayIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedConcurrentTurnBurstMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedConcurrentTurnBurstMySqlIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedConcurrentTurnBurstMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedConcurrentTurnBurstMySqlIT.java index abeaec8f23b..9f860abcb6d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedConcurrentTurnBurstMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedConcurrentTurnBurstMySqlIT.java @@ -4,7 +4,7 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.session.HarnessCoordinator; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedDurableWorkers.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedDurableWorkers.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedDurableWorkers.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedDurableWorkers.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamLivenessIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamLivenessIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamLivenessIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamLivenessIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamRelay.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamRelay.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamRelay.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamRelay.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamRelayTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamRelayTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamRelayTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedEventStreamRelayTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedExtensionsIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedExtensionsIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedExtensionsIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedExtensionsIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedGateTestSupport.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedGateTestSupport.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedGateTestSupport.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedGateTestSupport.java index 577042c9b2e..b119622be37 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedGateTestSupport.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedGateTestSupport.java @@ -5,14 +5,14 @@ import com.alibaba.qwen.code.daemon.HarnessEventStream; import com.alibaba.qwen.code.daemon.HarnessSessionRef; import com.alibaba.qwen.code.daemon.HostedHarnessClient; import com.alibaba.qwen.code.daemon.StreamHarnessEvents; +import com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.SourceEvent; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.SourceEvent; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.ProjectedEvent; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.session.HarnessEventProjector; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -327,7 +327,7 @@ final class HostedGateTestSupport { + invocation.getMethod().getName()); }); context.addBeanFactoryPostProcessor(beanFactory -> beanFactory - .registerSingleton("toolPublicationOss", oss)); + .registerSingleton("toolPublicationOss", new AliyunObjectStoreClient(oss))); } // The extension-record JDBC read, led by the Session's scope key: the @@ -336,7 +336,7 @@ final class HostedGateTestSupport { static List extensionRecords(JdbcTemplate jdbc, String tenant, String sessionId, String domain) { try { - String scope = ManagedSessionStore.sessionScopeKey(tenant, + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId); var rows = jdbc.queryForList( "SELECT record_id, record_resource_id FROM" diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedGateTestSupportTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedGateTestSupportTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedGateTestSupportTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedGateTestSupportTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHarnessMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHarnessMySqlIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHarnessMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHarnessMySqlIT.java index 5b3c8d02bfe..476425fd99d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHarnessMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHarnessMySqlIT.java @@ -11,7 +11,7 @@ import com.alibaba.qwen.code.daemon.ManagedSessionStoreConnection; import com.alibaba.qwen.code.daemon.PromptReceipt; import com.alibaba.qwen.code.daemon.StreamHarnessEvents; import com.alibaba.qwen.code.daemon.SubmitHarnessTurn; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookCancelFaultGatesIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookCancelFaultGatesIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookCancelFaultGatesIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookCancelFaultGatesIT.java index b2fc9cf0831..5a252a71d32 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookCancelFaultGatesIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookCancelFaultGatesIT.java @@ -1,8 +1,7 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.daemon.HarnessSessionRef; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; @@ -460,7 +459,7 @@ class HostedHookCancelFaultGatesIT { assertThat(ref.isNull()).as("hook result ref").isFalse(); String body = jdbc.queryForObject("SELECT CONVERT(inline_bytes USING utf8mb4) FROM ma_resource" + " WHERE session_scope_key = ? AND resource_id = ? AND tenant_id = ? AND session_id = ?", - String.class, ManagedSessionStore.sessionScopeKey(tenant, session), + String.class, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), ref.path("resourceId").asText(), tenant, session); return json.readTree(body); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookFaultGatesIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookFaultGatesIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookFaultGatesIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookFaultGatesIT.java index 1ef19a99b60..20d5171df36 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookFaultGatesIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookFaultGatesIT.java @@ -1,8 +1,7 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.daemon.HarnessSessionRef; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; @@ -388,7 +387,7 @@ class HostedHookFaultGatesIT { assertThat(ref.isNull()).as("hook result ref").isFalse(); String body = jdbc.queryForObject("SELECT CONVERT(inline_bytes USING utf8mb4) FROM ma_resource" + " WHERE session_scope_key = ? AND resource_id = ? AND tenant_id = ? AND session_id = ?", - String.class, ManagedSessionStore.sessionScopeKey(tenant, session), + String.class, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), ref.path("resourceId").asText(), tenant, session); return json.readTree(body); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookMessageDisplayReloadIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookMessageDisplayReloadIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookMessageDisplayReloadIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookMessageDisplayReloadIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookReplyLossFaultGatesIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookReplyLossFaultGatesIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookReplyLossFaultGatesIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookReplyLossFaultGatesIT.java index 34d56479f3b..a249a917e87 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookReplyLossFaultGatesIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookReplyLossFaultGatesIT.java @@ -1,14 +1,13 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.daemon.HarnessSessionRef; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.persistence.FaultingExtensionRecordStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedTaskEventStore; import com.alibaba.qwen.code.managedagent.persistence.StoreFaultGate; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -320,7 +319,7 @@ class HostedHookReplyLossFaultGatesIT { assertThat(ref.isNull()).as("hook result ref").isFalse(); String body = jdbc.queryForObject("SELECT CONVERT(inline_bytes USING utf8mb4) FROM ma_resource" + " WHERE session_scope_key = ? AND resource_id = ? AND tenant_id = ? AND session_id = ?", - String.class, ManagedSessionStore.sessionScopeKey(tenant, session), + String.class, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), ref.path("resourceId").asText(), tenant, session); return json.readTree(body); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookStreamGapIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookStreamGapIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookStreamGapIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedHookStreamGapIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedLifecycleRelay.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedLifecycleRelay.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedLifecycleRelay.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedLifecycleRelay.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedLifecycleRelayTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedLifecycleRelayTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedLifecycleRelayTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedLifecycleRelayTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpReplyLossIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpReplyLossIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpReplyLossIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpReplyLossIT.java index 0127e68d329..a67bf76b3bf 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpReplyLossIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpReplyLossIT.java @@ -1,9 +1,8 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -398,7 +397,7 @@ class HostedMcpReplyLossIT { } private JsonNode published(String session, JsonNode ref) { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String body = jdbc.queryForObject( "SELECT CONVERT(inline_bytes USING utf8mb4) FROM ma_resource" + " WHERE session_scope_key = ? AND resource_id = ?" diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpRestartFaultGatesIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpRestartFaultGatesIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpRestartFaultGatesIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpRestartFaultGatesIT.java index 2982e619071..1f3e3208f45 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpRestartFaultGatesIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpRestartFaultGatesIT.java @@ -1,10 +1,9 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import com.fasterxml.jackson.databind.JsonNode; @@ -1171,7 +1170,7 @@ class HostedMcpRestartFaultGatesIT { } private JsonNode published(String session, JsonNode ref) { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String body = jdbc.queryForObject( "SELECT CONVERT(inline_bytes USING utf8mb4) FROM ma_resource" + " WHERE session_scope_key = ? AND resource_id = ?" diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpResultDeliveryIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpResultDeliveryIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpResultDeliveryIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpResultDeliveryIT.java index de9b808943c..863f50dca6a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpResultDeliveryIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpResultDeliveryIT.java @@ -1,9 +1,8 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -296,7 +295,7 @@ class HostedMcpResultDeliveryIT { } private JsonNode published(String session, JsonNode ref) { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String body = jdbc.queryForObject( "SELECT CONVERT(inline_bytes USING utf8mb4) FROM ma_resource" + " WHERE session_scope_key = ? AND resource_id = ?" diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStoreFaultGatesIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStoreFaultGatesIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStoreFaultGatesIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStoreFaultGatesIT.java index debb5b9b1f5..fde12b99442 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStoreFaultGatesIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStoreFaultGatesIT.java @@ -1,15 +1,14 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.persistence.FaultingExtensionRecordStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedTaskEventStore; import com.alibaba.qwen.code.managedagent.persistence.StoreFaultGate; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -792,7 +791,7 @@ class HostedMcpStoreFaultGatesIT { } private JsonNode published(String session, JsonNode ref) { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String body = jdbc.queryForObject( "SELECT CONVERT(inline_bytes USING utf8mb4) FROM ma_resource" + " WHERE session_scope_key = ? AND resource_id = ?" diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStreamGapIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStreamGapIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStreamGapIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStreamGapIT.java index 3eab35b8418..472ed64f2f6 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStreamGapIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMcpStreamGapIT.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.daemon.HarnessSessionRef; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunFaultGatesIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunFaultGatesIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunFaultGatesIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunFaultGatesIT.java index ec732e21b3e..744e3093101 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunFaultGatesIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunFaultGatesIT.java @@ -1,12 +1,11 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.session.HarnessEventProjector; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.net.httpserver.HttpServer; @@ -1247,7 +1246,7 @@ class HostedMonitorRunFaultGatesIT { + " ma_resource WHERE session_scope_key = ? AND" + " tenant_id = ? AND session_id = ? AND kind LIKE" + " 'managed-tool-result-%'", - ManagedSessionStore.sessionScopeKey(tenant, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId), tenant, sessionId)) { byId.put(row.get("resource_id").toString(), row); } @@ -1260,7 +1259,7 @@ class HostedMonitorRunFaultGatesIT { + " 'managed-tool-result-page') OR (kind =" + " 'managed-tool-result-content' AND byte_length <" + " 1024))", - ManagedSessionStore.sessionScopeKey(tenant, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId), tenant, sessionId)) { String body = row.get("body").toString(); // The producers' short tail segment is raw bytes, not JSON; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunProbe.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunProbe.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunProbe.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunProbe.java index 88c6b2a910c..32c9532b4c5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunProbe.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMonitorRunProbe.java @@ -1,9 +1,9 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.daemon.HarnessEventStream; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.session.HarnessEventProjector; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.net.httpserver.HttpServer; @@ -173,8 +173,7 @@ final class HostedMonitorRunProbe implements AutoCloseable { // case), so each later letter polls slower. The record read leads with // the same key. private String scopeKey(String sessionId) { - return com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore - .sessionScopeKey(tenant, sessionId); + return com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId); } // FG8c: the in-JVM embedded broker spawns each binding's managed worker diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationActivationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationActivationTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationActivationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationActivationTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationExtension.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationExtension.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationExtension.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationExtension.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationExtensionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationExtensionTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationExtensionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationExtensionTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedMySqlIsolationIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedOperatorRecoveryProbe.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedOperatorRecoveryProbe.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedOperatorRecoveryProbe.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedOperatorRecoveryProbe.java index 08ecf063082..6779addbf1e 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedOperatorRecoveryProbe.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedOperatorRecoveryProbe.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; -import com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceRecoveryCommand; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.maintenance.WorkspaceRecoveryCommand; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.net.httpserver.HttpServer; import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedOperatorRecoveryProbeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedOperatorRecoveryProbeTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedOperatorRecoveryProbeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedOperatorRecoveryProbeTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashFixtureMain.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashFixtureMain.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashFixtureMain.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashFixtureMain.java index f5abd8619f4..3dbca2304d7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashFixtureMain.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashFixtureMain.java @@ -1,9 +1,9 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.boot.builder.SpringApplicationBuilder; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashIT.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashIT.java index 1a7a841e10d..882328649b1 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessCrashIT.java @@ -1,7 +1,6 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.fasterxml.jackson.databind.JsonNode; @@ -417,7 +416,7 @@ class HostedProcessCrashIT { new com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount( tenant, "storage", root.resolve("workspace").toString()))); new com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard(jdbc, - new org.springframework.jdbc.datasource.DataSourceTransactionManager(jdbc.getDataSource()), storageRegistrationProperties) + new org.springframework.jdbc.datasource.DataSourceTransactionManager(jdbc.getDataSource()), storageRegistrationProperties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()) .register(tenant, "storage", UUID.randomUUID().toString()); } controller = HttpServer.create(new java.net.InetSocketAddress("127.0.0.1", 0), 0); @@ -632,7 +631,7 @@ class HostedProcessCrashIT { private Map owner() throws Exception { return jdbc.queryForMap("SELECT " + ManagedSchemaProjection.STORAGE_OWNER.columns("o") + " FROM ma_storage_owner o JOIN ma_storage s ON s.physical_key = o.storage_key WHERE s.alias_key = ? AND s.tenant_id = ? AND s.storage_id = ?", - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenant, "storage"), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenant, "storage"), tenant, "storage"); } @@ -772,7 +771,7 @@ class HostedProcessCrashIT { // the raw tenant+session form scans the lane's accumulated ma_resource rows. byte[] checkpoint = jdbc.queryForObject("SELECT inline_bytes FROM ma_resource" + " WHERE session_scope_key = ? AND resource_id = ? AND tenant_id = ? AND session_id = ?", - byte[].class, ManagedSessionStore.sessionScopeKey(tenant, session), checkpointId, tenant, session); + byte[].class, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), checkpointId, tenant, session); JsonNode saved = JSON.readTree(checkpoint); assertThat(saved.path("continuation").path("phase").asText()).isEqualTo(prepared ? "before_model" : "await_runtime"); assertThat(events.stream().filter(event -> event.path("kind").asText().equals("input.accepted") @@ -783,7 +782,7 @@ class HostedProcessCrashIT { && event.path("payload").path("role").asText().equals("tool_result"))).isEmpty(); assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ma_resource WHERE session_scope_key = ?" + " AND kind = 'managed-tool-outcome' AND tenant_id = ? AND session_id = ?", - Integer.class, ManagedSessionStore.sessionScopeKey(tenant, session), tenant, session)) + Integer.class, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), tenant, session)) .isZero(); JsonNode target = report.path("target"); if (fault.equals("harness-result")) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessLiveness.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessLiveness.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessLiveness.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessLiveness.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessLivenessTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessLivenessTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessLivenessTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProcessLivenessTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderControlProbe.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderControlProbe.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderControlProbe.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderControlProbe.java index dffc4d3f8d8..cc367693734 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderControlProbe.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderControlProbe.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -172,7 +172,7 @@ final class HostedProviderControlProbe implements AutoCloseable { private Map owner(Map session) throws Exception { String storage = "storage-" + sessions.indexOf(session); var physical = jdbc.queryForMap("SELECT " + ManagedSchemaProjection.STORAGE_OWNER.columns("o") + " FROM ma_storage_owner o JOIN ma_storage s ON s.physical_key = o.storage_key WHERE s.alias_key = ? AND s.tenant_id = ? AND s.storage_id = ?", - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), tenant, storage); var member = jdbc.queryForMap("SELECT binding_id, runtime_generation, runtime_session_id, execution_storage_key" + " FROM ma_runtime_session WHERE tenant_id = ? AND harness_session_id = ?", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbe.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbe.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbe.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbe.java index 4c133385376..15aec5a15f6 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbe.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbe.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.net.httpserver.HttpExchange; @@ -242,7 +242,7 @@ final class HostedProviderMediaProbe implements AutoCloseable { String storageId = "storage-" + sessions.indexOf(session); String physical = jdbc.queryForObject("SELECT physical_key FROM ma_storage" + " WHERE alias_key = ? AND tenant_id = ? AND storage_id = ?", String.class, - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storageId), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storageId), tenant, storageId); assertThat(physical).isNotBlank(); var member = jdbc.queryForMap("SELECT binding_id, runtime_generation, session_state," diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbeTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbeTest.java index 408635c26aa..1f597a03162 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbeTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedProviderMediaProbeTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.net.httpserver.Headers; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedPublicWorkspaceIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedPublicWorkspaceIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedPublicWorkspaceIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedPublicWorkspaceIT.java index eb4f24c5f5c..95fafa5b021 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedPublicWorkspaceIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedPublicWorkspaceIT.java @@ -2,8 +2,8 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.daemon.HostedHarnessClient; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -918,7 +918,7 @@ class HostedPublicWorkspaceIT { var fixture = spring.getBean(com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentHostedRuntimeFixture.class); var bindingRepository = spring.getBean(com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository.class); var runtimeSessions = spring.getBean(com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository.class); - var state = spring.getBean(com.alibaba.qwen.code.managedagent.session.AgentStateStore.class); + var state = spring.getBean(com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.class); String first = residentSession("G0_RESIDENT_FIRST", "resident-first"); saveResidentOwnership("after-first-turn"); assertThat(Files.readString(roots.getFirst().resolve("child/proof.txt"))).isEqualTo("after-first-" + tenant); @@ -964,7 +964,7 @@ class HostedPublicWorkspaceIT { assertThat(fixture.physicalReleases).hasValue(0); assertThat(request("POST", "/v1/agents/sessions/" + first + "/close", null, "resident-close", "actor", 202).path("id").asText()).isEqualTo(operation); - com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentHostedRuntimeFixture.assertConflict(() -> new com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport() + com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentHostedRuntimeFixture.assertConflict(() -> new com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport() .activateWorkspace(original, oldContexts.getFirst(), context, true).toCompletableFuture().join()); com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentHostedRuntimeFixture.assertConflict(() -> spring.getBean(EmbeddedRuntimeBroker.class).service() .acquire(first, oldContexts.getFirst().getRuntimeSessionId(), oldContexts.getFirst().getSession().getTurnKind()) @@ -987,7 +987,7 @@ class HostedPublicWorkspaceIT { fixture = spring.getBean(com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentHostedRuntimeFixture.class); bindingRepository = spring.getBean(com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository.class); runtimeSessions = spring.getBean(com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository.class); - state = spring.getBean(com.alibaba.qwen.code.managedagent.session.AgentStateStore.class); + state = spring.getBean(com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.class); assertThat(fixture.workerAlive()).isTrue(); assertThat(fixture.starts).hasValue(1); saveResidentOwnership("after-application-reconstruction"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedRecoveryWedgeGate.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedRecoveryWedgeGate.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedRecoveryWedgeGate.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedRecoveryWedgeGate.java index 99c81328a14..ff564b20726 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedRecoveryWedgeGate.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedRecoveryWedgeGate.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.managedprobes.PublicationProbeWiring; import com.fasterxml.jackson.databind.ObjectMapper; @@ -63,13 +63,14 @@ import static org.assertj.core.api.Assertions.assertThat; * or test switch is needed. Run with -Dqwen.wedge.control=true for the * no-fault control run. */ -class HostedRecoveryWedgeGate { +public class HostedRecoveryWedgeGate { @TempDir private Path temporary; @Test @Timeout(1800) - void recoveryBlockedSessionMustNotWedgeOtherSessionTurns() throws Exception { + public void recoveryBlockedSessionMustNotWedgeOtherSessionTurns() + throws Exception { org.junit.jupiter.api.Assumptions.assumeTrue( "true".equals(System.getProperty("qwen.wedge.probe")), "A2 reproduction probe: skipped unless -Dqwen.wedge.probe=true"); @@ -86,7 +87,7 @@ class HostedRecoveryWedgeGate { assertThat(cli).as("CLI entry -Dqwen.cli.entry=%s", cli).isRegularFile(); boolean control = "true".equals(System.getProperty("qwen.wedge.control")); Path moduleDir = Path.of(System.getProperty("user.dir")).toAbsolutePath(); - Path repoRoot = moduleDir.resolve("../..").normalize(); + Path repoRoot = VerificationFixtures.repositoryRoot(); assertThat(repoRoot.resolve("integration-tests/helpers/hosted-recovery-blocked-wedge-driver.ts")) .isRegularFile(); Path evidence = (System.getProperty("qwen.wedge.evidence") != null @@ -211,7 +212,7 @@ class HostedRecoveryWedgeGate { } registration.getRuntimeBroker().setWorkspaceMounts(mounts); var guard = new WorkspaceStorageGuard(jdbc, - new DataSourceTransactionManager(jdbc.getDataSource()), registration); + new DataSourceTransactionManager(jdbc.getDataSource()), registration, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()); for (int index = 0; index < workspaces.size(); index++) { guard.register(tenant, "storage-" + index, UUID.randomUUID().toString()); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedSessionRelayFaultGatesIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedSessionRelayFaultGatesIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedSessionRelayFaultGatesIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedSessionRelayFaultGatesIT.java index 9b06c5528da..3504d9bbab6 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedSessionRelayFaultGatesIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedSessionRelayFaultGatesIT.java @@ -6,10 +6,10 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedSessionRelayForwardingProxy.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedSessionRelayForwardingProxy.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedSessionRelayForwardingProxy.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedSessionRelayForwardingProxy.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleFixtureMain.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleFixtureMain.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleFixtureMain.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleFixtureMain.java index f04bf75fe5f..948acc8aa25 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleFixtureMain.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleFixtureMain.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.Filter; @@ -81,7 +82,7 @@ public final class HostedShellLifecycleFixtureMain { case "toString" -> "l4-publication-fixture"; default -> throw new AssertionError("Unexpected object I/O: " + invocation.getMethod().getName()); }); - context.addBeanFactoryPostProcessor(factory -> factory.registerSingleton("toolPublicationOss", oss)); + context.addBeanFactoryPostProcessor(factory -> factory.registerSingleton("toolPublicationOss", new AliyunObjectStoreClient(oss))); }).run(arguments.toArray(String[]::new)); Path ready = Path.of(config.path("ready").asText()); Path writing = ready.resolveSibling(ready.getFileName() + ".tmp"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleRecoveryIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleRecoveryIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleRecoveryIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleRecoveryIT.java index 9f92b06b4eb..29ccd222bb1 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleRecoveryIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellLifecycleRecoveryIT.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.net.httpserver.HttpExchange; @@ -128,7 +128,7 @@ class HostedShellLifecycleRecoveryIT { new com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount( tenant, "storage", root.resolve("workspace").toString()))); new com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard(jdbc, - new org.springframework.jdbc.datasource.DataSourceTransactionManager(jdbc.getDataSource()), storageRegistrationProperties) + new org.springframework.jdbc.datasource.DataSourceTransactionManager(jdbc.getDataSource()), storageRegistrationProperties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()) .register(tenant, "storage", UUID.randomUUID().toString()); jdbc.update("INSERT INTO ma_workspace (tenant_id, workspace_id, workspace_generation, storage_id," + " display_name, config_ref, policy_ref, state) VALUES (?, 'workspace', 1, 'storage', 'L4', ?, ?, 'ACTIVE')", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellOutputProbe.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellOutputProbe.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellOutputProbe.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellOutputProbe.java index 5ee48553745..a680e6e2519 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellOutputProbe.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedShellOutputProbe.java @@ -1,7 +1,6 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.fasterxml.jackson.databind.JsonNode; @@ -81,7 +80,7 @@ final class HostedShellOutputProbe implements AutoCloseable { private Map owner(Map session) throws Exception { String storage = "storage-" + sessions.indexOf(session); var physical = jdbc.queryForMap("SELECT " + ManagedSchemaProjection.STORAGE_OWNER.columns("o") + " FROM ma_storage_owner o JOIN ma_storage s ON s.physical_key = o.storage_key WHERE s.alias_key = ? AND s.tenant_id = ? AND s.storage_id = ?", - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), tenant, storage); var member = jdbc.queryForMap("SELECT binding_id, runtime_generation, runtime_session_id, execution_storage_key" + " FROM ma_runtime_session WHERE tenant_id = ? AND harness_session_id = ?", @@ -176,12 +175,12 @@ final class HostedShellOutputProbe implements AutoCloseable { // raw tenant+session form scans the lane's accumulated ma_resource rows. private List> resources(String id, String kind) { return jdbc.queryForList("SELECT " + ManagedSchemaProjection.RESOURCE.columns() + " FROM ma_resource WHERE session_scope_key = ? AND kind = ? AND tenant_id = ? AND session_id = ?", - ManagedSessionStore.sessionScopeKey(tenant, id), kind, tenant, id); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, id), kind, tenant, id); } private byte[] resource(String id, String resourceId, String digest, int length) throws Exception { var row = jdbc.queryForMap("SELECT " + ManagedSchemaProjection.RESOURCE.columns() + " FROM ma_resource WHERE session_scope_key = ? AND resource_id = ? AND tenant_id = ? AND session_id = ?", - ManagedSessionStore.sessionScopeKey(tenant, id), resourceId, tenant, id); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, id), resourceId, tenant, id); byte[] bytes = (byte[]) row.get("inline_bytes"); assertThat(bytes).hasSize(length); assertThat(((Number) row.get("byte_length")).intValue()).isEqualTo(length); @@ -312,7 +311,7 @@ final class HostedShellOutputProbe implements AutoCloseable { assertThat(((Number) head.get("committed_sequence")).longValue()).isEqualTo(sequence); assertThat(head.get("last_commit_digest")).isEqualTo(previous); byte[] checkpoint = jdbc.queryForObject("SELECT inline_bytes FROM ma_resource WHERE session_scope_key = ? AND resource_id = ? AND tenant_id = ? AND session_id = ?", - byte[].class, ManagedSessionStore.sessionScopeKey(tenant, id), head.get("latest_checkpoint_resource_id"), tenant, id); + byte[].class, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, id), head.get("latest_checkpoint_resource_id"), tenant, id); assertThat(JSON.readTree(checkpoint).path("continuation").path("phase").asText()).isEqualTo("await_runtime"); if (report == null) { return; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedSseGapProbe.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedSseGapProbe.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedSseGapProbe.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedSseGapProbe.java index fdad343624a..f828dac2b45 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedSseGapProbe.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedSseGapProbe.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.session.HarnessEventProjector; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -141,7 +141,7 @@ final class HostedSseGapProbe implements AutoCloseable { + " WHERE s.alias_key = ? AND s.tenant_id = ? AND s.storage_id = ?" + " AND m.harness_session_id = ? AND m.binding_id = ?" + " AND m.runtime_generation = ? AND m.runtime_session_id = ?", - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storage), tenant, storage, session, execution.get("binding_id"), execution.get("runtime_generation"), promptId); assertThat(member.get("physical_key")).isNotNull(); assertThat(member.get("execution_storage_key")).isEqualTo(finished ? null : member.get("physical_key")); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamLeadMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamLeadMySqlIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamLeadMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamLeadMySqlIT.java index 537f9a74d5e..f7576de80e6 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamLeadMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamLeadMySqlIT.java @@ -1,8 +1,7 @@ package com.alibaba.qwen.code.managedagent; - import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamMemberCommandMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamMemberCommandMySqlIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamMemberCommandMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamMemberCommandMySqlIT.java index 61f4902b084..de1b4158a9f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamMemberCommandMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedTeamMemberCommandMySqlIT.java @@ -6,8 +6,8 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowFaultGatesIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowFaultGatesIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowFaultGatesIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowFaultGatesIT.java index ba8e43abbd9..97dd4225dad 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowFaultGatesIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowFaultGatesIT.java @@ -1,9 +1,9 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.workspace.ChildResultRelay; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowProbe.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowProbe.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowProbe.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowProbe.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowRelayFaultGatesIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowRelayFaultGatesIT.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowRelayFaultGatesIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowRelayFaultGatesIT.java index f36324a5201..48e24da2996 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowRelayFaultGatesIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkflowRelayFaultGatesIT.java @@ -6,11 +6,11 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceColdLoadProbe.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceColdLoadProbe.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceColdLoadProbe.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceColdLoadProbe.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceToolTurnIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceToolTurnIT.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceToolTurnIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceToolTurnIT.java index 15d9830ebb4..7cb5a6d9887 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceToolTurnIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceToolTurnIT.java @@ -1,8 +1,11 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.api.ToolPublicationController; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationAdmissionStore; @@ -10,10 +13,7 @@ import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.session.HarnessEventProjector; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; @@ -658,7 +658,7 @@ class HostedWorkspaceToolTurnIT { String storageId = "storage-" + index; String physical = jdbc.queryForObject("SELECT physical_key FROM ma_storage" + " WHERE alias_key = ? AND tenant_id = ? AND storage_id = ?", String.class, - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storageId), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storageId), tenant, storageId); assertThat(physical).isNotBlank(); Map member = jdbc.queryForMap("SELECT binding_id, runtime_generation, runtime_session_id," @@ -752,7 +752,7 @@ class HostedWorkspaceToolTurnIT { // Led by the indexed session scope key, as in HostedGateTestSupport: // the raw tenant+session form scans the lane's accumulated ma_resource rows. List> stored = jdbc.queryForList("SELECT " + ManagedSchemaProjection.RESOURCE.columns() + " FROM ma_resource WHERE session_scope_key = ? AND resource_id = ? AND tenant_id = ? AND session_id = ?", - ManagedSessionStore.sessionScopeKey(tenant, sessionId), resource.path("resourceId").asText(), tenant, sessionId); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId), resource.path("resourceId").asText(), tenant, sessionId); if (!applied) { if (resource.has("bytesBase64")) { assertThat(stored).as(fault + " rolled-back resource").isEmpty(); @@ -835,7 +835,7 @@ class HostedWorkspaceToolTurnIT { assertThat(head.get("latest_checkpoint_resource_id")).isEqualTo(checkpointId); byte[] checkpoint = jdbc.queryForObject("SELECT inline_bytes FROM ma_resource" + " WHERE session_scope_key = ? AND resource_id = ? AND tenant_id = ? AND session_id = ?", byte[].class, - ManagedSessionStore.sessionScopeKey(tenant, sessionId), checkpointId, tenant, sessionId); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId), checkpointId, tenant, sessionId); String phase = mapper.readTree(checkpoint).path("continuation").path("phase").asText(); if (fault.startsWith("result-")) { assertThat(phase).isEqualTo("await_runtime"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180HardenedVerificationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180HardenedVerificationTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180HardenedVerificationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180HardenedVerificationTest.java index 8572305cd55..6f9530b19f7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180HardenedVerificationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180HardenedVerificationTest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; @@ -220,8 +220,8 @@ class Issue13180HardenedVerificationTest { "INSERT INTO ma_session_record (record_kind, session_scope_key, record_key, tenant_id, session_id," + " action_id, record_id, action_version, domain, revision, state, options_json, gmt_create)" + " VALUES ('ACTION', ?, ?, ?, ?, ?, ?, 1, 'approval', 1, 'requested', ?, ?)", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey(tenant, session), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(tenant, session, actionId), tenant, session, actionId, actionId, + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(tenant, session, actionId), tenant, session, actionId, actionId, "{\"v\":1,\"requestId\":\"" + actionId + "\"," + "\"turnId\":\"turn-verify\"," + "\"functionCallId\":\"call-verify\"," diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180InternalPortTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180InternalPortTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180InternalPortTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180InternalPortTest.java index abdd68aa315..19002718bba 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180InternalPortTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180InternalPortTest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180SignedModeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180SignedModeTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180SignedModeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/Issue13180SignedModeTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/JournalEventPublicationPointTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/JournalEventPublicationPointTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/JournalEventPublicationPointTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/JournalEventPublicationPointTest.java index 1f2fdd4b7c9..f81927b8d47 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/JournalEventPublicationPointTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/JournalEventPublicationPointTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; -import com.alibaba.qwen.code.managedagent.event.JournalEventTransport; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.event.JournalEventTransport; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -82,8 +82,8 @@ class JournalEventPublicationPointTest { + " workspace_config_ref, workspace_policy_ref) VALUES (?, ?, ?, ?," + " 'qwen-code', 'ACTIVE', 1, 1, ?, ?, 1, '.', ?, 1," + " ?, ?)", - TENANT, session, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(TENANT), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(session), storage, WORKSPACE, + TENANT, session, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(TENANT), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(session), storage, WORKSPACE, WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, WorkspaceExecutionProfile.CONFIG_REF, WorkspaceExecutionProfile.POLICY_REF); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/LegacyCwdOperations.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/LegacyCwdOperations.java similarity index 82% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/LegacyCwdOperations.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/LegacyCwdOperations.java index 093d92bb50a..828cf591979 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/LegacyCwdOperations.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/LegacyCwdOperations.java @@ -2,10 +2,8 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import org.springframework.jdbc.core.JdbcTemplate; @@ -19,7 +17,7 @@ public final class LegacyCwdOperations { String tenant, String session, String actor, String actorDigest, String key, String digest, String target, long revision) { String id = "op_" + UUID.randomUUID().toString().replace("-", ""); - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String targetKey = ManagedSchemaKeys.key(session); long now = System.currentTimeMillis(); String request = JsonNodeFactory.instance.objectNode().put("schemaVersion", 1) @@ -35,7 +33,7 @@ public final class LegacyCwdOperations { ManagedSchemaKeys.operationKey(ManagedSchemaContract.OperationKind.CWD_CHANGE, scope, targetKey, id), scope, scope, targetKey, session, request, ManagedSchemaKeys.key(key), tenant, session, id, actorDigest, key, digest, target, revision, - ManagedWorkspaceRegistry.actorKey(tenant, actor), now, now, now); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenant, actor), now, now, now); return new OperationAdmission(store.findOperation(tenant, session, id).orElseThrow(), false); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/LegacyEvents.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/LegacyEvents.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/LegacyEvents.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/LegacyEvents.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedActionsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedActionsTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedActionsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedActionsTest.java index 84aaa8e184b..c1ca63376fb 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedActionsTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedActionsTest.java @@ -6,23 +6,22 @@ import ch.qos.logback.core.read.ListAppender; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.harness.UnavailableHarnessConnector; import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; import com.alibaba.qwen.code.managedagent.session.ActionResponseCoordinator; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.session.ManagedActionService; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -117,10 +116,10 @@ class ManagedActionsTest { @Bean @Primary ManagedAgentStore actionStore(@Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, ObjectMapper mapper, ManagedWorkspaceRegistry registry, @Qualifier("managedAgentClock") Clock clock, - com.alibaba.qwen.code.managedagent.event.CommittedEventPublisher publisher) { + com.alibaba.qwen.code.managedagent.contracts.event.CommittedEventPublisher publisher) { var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - return new ManagedAgentStore(jdbc, mapper, clock, publisher, registry, properties); + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, clock, publisher, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } } @@ -665,7 +664,7 @@ class ManagedActionsTest { call -> { delivered.incrementAndGet(); journal.change("decided", call.getArgument(3)); - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); }); mvc.perform( auth(post(path(session, journal.id) + "/responses"), tenant, "other") @@ -814,8 +813,8 @@ class ManagedActionsTest { responses.put(journal.id, call -> { if (attempts.incrementAndGet() == 1) { throw switch (failure) { - case "permanent" -> WorkspaceExecutionStore.unavailable(); - case "retryable" -> WorkspaceExecutionStore.unavailableTransient( + case "permanent" -> com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); + case "retryable" -> com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailableTransient( new IllegalStateException("temporary authority failure")); default -> new RuntimeBrokerException(409, "runtime_unavailable", "Runtime authority is unavailable.", false); @@ -1287,10 +1286,10 @@ class ManagedActionsTest { jdbc.update("INSERT INTO ma_request_receipt (receipt_key,namespace,tenant_key,tenant_id,scope_kind,scope_key,scope_id," + "idempotency_key,idempotency_digest,request_digest,actor_id,session_id,state,gmt_create,gmt_modified)" + " VALUES (?,'SESSION_CREATE_ACTOR',?,?,'ACTOR',?,?,?,?,?, ?,?,'COMPLETED',?,?)", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("fixture", tenant, key), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(tenant), tenant, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("owner"), "owner", key, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(key), "sha256:test", + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("fixture", tenant, key), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(tenant), tenant, + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("owner"), "owner", key, + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(key), "sha256:test", "owner".getBytes(java.nio.charset.StandardCharsets.UTF_8), session, System.currentTimeMillis(), System.currentTimeMillis()); jdbc.update("UPDATE ma_session SET creator_actor_key=? WHERE tenant_id=? AND session_id=?", "owner".getBytes(java.nio.charset.StandardCharsets.UTF_8), tenant, session); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentApiContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentApiContractTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentApiContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentApiContractTest.java index c67bd2aa76e..f6b71687d26 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentApiContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentApiContractTest.java @@ -1,5 +1,4 @@ package com.alibaba.qwen.code.managedagent; - import com.alibaba.qwen.code.managedagent.ManagedAgentServerIntegrationTest.FixtureHarness; import com.alibaba.qwen.code.managedagent.OpenApiContract.Operation; import com.alibaba.qwen.code.managedagent.api.ApiExceptionHandler; @@ -9,6 +8,7 @@ import com.alibaba.qwen.code.managedagent.api.SshWorkspaceController; import com.alibaba.qwen.code.managedagent.api.TenantContextArgumentResolver; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; import com.alibaba.qwen.code.managedagent.api.WorkspaceDiscoveryController; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.model.ApiModels; import com.alibaba.qwen.code.managedagent.model.ApiModels.CommandAdmission; import com.alibaba.qwen.code.managedagent.model.ApiModels.CreateSessionRequest; @@ -59,12 +59,11 @@ import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellWorkspace; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnSummary; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.persistence.ManagedTaskEventStore; import com.alibaba.qwen.code.managedagent.persistence.SshWorkspaceStore; import com.alibaba.qwen.code.managedagent.session.SessionRenameCoordinator; import com.alibaba.qwen.code.managedagent.workspace.SshWorkspaceService; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; @@ -159,7 +158,7 @@ class ManagedAgentApiContractTest { entry(com.alibaba.qwen.code.managedagent.api.SessionWorktreeController.Page.class, List.of("WebShellSessionWorktreePage")), entry(com.alibaba.qwen.code.managedagent.workspace.SessionWorktreeService.Options.class, List.of("WebShellSessionWorktreeOptions")), entry(com.alibaba.qwen.code.managedagent.workspace.SessionWorktreeService.Inspection.class, List.of("WebShellSessionWorktreeInspection")), - entry(com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore.Descriptor.class, List.of("WebShellSessionWorktree")), + entry(com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Descriptor.class, List.of("WebShellSessionWorktree")), entry(ApiModels.PublicSessionWorktree.class, List.of("SessionWorktree")), entry(ApiModels.WebShellLifecycleOperation.class, List.of("WebShellLifecycleOperation")), entry(ApiModels.WebShellExportRequest.class, List.of("WebShellExportRequest")), @@ -404,7 +403,7 @@ class ManagedAgentApiContractTest { jdbc.update("INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" + " VALUES (?, 'workspace', ?, 'OPERATOR')", tenant, actor.actorId().getBytes(StandardCharsets.UTF_8)); new JdbcRuntimeBindingRepository(jdbc.getDataSource(), - new com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector("fixture", new byte[32])) + new com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector("fixture", new byte[32])) .requestStorageFence(tenant, "storage", UUID.randomUUID().toString()); assertThat(CONTRACT.node("/components/schemas/ErrorEnvelope/properties/error/properties/retryable/type").asText()) .isEqualTo("boolean"); @@ -2025,14 +2024,12 @@ class ManagedAgentApiContractTest { taskEvents.expireThrough(tenant, sessionId, taskId, 1); exchange(drift, "listSessionTaskEvents", 409, get(eventsPath) - .param("after", ManagedTaskEventStore - .encodeCursor(tenant, sessionId, taskId, 0)) + .param("after", com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.encodeCursor(tenant, sessionId, taskId, 0)) .header(TENANT, tenant), null); exchange(drift, "listSessionTaskEvents", 200, get(eventsPath) - .param("after", ManagedTaskEventStore - .encodeCursor(tenant, sessionId, taskId, 1)) + .param("after", com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.encodeCursor(tenant, sessionId, taskId, 1)) .header(TENANT, tenant), null); @@ -2123,7 +2120,7 @@ class ManagedAgentApiContractTest { /** A committed record body for a synthetic child_run row. */ private void childRunResource(String tenant, String sessionId, String resourceId, String json) { - String scope = com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey(tenant, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId); byte[] body = json.getBytes(StandardCharsets.UTF_8); jdbc.update("INSERT INTO ma_resource (resource_key," + " session_scope_key, tenant_id, workspace_id, session_id," @@ -2134,8 +2131,7 @@ class ManagedAgentApiContractTest { + " 'command', 'REFERENCED', " + com.alibaba.qwen.code.runtimebroker.managedschema .ManagedSchemaTime.SQL_NOW + ")", - com.alibaba.qwen.code.runtimebroker.managedschema - .ManagedSchemaKeys.key(scope, resourceId), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scope, resourceId), scope, tenant, sessionId, resourceId, body.length, org.apache.commons.codec.digest.DigestUtils.sha256Hex(body), body); @@ -2161,8 +2157,7 @@ class ManagedAgentApiContractTest { + " ?, 'child_run', 'run-contract', ?, 1," + " 'resource-run-contract', 'child_agent', 'running'," + " 'ready', 'session', 'planned', 1, 2)", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore - .sessionScopeKey(tenant, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId), recordKey, tenant, sessionId, "d".repeat(64)); // #13746 F2: the run's committed record names its child Session, // and both task views carry that link inside their schemas. @@ -2285,8 +2280,7 @@ class ManagedAgentApiContractTest { // and its view advertises the action. String workflowKey = "e".repeat(64); jdbc.update("INSERT INTO ma_session_record (session_scope_key, record_key, tenant_id, workspace_id, session_id, domain, record_id, operation_hash, revision, record_resource_id, task_kind, task_state, runtime_state, delivery_target, delivery_state, gmt_create, started_at) VALUES (?, ?, ?, 'workspace-contract', ?, 'child_run', 'run-contract-wf', ?, 1, 'resource-run-contract-wf', 'workflow', 'running', 'ready', 'session', 'planned', 1, 2)", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore - .sessionScopeKey(tenant, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId), workflowKey, tenant, sessionId, "f".repeat(64)); childRunResource(tenant, sessionId, "resource-run-contract-wf", "{\"kind\":\"workflow\",\"childSessionId\":null}"); @@ -2343,8 +2337,7 @@ class ManagedAgentApiContractTest { + " AND operation_kind = 'TASK_CANCEL'" + " AND state IN ('PENDING', 'RUNNING')", tenant, sessionId); jdbc.update("INSERT INTO ma_session_record (session_scope_key, record_key, tenant_id, workspace_id, session_id, domain, record_id, operation_hash, revision, record_resource_id, task_kind, task_state, runtime_state, delivery_target, delivery_state, gmt_create, started_at) VALUES (?, ?, ?, 'workspace-contract', ?, 'automation_run', 'arun-contract', ?, 2, 'resource-automation-contract', 'automation_run', 'running', 'ready', 'session', 'planned', 1, 2)", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore - .sessionScopeKey(tenant, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId), automationKey, tenant, sessionId, "2".repeat(64)); childRunResource(tenant, sessionId, "resource-automation-contract", "{\"scheduleId\":\"asch_" + "1".repeat(32) @@ -2718,12 +2711,12 @@ class ManagedAgentApiContractTest { + " '.', '{}', 1000, 1000)", channelTenant); String sessionId = UUID.randomUUID().toString(); var routeRepository = new com.alibaba.qwen.code.managedagent.persistence.JdbcChannelRouteRepository(jdbc); - routeRepository.findOrCreate(new com.alibaba.qwen.code.managedagent.persistence.ChannelRouteRepository.ChannelRoute( + routeRepository.findOrCreate(new com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelRouteRepository.ChannelRoute( channelTenant, "a".repeat(64), "mail-1", 1, "1700:42", 1, sessionId, "alice@example.com", "alice@example.com", "thread-1", "admitted", "chin-" + "a".repeat(64), List.of("sha256:" + "b".repeat(64)), 1000, 1000)); var deliveryRepository = new com.alibaba.qwen.code.managedagent.persistence.JdbcChannelDeliveryRepository(jdbc); - deliveryRepository.findOrCreate(new com.alibaba.qwen.code.managedagent.persistence.ChannelDeliveryRepository.ChannelDelivery( + deliveryRepository.findOrCreate(new com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository.ChannelDelivery( channelTenant, "mail-1", "delivery-1", "delivery-1:0", 0, "planned", null, 1000, 1000)); deliveryRepository.transition(channelTenant, "mail-1", "delivery-1", "planned", "sending", null); deliveryRepository.transition(channelTenant, "mail-1", "delivery-1", "sending", "delivered", ""); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentDefinitionAdmissionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentDefinitionAdmissionTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentDefinitionAdmissionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentDefinitionAdmissionTest.java index 5280dfee581..3d026c9c99b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentDefinitionAdmissionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentDefinitionAdmissionTest.java @@ -3,6 +3,7 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.harness.UnavailableHarnessConnector; import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; @@ -11,7 +12,6 @@ import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStor import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -91,7 +91,7 @@ class ManagedAgentDefinitionAdmissionTest { var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); properties.getHarness().setWorkspaceShellEnabled(true); - var selected = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); + var selected = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var transactions = new TransactionTemplate(transactionManager); String id = transactions.execute(status -> selected.insertWorkspaceSessionCommand(tenant, "actor", "create", "digest", "qwen-code", null, null, List.of(), null, new WorkspaceSelection("ws-a", ".")).sessionId()); @@ -139,7 +139,7 @@ class ManagedAgentDefinitionAdmissionTest { properties.getHarness().setWorkspaceShellEnabled(true); properties.getHarness().setApprovalMode("default"); properties.setDefinitionToolProfiles(List.of("hosted-workspace-shell/1")); - var selected = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); + var selected = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); selected.setManagedAgentDefinitionStore(definitions); String agent = definition.get("id").asText(); assertThat(selected.workspaceSessionOptions(tenant, agent).planModeSupported()).isFalse(); @@ -175,8 +175,8 @@ class ManagedAgentDefinitionAdmissionTest { properties.getHarness().setWorkspaceFilesEnabled(true); properties.getHarness().setWorkspaceShellEnabled(shellEnabled); properties.getHarness().setApprovalMode("default"); - var selected = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), - ignored -> {}, registry, properties); + var selected = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), + ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); selected.setManagedAgentDefinitionStore(definitions); var transaction = new TransactionTemplate(transactionManager); var workspace = new WorkspaceSelection("ws-a", "."); @@ -219,8 +219,8 @@ class ManagedAgentDefinitionAdmissionTest { properties.getRuntimeBroker().setWorkspaceMounts(List.of( new ManagedAgentProperties.RuntimeBroker.WorkspaceMount( tenant, "storage-a", "/workspace"))); - var enabled = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), - ignored -> {}, registry, properties); + var enabled = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), + ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); enabled.setManagedAgentDefinitionStore(definitions); var transaction = new TransactionTemplate(transactionManager); var workspace = new WorkspaceSelection("ws-a", "."); @@ -233,8 +233,8 @@ class ManagedAgentDefinitionAdmissionTest { assertThat(enabled.requireSession(tenant, initial.sessionId()).approvalMode()) .isEqualTo(mode); properties.getHarness().setWorkspaceShellEnabled(false); - var disabled = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), - ignored -> {}, registry, properties); + var disabled = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), + ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); disabled.setManagedAgentDefinitionStore(definitions); var empty = transaction.execute(status -> disabled.insertWorkspaceSessionCommand( tenant, "actor", "empty", "empty-digest", agentId, null, @@ -495,7 +495,7 @@ class ManagedAgentDefinitionAdmissionTest { } }; ManagedAgentService service = new ManagedAgentService(store, - new RequestDigests(), null, harness, registry); + new RequestDigests(), null, harness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); assertThat(service.getWebShellSession(tenant, "actor", sessionId) .capabilities().workspaceTurns()).isTrue(); // The dispatch is refused only by the unavailable Harness. @@ -549,7 +549,7 @@ class ManagedAgentDefinitionAdmissionTest { var definitions = new com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore(jdbc, new RequestDigests()); assertThat(definitions.pinnedContent(session).get("instructions")) .isEqualTo("Review in Chinese."); - var resources = new com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore(jdbc); + var resources = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore(jdbc)); var first = resources.publishAgentInstructions(tenant, "workspace", session.sessionId(), "Review in Chinese."); assertThat(resources.publishAgentInstructions(tenant, "workspace", session.sessionId(), diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentDefinitionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentDefinitionTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentDefinitionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentDefinitionTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentMySqlIT.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentMySqlIT.java index 9fae35223a3..d9a8e19c858 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentMySqlIT.java @@ -2,6 +2,16 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.catalog.ManagedAgentDefinitionService; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.TaskProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitReceipt; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RenewWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.WriterGrant; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.AgentDefinitionRequest; import com.alibaba.qwen.code.managedagent.model.RequestDigests; @@ -13,23 +23,13 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnPage; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnSummary; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection.TaskProjection; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore.TaskRow; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitReceipt; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.RenewWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.SealWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.WriterGrant; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.runtimebroker.JdbcRepositoryContract; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.TaskRow; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRepositoryContract; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -40,7 +40,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -118,15 +117,15 @@ class ManagedAgentMySqlIT { option.setModelId(id); return option; }).toList()); - var store = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var transactions = new TransactionTemplate(new DataSourceTransactionManager(source)); for (String model : List.of("model-a", "model-b")) { var input = List.>of(Map.of("type", "input_text", "text", "first")); var admitted = transactions.execute(status -> store.insertWorkspaceSessionCommand(tenant, "actor", model, "digest-" + model, "qwen-code", null, null, input, com.alibaba.qwen.code.daemon.SubmitHarnessTurn.computePayloadDigest(input), - new com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection("ws", "."), + new com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection("ws", "."), mode, true, null, false, List.of(), false, model)); assertThat(jdbc.queryForMap("SELECT model_id,model_revision,approval_mode,approval_revision FROM ma_turn" + " WHERE tenant_id=? AND session_id=?", tenant, admitted.sessionId())) @@ -161,13 +160,13 @@ class ManagedAgentMySqlIT { new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource); String runId = UUID.randomUUID().toString().substring(0, 8); ManagedAgentStore store = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String createTenant = "mysql-create-mode-" + runId; store.insertSessionCommand( createTenant, @@ -191,7 +190,7 @@ class ManagedAgentMySqlIT { null, List.of(), null, - new com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection("ws-a", "."))) + new com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection("ws-a", "."))) .isInstanceOfSatisfying( ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("idempotency_conflict")); @@ -282,8 +281,8 @@ class ManagedAgentMySqlIT { TransactionTemplate transactions = new TransactionTemplate(new DataSourceTransactionManager(dataSource)); - ManagedSessionStore firstInstance = new ManagedSessionStore(jdbc); - ManagedSessionStore secondInstance = new ManagedSessionStore(jdbc); + ManagedSessionStore firstInstance = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); + ManagedSessionStore secondInstance = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String storeTenant = "mysql-private-store-" + runId; String sessionId = "mysql-private-session-" + runId; String workspaceId = "mysql-private-workspace-" + runId; @@ -524,7 +523,7 @@ class ManagedAgentMySqlIT { TransactionTemplate transactions = new TransactionTemplate(new DataSourceTransactionManager(dataSource)); - ManagedSessionStore store = new ManagedSessionStore(jdbc); + ManagedSessionStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); WriterGrant grant = inTransaction( transactions, @@ -686,13 +685,13 @@ class ManagedAgentMySqlIT { String lowerTenant = "case-" + UUID.randomUUID().toString().substring(0, 8); String upperTenant = lowerTenant.toUpperCase(java.util.Locale.ROOT); ManagedAgentStore store = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); Admission lower = store.insertSessionCommand( lowerTenant, @@ -736,13 +735,13 @@ class ManagedAgentMySqlIT { JdbcTemplate jdbc = new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource); ManagedAgentStore store = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String tenant = "mysql-workspace-" + UUID.randomUUID(); try { for (String id : List.of("workspace-a", "workspace-b")) { @@ -831,13 +830,13 @@ class ManagedAgentMySqlIT { new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource); ManagedWorkspaceRegistry registry = new ManagedWorkspaceRegistry(jdbc); ManagedAgentStore store = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, registry, - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); TransactionTemplate transactions = new TransactionTemplate(new DataSourceTransactionManager(dataSource)); String tenant = "mysql-actor-" + UUID.randomUUID(); @@ -892,7 +891,7 @@ class ManagedAgentMySqlIT { null, List.of(), null, - new com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection(workspace, "."))); + new com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection(workspace, "."))); assertThat(registry.canRead(tenant, actor, workspace)).isTrue(); assertThat(registry.canRead(tenant, "actor-a", workspace)).isFalse(); assertThat(registry.canRead(tenant, actor, "workspace-a")).isFalse(); @@ -976,7 +975,7 @@ class ManagedAgentMySqlIT { transactions, () -> new ExtensionRecordJournal( - new ManagedSessionStore(jdbc), + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)), tenant, "mysql-extension-workspace", session) @@ -1100,13 +1099,13 @@ class ManagedAgentMySqlIT { JdbcTemplate jdbc = new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource); ManagedAgentStore agents = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); TransactionTemplate transactions = new TransactionTemplate(new DataSourceTransactionManager(dataSource)); String tenant = "mysql-journal-deleted"; @@ -1127,7 +1126,7 @@ class ManagedAgentMySqlIT { transactions, () -> new ExtensionRecordJournal( - new ManagedSessionStore(jdbc), + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)), tenant, "mysql-journal-workspace", session) @@ -1227,7 +1226,7 @@ class ManagedAgentMySqlIT { inTransaction( transactions, () -> - new ManagedSessionStore(jdbc) + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)) .sealWriter( tenant, session, @@ -1281,13 +1280,13 @@ class ManagedAgentMySqlIT { JdbcTemplate jdbc = new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource); ManagedAgentStore agents = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); TransactionTemplate transactions = new TransactionTemplate(new DataSourceTransactionManager(dataSource)); String tenant = "mysql-journal-mid-commit"; @@ -1308,7 +1307,7 @@ class ManagedAgentMySqlIT { transactions, () -> new ExtensionRecordJournal( - new ManagedSessionStore(jdbc), + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)), tenant, "mysql-journal-workspace", session) @@ -1430,13 +1429,13 @@ class ManagedAgentMySqlIT { JdbcTemplate jdbc = new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource); ManagedAgentStore store = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); TransactionTemplate transactions = new TransactionTemplate(new DataSourceTransactionManager(dataSource)); String tenant = "mysql-operation-" + UUID.randomUUID(); @@ -1586,7 +1585,7 @@ class ManagedAgentMySqlIT { "session.closed", "session.archived"); - ManagedSessionStore journals = new ManagedSessionStore(jdbc); + ManagedSessionStore journals = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String token = "mysql-lifecycle-writer-token-00000000"; assertThatThrownBy(() -> inTransaction(transactions, () -> journals.acquireWriter(tenant, sessionId, token, @@ -1644,13 +1643,13 @@ class ManagedAgentMySqlIT { JdbcTemplate jdbc = new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource); ManagedAgentStore store = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String tenant = "mysql-turns-" + UUID.randomUUID(); String sessionId = store.insertSessionCommand( @@ -1750,7 +1749,7 @@ class ManagedAgentMySqlIT { return super.update(sql, arguments); } }; - var store = new ManagedSessionStore(jdbc); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var tx = new TransactionTemplate(new DataSourceTransactionManager(isolated)); tx.setIsolationLevel( org.springframework.transaction.TransactionDefinition @@ -1865,16 +1864,16 @@ class ManagedAgentMySqlIT { .migrate(); var jdbc = new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(source); var store = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var authority = - new com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore( - jdbc, new DataSourceTransactionManager(source)); + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore( + jdbc, new DataSourceTransactionManager(source))); com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceRecoveryContract.verify( source, jdbc, store, authority); com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceRecoveryContract.verifyOperatorPrepare( @@ -1926,7 +1925,7 @@ class ManagedAgentMySqlIT { JdbcTemplate jdbc = new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource); jdbc.queryForList("ANALYZE TABLE ma_session_record"); - String scopeKey = ManagedSessionStore.sessionScopeKey("mysql-hook-index", session); + String scopeKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("mysql-hook-index", session); for (Object[] lookup : List.of( new Object[] { @@ -2003,7 +2002,7 @@ class ManagedAgentMySqlIT { .isInstanceOfSatisfying( ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.NOT_FOUND); + assertThat(error.getStatus()).isEqualTo(404); assertThat(error.getCode()).isEqualTo("agent_not_found"); }); assertThat(store.latest(tenant, agentId)).isPresent(); @@ -2137,7 +2136,7 @@ class ManagedAgentMySqlIT { .isInstanceOfSatisfying( ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()).isEqualTo("agent_revision_conflict"); }); assertThat(raced).isTrue(); @@ -2275,7 +2274,7 @@ class ManagedAgentMySqlIT { Flyway.configure().dataSource(source).locations("classpath:db/managed-schema").load().migrate(); JdbcTemplate jdbc = new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(source); TransactionTemplate transactions = new TransactionTemplate(new DataSourceTransactionManager(source)); - ManagedSessionStore resources = new ManagedSessionStore(jdbc); + ManagedSessionStore resources = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String tenant = "mysql-skills-" + UUID.randomUUID(); String session = UUID.randomUUID().toString(); String token = "s".repeat(32); @@ -2287,7 +2286,7 @@ class ManagedAgentMySqlIT { () -> resources.publishAgentSkills(tenant, "workspace", session, skills)); assertThat(inTransaction(transactions, () -> resources.publishAgentSkills(tenant, "workspace", session, skills))).isEqualTo(published); - var reloaded = new ManagedSessionStore(new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(source)) + var reloaded = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(source))) .readResource(tenant, "workspace", session, published.resourceId(), token); assertThat(new ObjectMapper().readTree(reloaded.bytes())).isEqualTo(new ObjectMapper().valueToTree(skills)); assertThat(reloaded.digest()).isEqualTo(published.digest()); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentPackagingIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentPackagingIT.java similarity index 84% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentPackagingIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentPackagingIT.java index 76aa9eacf5f..6779c5c3405 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentPackagingIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentPackagingIT.java @@ -17,7 +17,7 @@ class ManagedAgentPackagingIT { Path directory; @Test void standaloneArtifactsIncludeConfigurationWhileLibraryDoesNot() throws Exception { - String prefix = "target/qwen-managed-agent-server-" + System.getProperty("managed.artifact.version"); + String prefix = System.getProperty("managed.artifact.directory") + "/qwen-managed-agent-server-" + System.getProperty("managed.artifact.version"); try (var library = new ZipFile(Path.of(prefix + ".jar").toFile())) { assertThat(library.getEntry("application.yml")).isNull(); assertThat(library.getEntry("application-managed-local.yml")).isNull(); @@ -29,17 +29,20 @@ class ManagedAgentPackagingIT { try (var executable = new ZipFile(Path.of(prefix + "-" + classifier + ".jar").toFile())) { assertThat(executable.getEntry("BOOT-INF/classes/application.yml")).as(classifier).isNotNull(); assertThat(executable.getEntry("BOOT-INF/classes/application-managed-local.yml")).as(classifier).isNotNull(); + assertThat(executable.stream().map(entry -> entry.getName())) + .as(classifier + " runtime libraries") + .noneMatch(name -> name.startsWith("BOOT-INF/lib/lombok-")); } } } @Test void retirementLauncherLoadsFromExecutableJar() throws Exception { - Path jar = Path.of("target/qwen-managed-agent-server-" + System.getProperty("managed.artifact.version") + Path jar = Path.of(System.getProperty("managed.artifact.directory") + "/qwen-managed-agent-server-" + System.getProperty("managed.artifact.version") + "-exec.jar").toAbsolutePath(); Path log = directory.resolve("retirement.log"); Process process = new ProcessBuilder(Path.of(System.getProperty("java.home"), "bin", "java").toString(), - "-Dloader.main=com.alibaba.qwen.code.managedagent.persistence.ResidentWorkspaceRetirementMain", + "-Dloader.main=com.alibaba.qwen.code.managedagent.maintenance.ResidentWorkspaceRetirementMain", "-cp", jar.toString(), "org.springframework.boot.loader.launch.PropertiesLauncher") .redirectErrorStream(true).redirectOutput(log.toFile()).start(); try { @@ -56,7 +59,7 @@ class ManagedAgentPackagingIT { @Test void operatorRecoveryReadsDocumentedEnvironment() throws Exception { assumeTrue(Boolean.getBoolean("managed.operator.tools.enabled")); - Path jar = Path.of("target/qwen-managed-agent-server-" + System.getProperty("managed.artifact.version") + Path jar = Path.of(System.getProperty("managed.artifact.directory") + "/qwen-managed-agent-server-" + System.getProperty("managed.artifact.version") + "-operator-recovery.jar").toAbsolutePath(); Path log = directory.resolve("operator.log"); var builder = new ProcessBuilder(Path.of(System.getProperty("java.home"), "bin", "java").toString(), diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentServerIntegrationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentServerIntegrationTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentServerIntegrationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentServerIntegrationTest.java index e9b97e4384a..0dd8a58b97f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentServerIntegrationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentServerIntegrationTest.java @@ -1,12 +1,13 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.daemon.HarnessRuntimeRecovery; import com.alibaba.qwen.code.managedagent.api.ManagedSessionStoreController; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRuntimeRecovery; import com.alibaba.qwen.code.managedagent.model.ApiModels.CommandAdmission; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.Admission; @@ -19,7 +20,6 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.ProjectedEvent; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionMutationKind; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.session.HarnessCoordinator; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; @@ -213,11 +213,11 @@ class ManagedAgentServerIntegrationTest { ManagedWorkspaceRegistry workspaces = applicationContext.getBean( ManagedWorkspaceRegistry.class); ManagedAgentService upgraded = new ManagedAgentService( - new ManagedAgentStore(jdbc, objectMapper, Clock.systemUTC(), - ignored -> {}, workspaces, changed), + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, objectMapper, Clock.systemUTC(), + ignored -> {}, workspaces, changed, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())), applicationContext.getBean(RequestDigests.class), applicationContext.getBean(HarnessCoordinator.class), - harness, workspaces); + harness, workspaces, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); CommandAdmission retry = upgraded.createSession(tenant, "revision-create", "qwen-code", "1", null, null, List.of()); @@ -1674,7 +1674,7 @@ class ManagedAgentServerIntegrationTest { Admission turn = store.insertTurnCommand(tenant, "SUBMIT_TURN", "exempt-turn", "sha256:" + "8".repeat(64), session.sessionId(), List.of(), "sha256:" + "9".repeat(64)); - DaemonHttpException writerConflict = mock(DaemonHttpException.class); + HarnessHttpFailure writerConflict = mock(HarnessHttpFailure.class); when(writerConflict.getStatusCode()).thenReturn(409); when(writerConflict.getErrorCode()) .thenReturn("managed_session_writer_conflict"); @@ -3281,8 +3281,8 @@ class ManagedAgentServerIntegrationTest { if (promptOversized || recordOversized) { lastRejectionArm.set( promptOversized ? "prompt" : "record"); - DaemonHttpException tooLarge = - mock(DaemonHttpException.class); + HarnessHttpFailure tooLarge = + mock(HarnessHttpFailure.class); when(tooLarge.getStatusCode()).thenReturn(413); throw tooLarge; } @@ -3495,10 +3495,10 @@ class ManagedAgentServerIntegrationTest { String harnessSession = "harness-" + UUID.randomUUID(); String operation = "operation-" + UUID.randomUUID(); var scope = new com.alibaba.qwen.code.runtimebroker.RuntimeScope(tenant, "workspace", "1", "/workspace", "capability", "session"); - var bindings = new com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository(jdbc.getDataSource(), - new com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector("test", new byte[32])); - var sessions = new com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository(jdbc.getDataSource()); - var executions = new com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository(jdbc.getDataSource()); + var bindings = new com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository(jdbc.getDataSource(), + new com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector("test", new byte[32])); + var sessions = new com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository(jdbc.getDataSource()); + var executions = new com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository(jdbc.getDataSource()); var provisioner = org.mockito.Mockito.mock(com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner.class, org.mockito.Mockito.CALLS_REAL_METHODS); var transport = org.mockito.Mockito.mock(com.alibaba.qwen.code.runtimebroker.RuntimeTransport.class); @@ -3513,12 +3513,12 @@ class ManagedAgentServerIntegrationTest { assertThat(cause).isInstanceOfSatisfying(RuntimeBrokerException.class, conflict -> assertThat(conflict.getCode()).isEqualTo("runtime_admission_closed")); }; - try (var broker = new com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService( + try (var broker = new com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService( ignored -> java.util.concurrent.CompletableFuture.completedFuture(scope), provisioner, transport, bindings, sessions, executions, "cleanup-probe", Duration.ofMinutes(1), Duration.ofMinutes(1))) { var binding = broker.warm(harnessSession).toCompletableFuture().join(); jdbc.execute((org.springframework.jdbc.core.ConnectionCallback) connection -> { - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.beginHarnessLifecycle(connection, tenant, harnessSession, operation); + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.beginHarnessLifecycle(connection, tenant, harnessSession, operation); return null; }); jdbc.update("UPDATE ma_session_head SET claim_generation = 7, claim_lease_until = ? WHERE tenant_id = ? AND harness_session_id = ?", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentStoreLocaleTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentStoreLocaleTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentStoreLocaleTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentStoreLocaleTest.java index 99797d32820..cbf5bb5fe30 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentStoreLocaleTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAgentStoreLocaleTest.java @@ -32,10 +32,10 @@ class ManagedAgentStoreLocaleTest { + ";LOCK_TIMEOUT=10000"); ManagedSchema.initialize(source); JdbcTemplate jdbc = new ManagedSchemaJdbcTemplate(source); - ManagedAgentStore store = new ManagedAgentStore(jdbc, + ManagedAgentStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), events -> {}, mock(ManagedWorkspaceRegistry.class), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); Admission session = store.insertSessionCommand("tenant-locale", "CREATE_SESSION", "create-key", "digest-create", "qwen-code", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalChangeMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalChangeMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalChangeMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalChangeMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalChangeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalChangeTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalChangeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalChangeTest.java index dddec2b4f41..268e6f5bc5d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalChangeTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalChangeTest.java @@ -2,8 +2,11 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.daemon.SubmitHarnessTurn; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AuthorizeLifecycleRequest; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.model.ApiModels; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellApprovalChangeResult; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellApprovalSelection; @@ -11,12 +14,9 @@ import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AuthorizeLifecycleRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.session.HarnessCoordinator; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; @@ -114,7 +114,7 @@ class ManagedApprovalChangeTest { jdbc.update("INSERT INTO ma_session_record (session_scope_key,record_key,tenant_id,session_id," + "domain,record_id,revision,gmt_create,record_kind,state,action_id,action_version,options_json)" + " VALUES (?,?,?,?,'action','action',1,1,'ACTION','requested','action',1,?)", - ManagedSessionStore.sessionScopeKey(tenant, session), "a".repeat(64), tenant, session, + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), "a".repeat(64), tenant, session, "{\"expiresAt\":" + (System.currentTimeMillis() + 100_000) + "}"); assertThat(service.queryWebShellSessionApproval(tenant, "actor", session).liveSupported()).isTrue(); assertThat(change("actor", "live-change", "yolo", 0).approvalRevision()).isEqualTo(1); @@ -133,7 +133,7 @@ class ManagedApprovalChangeTest { @Test void liveApprovalReadsRequireTheCurrentWriterAndReturnTheDurableSelection() { - var privateStore = new ManagedSessionStore(jdbc); + var privateStore = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String token = "live-approval-writer-token-0123456789"; var grant = transaction().execute(status -> privateStore.acquireWriter(tenant, session, token, new AcquireWriterRequest("ws", "writer", 60_000L))); @@ -155,7 +155,7 @@ class ManagedApprovalChangeTest { @Test void liveApprovalReadsReturnTheSelectionForTheFilesV3Profile() throws Exception { session = customSession("hosted-workspace-files/3", "default"); - var privateStore = new ManagedSessionStore(jdbc); + var privateStore = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String token = "live-approval-v3-writer-token-0123456789"; var grant = transaction().execute(status -> privateStore.acquireWriter(tenant, session, token, new AcquireWriterRequest("ws", "writer", 60_000L))); @@ -178,9 +178,9 @@ class ManagedApprovalChangeTest { jdbc.update("UPDATE ma_turn SET status=? WHERE tenant_id=?", state, tenant); assertCode(() -> change("actor", "blocked-" + state, "yolo", 0), "session_context_busy"); } - for (String code : List.of(com.alibaba.qwen.code.managedagent.session.AgentStateStore.RUNTIME_RECOVERY_BLOCKED, - com.alibaba.qwen.code.managedagent.session.AgentStateStore.WORKFLOW_RECOVERY_BLOCKED, - com.alibaba.qwen.code.managedagent.session.AgentStateStore.HARNESS_RECOVERY_BLOCKED)) { + for (String code : List.of(com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.RUNTIME_RECOVERY_BLOCKED, + com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.WORKFLOW_RECOVERY_BLOCKED, + com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.HARNESS_RECOVERY_BLOCKED)) { jdbc.update("UPDATE ma_turn SET status='RUNNING', error_code=? WHERE tenant_id=?", code, tenant); assertCode(() -> change("actor", "blocked-" + code, "yolo", 0), "session_context_busy"); assertThat(jdbc.queryForObject("SELECT approval_revision FROM ma_session WHERE tenant_id=? AND session_id=?", @@ -619,11 +619,11 @@ class ManagedApprovalChangeTest { jdbc.update("INSERT INTO ma_session_record (session_scope_key,record_key,tenant_id,session_id," + "domain,record_id,revision,gmt_create,record_kind,state,action_id,action_version,options_json)" + " VALUES (?,?,?,?,'action','action',1,1,'ACTION','requested','action',1,?)", - ManagedSessionStore.sessionScopeKey(tenant, session), "a".repeat(64), tenant, session, + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), "a".repeat(64), tenant, session, "{\"expiresAt\":" + (System.currentTimeMillis() + 100_000) + "}"); } if (shape.equals("recovery")) { - transaction().execute(status -> new ManagedSessionStore(jdbc).acquireWriter(tenant, session, + transaction().execute(status -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).acquireWriter(tenant, session, "model-selection-writer-token-0123456789", new AcquireWriterRequest("ws", "writer", 60_000L))); jdbc.update("UPDATE ma_session_head SET recovery_status='BLOCKED' WHERE tenant_id=?", tenant); } @@ -666,7 +666,7 @@ class ManagedApprovalChangeTest { properties.getHarness().setApprovalMode("default"); properties.getRuntimeBroker().setWorkspaceMounts(List.of( new ManagedAgentProperties.RuntimeBroker.WorkspaceMount(tenant, "storage", "/tmp"))); - var result = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, registry, properties); + var result = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); result.setManagedAgentDefinitionStore(definitions); return result; } @@ -719,7 +719,7 @@ class ManagedApprovalChangeTest { when(harness.supportsLiveApprovalSelection()).thenReturn(true); transaction().execute(status -> service.changeWebShellSessionApproval(tenant, "actor", session, "stored-rules", "default", 0, null, rules)); - var privateStore = new ManagedSessionStore(jdbc); + var privateStore = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String token = "rules-writer-token-01234567890123456789"; var grant = transaction().execute(status -> privateStore.acquireWriter(tenant, session, token, new AcquireWriterRequest("ws", "writer", 60_000L))); @@ -771,7 +771,7 @@ class ManagedApprovalChangeTest { } private ManagedAgentService service(ManagedAgentStore source) { - return new ManagedAgentService(source, new RequestDigests(), mock(HarnessCoordinator.class), harness, registry); + return new ManagedAgentService(source, new RequestDigests(), mock(HarnessCoordinator.class), harness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); } private WebShellApprovalChangeResult change(String actor, String key, String mode, long revision) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalSelectionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalSelectionTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalSelectionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalSelectionTest.java index 1c85dad059f..7830babdb54 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalSelectionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedApprovalSelectionTest.java @@ -3,17 +3,17 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.model.ApiModels.CommandAdmission; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.session.HarnessCoordinator; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -303,8 +303,8 @@ class ManagedApprovalSelectionTest { properties.getHarness().setWorkspaceShellEnabled(shell); properties.getHarness().setApprovalMode(mode); var result = - new ManagedAgentStore( - jdbc, json, Clock.systemUTC(), ignored -> {}, registry, properties); + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( + jdbc, json, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); result.setManagedAgentDefinitionStore(definitions); return result; } @@ -317,7 +317,7 @@ class ManagedApprovalSelectionTest { new RequestDigests(), mock(HarnessCoordinator.class), mock(HarnessConnector.class), - registry); + registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); return transaction() .execute( status -> diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactApiIntegrationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactApiIntegrationTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactApiIntegrationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactApiIntegrationTest.java index 641f9fd25bf..e18d0bdfdd7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactApiIntegrationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactApiIntegrationTest.java @@ -7,15 +7,15 @@ import com.alibaba.qwen.code.managedagent.api.ManagedToolDetailsController; import com.alibaba.qwen.code.managedagent.api.RequestIdFilter; import com.alibaba.qwen.code.managedagent.api.TenantContextArgumentResolver; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; -import com.alibaba.qwen.code.managedagent.artifact.ManagedArtifactPolicy; import com.alibaba.qwen.code.managedagent.artifact.ManagedArtifactService; import com.alibaba.qwen.code.managedagent.artifact.ManagedToolDetailsService; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ManagedArtifactPolicy; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealWriterRequest; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellEvent; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.SealWriterRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedToolDetailsStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.session.HarnessCoordinator; @@ -89,7 +89,7 @@ class ManagedArtifactApiIntegrationTest { } sessions = new ManagedAgentService(fixture.sessions(), new RequestDigests(), mock(HarnessCoordinator.class), mock(HarnessConnector.class), - new ManagedWorkspaceRegistry(fixture.jdbc())); + new ManagedWorkspaceRegistry(fixture.jdbc()), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); var context = new org.springframework.context.annotation.AnnotationConfigApplicationContext(); context.registerBean(com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.class, fixture::properties); @@ -469,7 +469,7 @@ class ManagedArtifactApiIntegrationTest { private JsonNode detailsResource(String id, String kind, byte[] bytes) { String digest = org.apache.commons.codec.digest.DigestUtils.sha256Hex(bytes); - String scope = ManagedSessionStore.sessionScopeKey("tenant-1", "session-1"); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant-1", "session-1"); fixture.jdbc().update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id," + " resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, state, gmt_create)" + " VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?, 'MYSQL_INLINE', ?, 'REFERENCED', 1)", @@ -529,7 +529,7 @@ class ManagedArtifactApiIntegrationTest { @Test void exposesOriginalCommittedBytesAfterWriterSealAndSessionClose() throws Exception { new TransactionTemplate(fixture.manager()).executeWithoutResult(ignored -> - new ManagedSessionStore(fixture.jdbc()).sealWriter("tenant-1", "session-1", "a".repeat(32), + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc())).sealWriter("tenant-1", "session-1", "a".repeat(32), new SealWriterRequest("workspace-1", "writer-1", 1))); assertThat(fixture.jdbc().queryForObject("SELECT state FROM ma_session_head", String.class)) .isEqualTo("SEALED"); @@ -593,8 +593,7 @@ class ManagedArtifactApiIntegrationTest { JsonNode committed = JSON.readTree( Files.readString( - Path.of( - "../web-shell/client/components/managed/managed-tool-result.java-fixture.json"))); + VerificationFixtures.moduleRoot().resolve("../web-shell/client/components/managed/managed-tool-result.java-fixture.json"))); assertThat(JSON.readTree(normalizeTimes(fixtureJson).toString())) .isEqualTo(JSON.readTree(normalizeTimes(committed).toString())); } @@ -798,7 +797,7 @@ class ManagedArtifactApiIntegrationTest { var reader = org.mockito.Mockito.spy(fixture.reader()); org.mockito.Mockito.doAnswer(call -> { fixture.jdbc().update("UPDATE ma_session SET status = 'DELETED' WHERE session_id = X'73657373696f6e2d31'"); - throw new com.alibaba.qwen.code.managedagent.exception.ManagedAgentException(org.springframework.http.HttpStatus.CONFLICT, + throw new com.alibaba.qwen.code.managedagent.exception.ManagedAgentException(409, "tool_output_session_retired", "Retired during lease admission"); }).when(reader).lease(org.mockito.ArgumentMatchers.any()); var service = new ManagedArtifactService(sessions, fixture.results(), reader, fixture.policy(), fixture.properties()); @@ -806,7 +805,7 @@ class ManagedArtifactApiIntegrationTest { "session-1", stdout.path("id").asText(), stdout.path("revision").asText(), null, null, null, new org.springframework.mock.web.MockHttpServletResponse())) .isInstanceOfSatisfying(com.alibaba.qwen.code.managedagent.exception.ManagedAgentException.class, - error -> assertThat(error.getStatus()).isEqualTo(org.springframework.http.HttpStatus.NOT_FOUND)); + error -> assertThat(error.getStatus()).isEqualTo(org.springframework.http.HttpStatus.NOT_FOUND.value())); } private static MockHttpServletRequestBuilder bytes(JsonNode artifact) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactReadIntegrationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactReadIntegrationTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactReadIntegrationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactReadIntegrationTest.java index 584b1af23ca..e946bbd2f22 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactReadIntegrationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactReadIntegrationTest.java @@ -7,8 +7,8 @@ import com.alibaba.qwen.code.managedagent.api.ManagedArtifactController; import com.alibaba.qwen.code.managedagent.api.RequestIdFilter; import com.alibaba.qwen.code.managedagent.api.TenantContextArgumentResolver; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; -import com.alibaba.qwen.code.managedagent.artifact.ManagedArtifactPolicy; import com.alibaba.qwen.code.managedagent.artifact.ManagedArtifactService; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ManagedArtifactPolicy; import com.alibaba.qwen.code.managedagent.model.TenantContext; import com.alibaba.qwen.code.managedagent.persistence.ManagedArtifactReader; import com.fasterxml.jackson.databind.ObjectMapper; @@ -109,7 +109,7 @@ class ManagedArtifactReadIntegrationTest { return invocation.callRealMethod(); }) .when(reader) - .readRange(any(), anyLong(), anyInt(), any(com.alibaba.qwen.code.managedagent.persistence.ToolPublicationRetentionStore.ReadLease.class), any(Runnable.class)); + .readRange(any(), anyLong(), anyInt(), any(com.alibaba.qwen.code.managedagent.contracts.artifact.ArtifactReadLease.class), any(Runnable.class)); var mvc = mvc( new ManagedArtifactService( diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactSchedulingTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactSchedulingTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactSchedulingTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactSchedulingTest.java index d9ef0226ccf..610247c58cb 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactSchedulingTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedArtifactSchedulingTest.java @@ -2,12 +2,12 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedArtifactConfiguration; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.event.MessageMaterializer; import com.alibaba.qwen.code.managedagent.model.StoreModels.MaterializationTarget; import com.alibaba.qwen.code.managedagent.persistence.ManagedArtifactReader; import com.alibaba.qwen.code.managedagent.persistence.ManagedToolResultProjector; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.support.StaticListableBeanFactory; import org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration; @@ -78,7 +78,7 @@ class ManagedArtifactSchedulingTest { ManagedAgentProperties.class, fixture::properties); context.registerBean( - com.alibaba.qwen.code.managedagent.artifact.ManagedArtifactPolicy.class, + com.alibaba.qwen.code.managedagent.contracts.artifact.ManagedArtifactPolicy.class, fixture::policy); context.registerBean( "materializer", MessageMaterializer.class, diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAutomationRecordContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAutomationRecordContractTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAutomationRecordContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAutomationRecordContractTest.java index 903c1cfd104..1b1131c8051 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAutomationRecordContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedAutomationRecordContractTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelJdbcContract.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelJdbcContract.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelJdbcContract.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelJdbcContract.java index dec3b361cb3..af586e9dc01 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelJdbcContract.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelJdbcContract.java @@ -1,13 +1,13 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ChannelDeliveryRepository; -import com.alibaba.qwen.code.managedagent.persistence.ChannelDeliveryRepository.ChannelDelivery; -import com.alibaba.qwen.code.managedagent.persistence.ChannelDeliveryRepository.DeliveryCursor; -import com.alibaba.qwen.code.managedagent.persistence.ChannelDeliveryRepository.DeliveryPage; -import com.alibaba.qwen.code.managedagent.persistence.ChannelRouteRepository; -import com.alibaba.qwen.code.managedagent.persistence.ChannelRouteRepository.ChannelRoute; -import com.alibaba.qwen.code.managedagent.persistence.ChannelRouteRepository.RouteCursor; -import com.alibaba.qwen.code.managedagent.persistence.ChannelRouteRepository.RoutePage; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository.ChannelDelivery; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository.DeliveryCursor; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository.DeliveryPage; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelRouteRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelRouteRepository.ChannelRoute; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelRouteRepository.RouteCursor; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelRouteRepository.RoutePage; import com.alibaba.qwen.code.managedagent.persistence.JdbcChannelDeliveryRepository; import com.alibaba.qwen.code.managedagent.persistence.JdbcChannelRouteRepository; import org.springframework.jdbc.core.JdbcTemplate; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelJdbcContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelJdbcContractTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelJdbcContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelJdbcContractTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelRecordContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelRecordContractTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelRecordContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelRecordContractTest.java index 9791c4ab756..20e1c1f2566 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelRecordContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChannelRecordContractTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildAcceptanceRecordContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildAcceptanceRecordContractTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildAcceptanceRecordContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildAcceptanceRecordContractTest.java index b00aaf1fbdf..847216a7861 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildAcceptanceRecordContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildAcceptanceRecordContractTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildRunRecordContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildRunRecordContractTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildRunRecordContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildRunRecordContractTest.java index 0afb96fcced..2e87e8e711f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildRunRecordContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedChildRunRecordContractTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedConfigInstallContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedConfigInstallContractTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedConfigInstallContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedConfigInstallContractTest.java index 8ab7064f308..6901f4456ca 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedConfigInstallContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedConfigInstallContractTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedConfigInstall; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedConfigInstall; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionMySqlIT.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionMySqlIT.java index 11022f95ddd..9770d6d836d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionMySqlIT.java @@ -69,7 +69,7 @@ class ManagedContextCompressionMySqlIT extends ManagedContextCompressionOperatio @Test void projectionBackfillAndNativeCompactionCommitRemainMonotonic() throws Exception { var records = new com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore(jdbc, store); - var nativeStore = new com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore(jdbc, records); + var nativeStore = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore(jdbc, records)); var journal = tx(() -> new ExtensionRecordJournal(nativeStore, "tenant", "workspace", session).open()); var summary = "{}".getBytes(java.nio.charset.StandardCharsets.UTF_8); var resource = ManagedExtensionRecordStoreTest.hookResource("race-summary", "managed-message", summary); @@ -81,7 +81,7 @@ class ManagedContextCompressionMySqlIT extends ManagedContextCompressionOperatio payload.putArray("replacedMessageIds"); payload.putNull("tokenCountsRef"); }, lines -> lines); - var commit = new com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest( + var commit = new com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest( request.workspaceId(), request.writerId(), request.writerGeneration(), request.expectedJournalRevision(), request.expectedCommittedSequence(), request.transactionId(), request.operation(), request.commandId(), request.contentDigest(), request.firstSequence(), request.lastSequence(), request.eventCount(), diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionOperationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionOperationTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionOperationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionOperationTest.java index aed81e1967d..20ecdc7b839 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionOperationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedContextCompressionOperationTest.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; @@ -39,8 +39,8 @@ class ManagedContextCompressionOperationTest { transactions = new TransactionTemplate(new DataSourceTransactionManager(dataSource)); var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - store = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), - ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), + ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); jdbc.update("INSERT INTO ma_workspace (tenant_id, workspace_id, workspace_generation, storage_id," + " display_name, config_ref, policy_ref, state) VALUES ('tenant', 'workspace', 1, 'storage'," + " 'Workspace', 'config', 'policy', 'ACTIVE')"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedCwdChangeOperationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedCwdChangeOperationTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedCwdChangeOperationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedCwdChangeOperationTest.java index a7b8bf10240..963a7322dcc 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedCwdChangeOperationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedCwdChangeOperationTest.java @@ -1,6 +1,9 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.CwdChangeOutcome; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; @@ -9,21 +12,16 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionMutationKind; import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore.CwdChangeOutcome; import com.alibaba.qwen.code.managedagent.session.SessionLifecycleCoordinator; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; @@ -831,7 +829,7 @@ class ManagedCwdChangeOperationTest { assertThat(warmer.verified).containsExactly("services/b"); assertThat(warmer.bindings.get(0).getWorkspaceId()).isEqualTo(WS); - warmer.refuse(WorkspaceExecutionStore.unavailable()); + warmer.refuse(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()); String secondId = fixture.createBoundSession(TENANT, WS); String second = begin(fixture, secondId, "key", "digest", "gone", 1).operation().operationId(); @@ -883,7 +881,7 @@ class ManagedCwdChangeOperationTest { String sessionId = fixture.createBoundSession(TENANT, WS); String operationId = begin(fixture, sessionId, "key", "digest", "services/b", 1).operation().operationId(); - warmer.refuse(WorkspaceExecutionStore.unavailableTransient( + warmer.refuse(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailableTransient( new java.io.IOException("stale mount handle"))); coordinator.dispatch(TENANT, sessionId, operationId); OperationRecord waiting = fixture.store.findOperation(TENANT, @@ -919,7 +917,7 @@ class ManagedCwdChangeOperationTest { String sessionId = fixture.createBoundSession(TENANT, WS); String operationId = begin(fixture, sessionId, "key", "digest", "services/b", 1).operation().operationId(); - warmer.refuseAlways(WorkspaceExecutionStore.unavailableTransient( + warmer.refuseAlways(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailableTransient( new java.io.IOException("stale mount handle"))); coordinator.dispatch(TENANT, sessionId, operationId); for (int attempts = 2; attempts <= 8; attempts++) { @@ -976,7 +974,7 @@ class ManagedCwdChangeOperationTest { String sessionId = fixture.createBoundSession(TENANT, WS); String operationId = begin(fixture, sessionId, "key", "digest", "services/b", 1).operation().operationId(); - warmer.refuseAlways(WorkspaceExecutionStore.unavailableTransient( + warmer.refuseAlways(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailableTransient( new java.io.IOException("stale mount handle"))); // Three deliveries pass the lifecycle budget of 2: the close/delete // terminal arm would fire at the second. The cwd change keeps its @@ -1048,7 +1046,7 @@ class ManagedCwdChangeOperationTest { private static void assertRefusal(ManagedAgentException error, HttpStatus status, String code) { - assertThat(error.getStatus()).isEqualTo(status); + assertThat(error.getStatus()).isEqualTo(status.value()); assertThat(error.getCode()).isEqualTo(code); } @@ -1114,8 +1112,8 @@ class ManagedCwdChangeOperationTest { } }; ObjectMapper mapper = new ObjectMapper(); - ManagedAgentStore store = new ManagedAgentStore(jdbc, mapper, clock, - ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + ManagedAgentStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, clock, + ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); return new Fixture(store, jdbc, mapper, properties, clock); } @@ -1178,13 +1176,13 @@ class ManagedCwdChangeOperationTest { } void insertOperation(String sessionId, String operationId, String kind, String state, String delivery, String taskId) { - String scope = ManagedSessionStore.sessionScopeKey(TENANT, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, sessionId); String target = ManagedSchemaKeys.key(sessionId); jdbc.update("INSERT INTO ma_operation (operation_key, scope_key, target_kind, target_key, target_id, request_json," + " tenant_id, session_id, operation_id, operation_kind, request_digest, state, phase, admission_stage," + " delivery_state, session_status_before, task_id, gmt_create, gmt_modified)" + " VALUES (?, ?, 'SESSION', ?, ?, '{}', ?, ?, ?, ?, 'digest', ?, ?, 'JAVA_DURABLE', ?, 'ACTIVE', ?, 0, 0)", - ManagedSchemaKeys.operationKey(com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind.valueOf(kind), + ManagedSchemaKeys.operationKey(com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind.valueOf(kind), scope, target, operationId), scope, target, sessionId, TENANT, sessionId, operationId, kind, state, state, delivery, taskId); } @@ -1193,7 +1191,7 @@ class ManagedCwdChangeOperationTest { jdbc.update("INSERT INTO ma_session_record (session_scope_key, record_key, record_kind, domain, record_id, tenant_id," + " workspace_id, session_id, action_id, action_version, revision, state, options_json, gmt_create)" + " VALUES (?, ?, 'ACTION', 'action', ?, ?, ?, ?, ?, 1, 1, 'requested', ?, 0)", - ManagedSessionStore.sessionScopeKey(TENANT, sessionId), ManagedSchemaKeys.key("action", actionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, sessionId), ManagedSchemaKeys.key("action", actionId), actionId, TENANT, WS, sessionId, actionId, mapper.valueToTree(java.util.Map.of("expiresAt", expiresAtMillis)).toString()); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedCwdOperationContractShapeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedCwdOperationContractShapeTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedCwdOperationContractShapeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedCwdOperationContractShapeTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventEnvelopeContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventEnvelopeContractTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventEnvelopeContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventEnvelopeContractTest.java index c87707ecf4e..755a3181976 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventEnvelopeContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventEnvelopeContractTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.event.ManagedEventEnvelope; -import com.alibaba.qwen.code.managedagent.event.ManagedEventEnvelopes; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelope; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelopes; import com.alibaba.qwen.code.managedagent.model.StoreModels.EventRecord; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventIdentityMigrationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventIdentityMigrationTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventIdentityMigrationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventIdentityMigrationTest.java index 7bdd0942406..f643df1334c 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventIdentityMigrationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedEventIdentityMigrationTest.java @@ -30,13 +30,13 @@ class ManagedEventIdentityMigrationTest { var jdbc = new ManagedSchemaJdbcTemplate(source); var transactions = new TransactionTemplate(new DataSourceTransactionManager(source)); var store = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String tenant = "tenant"; String session = transactions diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionCommandVerifierTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionCommandVerifierTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionCommandVerifierTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionCommandVerifierTest.java index 0addfe87d01..c045995e4cb 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionCommandVerifierTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionCommandVerifierTest.java @@ -1,13 +1,12 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionCommandVerifier; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.runtimebroker.ManagedExtensionExecutionProfile; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; @@ -16,8 +15,8 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -176,7 +175,7 @@ class ManagedExtensionCommandVerifierTest { private ObjectNode resource(String kind, JsonNode body) throws IOException { byte[] bytes = JSON.writeValueAsBytes(body); String id = UUID.randomUUID().toString(); - String scope = ManagedSessionStore.sessionScopeKey("tenant", "harness"); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "harness"); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind," + " schema_version, byte_length, sha256, storage_kind, inline_bytes, state, gmt_create)" + " VALUES (?, ?, 'tenant', 'workspace', 'harness', ?, ?, 1, ?, ?, 'MYSQL_INLINE', ?, 'REFERENCED', 0)", @@ -188,7 +187,7 @@ class ManagedExtensionCommandVerifierTest { private void project(String domain, String id, String opening, JsonNode ref) { jdbc.update("INSERT INTO ma_session_record (session_scope_key, record_key, tenant_id, workspace_id, session_id, domain, record_id," + " operation_hash, revision, record_resource_id, gmt_create) VALUES (?, ?, 'tenant', 'workspace', 'harness', ?, ?, ?, 1, ?, 0)", - ManagedSessionStore.sessionScopeKey("tenant", "harness"), ManagedExtensionProjection.recordKey("harness", domain, id), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "harness"), ManagedExtensionProjection.recordKey("harness", domain, id), domain, id, DigestUtils.sha256Hex(opening), ref.get("resourceId").textValue()); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionConfigurationAdmissionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionConfigurationAdmissionTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionConfigurationAdmissionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionConfigurationAdmissionTest.java index 3a737d21d3d..ca676ba7bba 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionConfigurationAdmissionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionConfigurationAdmissionTest.java @@ -1,11 +1,10 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedConfigInstall; import com.alibaba.qwen.code.managedagent.persistence.ManagedMcpSettings; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -109,8 +108,8 @@ class ManagedExtensionConfigurationAdmissionTest { assertThatThrownBy(() -> commit(journal, id, workspace, committed, true, true)).isInstanceOf(ManagedAgentException.class); assertThat(ManagedMcpSettings.find(jdbc, "tenant", id).path("phase").asText()).isEqualTo("prepared"); commit(journal, id, workspace, committed, true, false); - assertThat(transaction(() -> ManagedConfigInstall.blocked(jdbc, "tenant", id))).isTrue(); - assertThat(transaction(() -> ManagedConfigInstall.blocked(jdbc, "tenant", sibling))).isFalse(); + assertThat(transaction(() -> com.alibaba.qwen.code.managedagent.persistence.PersistenceTestAccess.blocked(jdbc, "tenant", id))).isTrue(); + assertThat(transaction(() -> com.alibaba.qwen.code.managedagent.persistence.PersistenceTestAccess.blocked(jdbc, "tenant", sibling))).isFalse(); assertThatCode(() -> transaction(() -> sessions.requireExtensionManagement("tenant", "actor", sibling, UUID.randomUUID().toString(), true))).doesNotThrowAnyException(); assertThatThrownBy(() -> transaction(() -> sessions.requireExtensionManagement("tenant", "actor", id, UUID.randomUUID().toString(), true))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("session_config_recovery_required")); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionConfigurationMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionConfigurationMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionConfigurationMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionConfigurationMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionCreationMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionCreationMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionCreationMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionCreationMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionProjectionContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionProjectionContractTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionProjectionContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionProjectionContractTest.java index f6359f0483d..8cd6c994c55 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionProjectionContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionProjectionContractTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection.TaskProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.TaskProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordContractTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordContractTest.java index 4408ec7d36b..680c7fce279 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordContractTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordStoreTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordStoreTest.java index 85f739c365f..ef497b1c801 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedExtensionRecordStoreTest.java @@ -1,18 +1,17 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicList; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicTask; import com.alibaba.qwen.code.managedagent.model.StoreModels.EventRecord; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedTaskEventStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; import com.alibaba.qwen.code.managedagent.session.ManagedTaskService; import com.fasterxml.jackson.databind.JsonNode; @@ -261,7 +260,7 @@ class ManagedExtensionRecordStoreTest { run.put("definitionRevision", 1); run.put("targetSessionId", sessionId); String occurrenceKey = run.get("occurrenceKey").asText(); - run.put("automationRunId", AutomationLedgerStore.automationRunId( + run.put("automationRunId", com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationRunId( "schedule-" + sessionId, occurrenceKey)); // H6b: no definition, a stale revision and an underived id refuse // before anything is written; the bound run commits. @@ -1015,7 +1014,7 @@ class ManagedExtensionRecordStoreTest { var instances = new com.alibaba.qwen.code.managedagent.persistence.ChannelInstanceStore(jdbc); var ledger = new com.alibaba.qwen.code.managedagent.persistence.JdbcChannelDeliveryRepository(jdbc); String deliveryId = delivery.path("deliveryId").asText(); - ledger.findOrCreate(new com.alibaba.qwen.code.managedagent.persistence.ChannelDeliveryRepository.ChannelDelivery( + ledger.findOrCreate(new com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository.ChannelDelivery( TENANT, "race-channel", deliveryId, "seg-1", 0, "planned", null, 0, 0)); var oldClaim = instances.claim(TENANT, "race-channel", deliveryId, sessionId); ledger.transition(TENANT, "race-channel", deliveryId, "planned", "sending", null, oldClaim.claimGeneration()); @@ -1023,7 +1022,7 @@ class ManagedExtensionRecordStoreTest { ObjectNode unknown = delivery.deepCopy(); ((ObjectNode) unknown.path("run")).put("state", "waiting"); ((ObjectNode) unknown.path("run").path("delivery")).put("state", "unknown"); - var stale = journal.requestDomain(com.alibaba.qwen.code.managedagent.persistence.ChannelInstanceStore.reconciliationOperationId(oldClaim), + var stale = journal.requestDomain(com.alibaba.qwen.code.managedagent.repository.ChannelInstanceStore.reconciliationOperationId(oldClaim), "channel_delivery", unknown, List.of(), 4000); long before = jdbc.queryForObject("SELECT COUNT(*) FROM ma_session_journal_tx WHERE tenant_id=? AND session_id=?", Long.class, TENANT, sessionId); var newClaim = instances.touchClaim(TENANT, "race-channel", deliveryId); @@ -1033,7 +1032,7 @@ class ManagedExtensionRecordStoreTest { assertThat(records.listRecords(TENANT, sessionId, "channel_delivery").getFirst().path("run").path("delivery").path("state").asText()).isEqualTo("sending"); assertThat(ledger.transition(TENANT, "race-channel", deliveryId, "sending", "unknown", null, oldClaim.claimGeneration())).isNull(); assertThat(instances.reserveReconciliation(newClaim)).isTrue(); - var current = journal.requestDomain(com.alibaba.qwen.code.managedagent.persistence.ChannelInstanceStore.reconciliationOperationId(newClaim), + var current = journal.requestDomain(com.alibaba.qwen.code.managedagent.repository.ChannelInstanceStore.reconciliationOperationId(newClaim), "channel_delivery", unknown, List.of(), 4000); journal.commit(current); journal.committed(current); @@ -1733,8 +1732,8 @@ class ManagedExtensionRecordStoreTest { // Stage H rule that refuses a revision is a conflict. assertThat(error.getStatus()).as(label).isEqualTo( ManagedSessionStoreModels.ERROR_INVALID_REQUEST - .equals(code) ? HttpStatus.BAD_REQUEST - : HttpStatus.CONFLICT); + .equals(code) ? HttpStatus.BAD_REQUEST.value() + : HttpStatus.CONFLICT.value()); if (message != null) { assertThat(error.getMessage()).as(label) .contains(message); @@ -2311,7 +2310,7 @@ class ManagedExtensionRecordStoreTest { }; ManagedTaskService service = new ManagedTaskService(agents, records, racing); - String cursor = ManagedTaskEventStore.encodeCursor(TENANT, sessionId, taskId, 2); + String cursor = com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.encodeCursor(TENANT, sessionId, taskId, 2); assertThatThrownBy(() -> service.queryWebShellTaskEvents(TENANT, TENANT, sessionId, taskId, cursor, 10)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { @@ -2339,7 +2338,7 @@ class ManagedExtensionRecordStoreTest { taskId, null, 10); assertThat(page.data()).hasSize(1); assertThat(page.nextCursor()).isEqualTo( - ManagedTaskEventStore.encodeCursor(TENANT, sessionId, taskId, 1)); + com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.encodeCursor(TENANT, sessionId, taskId, 1)); // An as-of-now explicit cursor still admits: the floor did not move // past it while the read was happening. assertThat(tasks.queryWebShellTaskEvents(TENANT, TENANT, sessionId, @@ -2364,7 +2363,7 @@ class ManagedExtensionRecordStoreTest { assertThat(error.getCode()).isEqualTo( ManagedSessionStoreModels.ERROR_RESOURCE_MISSING); assertThat(error.getStatus()).isEqualTo( - HttpStatus.CONFLICT); + HttpStatus.CONFLICT.value()); }); // A row that no longer reads back, two ways the store knows it. String corruptSession = UUID.randomUUID().toString(); @@ -2389,7 +2388,7 @@ class ManagedExtensionRecordStoreTest { assertThat(error.getCode()).isEqualTo( "managed_session_resource_corrupt"); assertThat(error.getStatus()).isEqualTo( - HttpStatus.INTERNAL_SERVER_ERROR); + HttpStatus.INTERNAL_SERVER_ERROR.value()); }); // Its Session is not the request's, though the row says otherwise. String foreignSession = UUID.randomUUID().toString(); @@ -2410,7 +2409,7 @@ class ManagedExtensionRecordStoreTest { assertThat(error.getCode()).isEqualTo( "managed_session_not_found"); assertThat(error.getStatus()) - .isEqualTo(HttpStatus.NOT_FOUND); + .isEqualTo(HttpStatus.NOT_FOUND.value()); }); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookAdmissionIndexTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookAdmissionIndexTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookAdmissionIndexTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookAdmissionIndexTest.java index a914c99decb..7d563f5ec69 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookAdmissionIndexTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookAdmissionIndexTest.java @@ -4,9 +4,9 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.fasterxml.jackson.databind.node.ObjectNode; import org.h2.jdbcx.JdbcDataSource; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookCatalogContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookCatalogContractTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookCatalogContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookCatalogContractTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookRecordContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookRecordContractTest.java similarity index 87% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookRecordContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookRecordContractTest.java index 00931f27852..3bb767cc45c 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookRecordContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedHookRecordContractTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -19,7 +19,7 @@ public class ManagedHookRecordContractTest { private static final ObjectMapper JSON = new ObjectMapper(); @Test - void validatesTheSharedRecordsAndStarts() throws IOException { + public void validatesTheSharedRecordsAndStarts() throws IOException { JsonNode fixtures = fixtures(); for (JsonNode fixture : fixtures.get("cases")) { String domain = fixture.get("domain").textValue(); @@ -39,7 +39,7 @@ public class ManagedHookRecordContractTest { } @Test - void validatesTheSharedSuccessors() throws IOException { + public void validatesTheSharedSuccessors() throws IOException { JsonNode fixtures = fixtures(); for (JsonNode fixture : fixtures.get("successors")) { String domain = fixture.get("domain").textValue(); @@ -63,7 +63,7 @@ public class ManagedHookRecordContractTest { throw new IOException("Hook contract fixtures not found"); } - static JsonNode merge(JsonNode base, JsonNode patch) { + public static JsonNode merge(JsonNode base, JsonNode patch) { ObjectNode value = base == null || !base.isObject() ? JSON.createObjectNode() : base.deepCopy(); patch.fields().forEachRemaining(entry -> value.set(entry.getKey(), entry.getValue().isObject() ? merge(value.get(entry.getKey()), entry.getValue()) : entry.getValue().deepCopy())); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMaterializationDeferTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMaterializationDeferTest.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMaterializationDeferTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMaterializationDeferTest.java index 21cfc718db3..5f426feab3f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMaterializationDeferTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMaterializationDeferTest.java @@ -42,12 +42,12 @@ class ManagedMaterializationDeferTest { + " session_id, stream_kind, stream_id, last_sequence, covered_sequence," + " gmt_modified) VALUES (?, ?, ?, ?, ?, ?, ?)", "tenant", "session", "SESSION", "", 9L, 9L, 1L); - ManagedAgentStore store = new ManagedAgentStore(jdbc, + ManagedAgentStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.fixed(Instant.ofEpochMilli(123456789L), ZoneOffset.UTC), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); store.deferMaterializationTarget("tenant", "session"); @@ -92,12 +92,12 @@ class ManagedMaterializationDeferTest { "tenant", session, "SESSION", "", 9L, 1L, 1000L * (i + 1)); } - ManagedAgentStore store = new ManagedAgentStore(jdbc, + ManagedAgentStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.fixed(Instant.ofEpochMilli(123456789L), ZoneOffset.UTC), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); assertThat(store.findMaterializationTargets(2)) .containsExactly( diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpCatalogContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpCatalogContractTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpCatalogContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpCatalogContractTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpRecordContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpRecordContractTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpRecordContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpRecordContractTest.java index d0c5f3c5ed0..6cdc98d0f1f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpRecordContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpRecordContractTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpSettingsMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpSettingsMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpSettingsMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpSettingsMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpSettingsStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpSettingsStoreTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpSettingsStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpSettingsStoreTest.java index b0329dfa224..55e9db8654d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpSettingsStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedMcpSettingsStoreTest.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.persistence.ManagedMcpSettings; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelContextTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelContextTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelContextTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelContextTest.java index 93a955362c8..f1d3db419ec 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelContextTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelContextTest.java @@ -1,16 +1,16 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellModelContext; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.session.HarnessCoordinator; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; @@ -77,7 +77,7 @@ class ManagedModelContextTest { }; jdbc = new ManagedSchemaJdbcTemplate(counted); records = new ManagedExtensionRecordStore(jdbc); - sessions = new ManagedSessionStore(jdbc, records); + sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc, records)); transactions = new TransactionTemplate(new DataSourceTransactionManager(counted)); } @@ -274,9 +274,9 @@ class ManagedModelContextTest { "output_committed", 1_000)); var properties = new ManagedAgentProperties(); var registry = new ManagedWorkspaceRegistry(jdbc); - var state = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, registry, properties); + var state = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var service = new ManagedAgentService(state, new RequestDigests(), mock(HarnessCoordinator.class), - mock(HarnessConnector.class), registry); + mock(HarnessConnector.class), registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); assertThat(service.getWebShellSession(TENANT, "reader", "summary").modelContext()) .isEqualTo(context("summary")); assertThat(service.listWebShellSessions(TENANT, "reader", null, 100).data()) @@ -291,8 +291,8 @@ class ManagedModelContextTest { @Test void compactionCommitsItsIndependentRevisionAndInvalidatesUsageExactlyOnce() { var journal = open(TENANT, "compaction", true); - var state = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + var state = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); assertThat(state.contextCompressionState(TENANT, "compaction").compactionRevision()).isZero(); commit(journal, request(journal, "before", usage("before", 80, 100), USAGE_KIND, "output_committed", 1_000)); @@ -339,8 +339,8 @@ class ManagedModelContextTest { commit(journal, request(journal, "newer-usage", usage("newer", 12, 100), USAGE_KIND, "output_committed", 2_000)); jdbc.update("DELETE FROM ma_session_record WHERE domain = 'hosted_context_compaction'"); - var state = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + var state = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); assertThat(state.contextCompressionState(TENANT, "old-compaction").compactionRevision()) .isEqualTo(compaction.firstSequence()); assertThat(context("old-compaction").modelId()).isEqualTo("newer"); @@ -353,16 +353,16 @@ class ManagedModelContextTest { @Test void aRefusedNativeCommitKeepsItsDurableCandidateAndCanContinueAfterAuthorityReturns() { var allowed = new java.util.concurrent.atomic.AtomicBoolean(false); - var guard = mock(com.alibaba.qwen.code.managedagent.session.AgentStateStore.class); + var guard = mock(com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.class); org.mockito.Mockito.doAnswer(invocation -> { if (!allowed.get()) { - throw new ManagedAgentException(org.springframework.http.HttpStatus.CONFLICT, + throw new ManagedAgentException(409, "context_compression_authorization_revoked", "Actor grant was revoked"); } return null; }).when(guard).requireContextCompressionCommit(TENANT, "manual", "op_manual"); records = new ManagedExtensionRecordStore(jdbc, guard); - sessions = new ManagedSessionStore(jdbc, records); + sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc, records)); var journal = open(TENANT, "manual", true); byte[] bytes = "{}".getBytes(StandardCharsets.UTF_8); var pinned = ManagedExtensionRecordStoreTest.hookResource("pinned", "managed-message", bytes); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelSelectionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelSelectionTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelSelectionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelSelectionTest.java index 008c5a76cf9..12c59b9f4e8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelSelectionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedModelSelectionTest.java @@ -2,8 +2,10 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.daemon.SubmitHarnessTurn; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellModelChangeResult; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellModelSelection; @@ -11,11 +13,9 @@ import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionLineage; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.session.HarnessCoordinator; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import com.fasterxml.jackson.databind.ObjectMapper; @@ -118,7 +118,7 @@ class ManagedModelSelectionTest { properties.getRuntimeBroker().setWorkspaceMounts(List.of( new ManagedAgentProperties.RuntimeBroker.WorkspaceMount(tenant, "storage", "/tmp/model-selection"))); properties.setModelCatalog(List.of(option("model-a"))); - var enabled = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, registry, properties); + var enabled = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); when(harness.isWorkspaceFilesAvailable()).thenReturn(true); when(harness.supportsApprovalSelection()).thenReturn(true); when(harness.supportsPublicChildAgents()).thenReturn(childrenSupported); @@ -261,11 +261,11 @@ class ManagedModelSelectionTest { jdbc.update("INSERT INTO ma_session_record (session_scope_key,record_key,tenant_id,session_id," + "domain,record_id,revision,gmt_create,record_kind,state,action_id,action_version,options_json)" + " VALUES (?,?,?,?,'action','action',1,1,'ACTION','requested','action',1,?)", - ManagedSessionStore.sessionScopeKey(tenant, session), "a".repeat(64), tenant, session, + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), "a".repeat(64), tenant, session, "{\"expiresAt\":" + (System.currentTimeMillis() + 100_000) + "}"); } if (shape.equals("recovery")) { - transaction().execute(status -> new ManagedSessionStore(jdbc).acquireWriter(tenant, session, + transaction().execute(status -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).acquireWriter(tenant, session, "model-selection-writer-token-0123456789", new AcquireWriterRequest("ws", "writer", 60_000L))); jdbc.update("UPDATE ma_session_head SET recovery_status='BLOCKED' WHERE tenant_id=?", tenant); } @@ -320,8 +320,8 @@ class ManagedModelSelectionTest { @Test void aSelectionCommittedDuringSummaryReadCannotReviveThePreviousContext() { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); - String record = com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection.recordKey(session, + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); + String record = com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.recordKey(session, "hosted_context_usage", "latest"); byte[] bytes = "{\"version\":1,\"model\":\"model-a\",\"attempts\":[{\"promptTokenCount\":12,\"contextWindowSize\":100}]}" .getBytes(java.nio.charset.StandardCharsets.UTF_8); @@ -346,8 +346,8 @@ class ManagedModelSelectionTest { @Test void switchingClearsOnlyTheLatestUsageProjectionAndNeverReplaysTheClear() { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); - String record = com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection.recordKey(session, + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); + String record = com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.recordKey(session, "hosted_context_usage", "latest"); jdbc.update("INSERT INTO ma_session_record (session_scope_key,record_key,tenant_id,workspace_id," + "session_id,domain,record_id,revision,record_resource_id,gmt_create,settled_at)" @@ -373,7 +373,7 @@ class ManagedModelSelectionTest { if (catalog) { properties.setModelCatalog(List.of(option("model-a"), option("model-b"))); } - return new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, registry, properties); + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } private ManagedAgentProperties.ModelOption option(String id) { @@ -384,7 +384,7 @@ class ManagedModelSelectionTest { } private ManagedAgentService service(ManagedAgentStore source) { - return new ManagedAgentService(source, new RequestDigests(), mock(HarnessCoordinator.class), harness, registry); + return new ManagedAgentService(source, new RequestDigests(), mock(HarnessCoordinator.class), harness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); } private WebShellModelChangeResult change(String actor, String key, String model, long revision) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsMySqlIT.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsMySqlIT.java index 1f571aed656..3d7df08400d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsMySqlIT.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.persistence.IsolatedManagedSchemaMySqlTestDatabase; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsTest.java index 42531df3669..e5d43973f00 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedNativeJournalSnapshotsTest.java @@ -1,11 +1,11 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.persistence.ManagedNativeJournalSnapshots; import com.alibaba.qwen.code.managedagent.persistence.ManagedNativeJournalSnapshots.PublishRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -51,7 +51,7 @@ class ManagedNativeJournalSnapshotsTest { ManagedSchema.initialize(source); jdbc = new FaultJdbc(source); tx = new TransactionTemplate(new DataSourceTransactionManager(source)); - sessions = new ManagedSessionStore(jdbc); + sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); snapshots = new ManagedNativeJournalSnapshots(jdbc, sessions, JSON); journal = new ExtensionRecordJournal(sessions, TENANT, WORKSPACE, SESSION); tx.executeWithoutResult(status -> journal.open()); @@ -129,14 +129,14 @@ class ManagedNativeJournalSnapshotsTest { @Test void aPreexistingOrdinaryResourceCannotBeOverwrittenByAManifestClaim() { - String scope = ManagedSessionStore.sessionScopeKey(TENANT, SESSION); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, SESSION); String id = "managed-native-journal-snapshot:" - + com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(scope, "collision"); + + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scope, "collision"); byte[] bytes = "ordinary".getBytes(java.nio.charset.StandardCharsets.UTF_8); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id," + " resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, state, gmt_create)" + " VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?, 'MYSQL_INLINE', ?, 'REFERENCED', 1)", - ManagedSessionStore.resourceKey(scope, id), scope, TENANT, WORKSPACE, SESSION, id, "managed-input", + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, id), scope, TENANT, WORKSPACE, SESSION, id, "managed-input", bytes.length, org.apache.commons.codec.digest.DigestUtils.sha256Hex(bytes), bytes); assertThatThrownBy(() -> snapshots.publish(TENANT, SESSION, TOKEN, request("collision"))) .isInstanceOf(ManagedAgentException.class); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedOperationSchemaUpgradeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedOperationSchemaUpgradeTest.java similarity index 89% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedOperationSchemaUpgradeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedOperationSchemaUpgradeTest.java index 8681c72b6aa..b5196133b3f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedOperationSchemaUpgradeTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedOperationSchemaUpgradeTest.java @@ -2,10 +2,9 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; @@ -28,14 +27,14 @@ class ManagedOperationSchemaUpgradeTest { ManagedSchema.initialize(source); var jdbc = new JdbcTemplate(source); var store = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); - String scope = ManagedSessionStore.sessionScopeKey("t", "s"); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("t", "s"); jdbc.update( "INSERT INTO ma_operation" + " (operation_key,scope_key,operation_id,operation_kind,tenant_id,session_id," diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRenameReconciliationMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRenameReconciliationMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRenameReconciliationMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRenameReconciliationMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRenameReconciliationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRenameReconciliationTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRenameReconciliationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRenameReconciliationTest.java index bbd49d8de34..9e0f47293b4 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRenameReconciliationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRenameReconciliationTest.java @@ -1,6 +1,7 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.AcceptanceVerdict; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; @@ -10,9 +11,8 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionMutationComma import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; import org.h2.jdbcx.JdbcDataSource; @@ -21,7 +21,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.aop.framework.ProxyFactory; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource; @@ -100,9 +99,9 @@ class ManagedRenameReconciliationTest { return Instant.ofEpochMilli(now.get()); } }; - store = new ManagedAgentStore(jdbc, new ObjectMapper(), clock, + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), clock, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); ProxyFactory proxy = new ProxyFactory(store); proxy.setInterfaces(AgentStateStore.class); proxy.addAdvice(new TransactionInterceptor( @@ -464,7 +463,7 @@ class ManagedRenameReconciliationTest { private static void refused(Runnable call, String code) { assertThatThrownBy(call::run).isInstanceOfSatisfying( ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()).isEqualTo(code); }); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRuntimeRetentionRaceMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRuntimeRetentionRaceMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRuntimeRetentionRaceMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRuntimeRetentionRaceMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRuntimeRetentionRaceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRuntimeRetentionRaceTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRuntimeRetentionRaceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRuntimeRetentionRaceTest.java index fc503e42854..748d8ffa667 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRuntimeRetentionRaceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedRuntimeRetentionRaceTest.java @@ -1,15 +1,11 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.persistence.ManagedRuntimeRetentionGuard; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceOperatorRecoveryStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceTestStorage; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeRetention; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; @@ -18,8 +14,12 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeRetention; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.flywaydb.core.Flyway; import org.h2.jdbcx.JdbcDataSource; @@ -240,7 +240,7 @@ class ManagedRuntimeRetentionRaceTest { new RuntimeSession("harness", "session", "bootstrap", scope), binding.getBindingId(), binding.getGeneration(), RuntimeSessionRecord.State.ACQUIRING, 0, Instant.now())); var context = new ContextBinding("tenant", "workspace", 1, "storage", ".", "context", 1); - var holders = new WorkspaceExecutionStore(jdbc, manager); + var holders = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, manager)); if (reference == Reference.HOLDER) { write = () -> holders.claim(context, session); return; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSchemaJournalAtomicityTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSchemaJournalAtomicityTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSchemaJournalAtomicityTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSchemaJournalAtomicityTest.java index 35e5192cff7..ed46766c286 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSchemaJournalAtomicityTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSchemaJournalAtomicityTest.java @@ -1,9 +1,9 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; @@ -52,7 +52,7 @@ class ManagedSchemaJournalAtomicityTest { transactions = new TransactionTemplate(new DataSourceTransactionManager(source)); journal = new ExtensionRecordJournal( - new ManagedSessionStore(jdbc), tenant, "workspace", session); + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)), tenant, "workspace", session); transactions.executeWithoutResult(status -> journal.open()); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSchemaJournalMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSchemaJournalMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSchemaJournalMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSchemaJournalMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCommandRecordContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCommandRecordContractTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCommandRecordContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCommandRecordContractTest.java index a8b8e03ce8c..fd4c4cb97bf 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCommandRecordContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCommandRecordContractTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCreationSkillsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCreationSkillsTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCreationSkillsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCreationSkillsTest.java index cc65fca24a8..769b4ab82ef 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCreationSkillsTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionCreationSkillsTest.java @@ -3,9 +3,9 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackageService; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionSkills; import com.fasterxml.jackson.databind.JsonNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionLifecycleTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionLifecycleTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionLifecycleTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionLifecycleTest.java index 310a02faffc..8c8c5bd1e45 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionLifecycleTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionLifecycleTest.java @@ -1,22 +1,22 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.daemon.DaemonHttpException; import com.alibaba.qwen.code.managedagent.ManagedAgentServerIntegrationTest.FixtureHarness; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionMutationKind; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.SealWriterRequest; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.fasterxml.jackson.databind.JsonNode; @@ -33,7 +33,6 @@ import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.web.servlet.MockMvc; @@ -433,10 +432,10 @@ class ManagedSessionLifecycleTest { HarnessConnector available = mock(HarnessConnector.class, delegatesTo(harness)); doReturn(true).when(available).isAvailable(); ManagedAgentService subject = new ManagedAgentService(store, - new RequestDigests(), null, available, null); + new RequestDigests(), null, available, null, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); assertThatThrownBy(() -> subject.renameSession(tenant, null, "rename-closing", sessionId, "blocked")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()).isEqualTo("session_operation_active"); }); } finally { @@ -543,17 +542,17 @@ class ManagedSessionLifecycleTest { doReturn(true).when(controlled).supportsFencedTitles(); doReturn(new HarnessConnector.Attachment("boot")).when(controlled) .createOrLoad(anyString(), anyString(), anyBoolean(), anyBoolean()); - DaemonHttpException refusal = mock(DaemonHttpException.class); + HarnessHttpFailure refusal = mock(HarnessHttpFailure.class); doReturn(privateStatus).when(refusal).getStatusCode(); doReturn(code).when(refusal).getErrorCode(); doThrow(refusal).when(controlled).rename(anyString(), anyString(), anyString(), anyLong()); ManagedAgentService subject = new ManagedAgentService(store, - new RequestDigests(), null, controlled, null); + new RequestDigests(), null, controlled, null, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); harness.setAvailable(false); try { assertThatThrownBy(() -> subject.renameSession(tenant, null, "superseded", sessionId, "title")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus().value()).isEqualTo(expectedStatus); + assertThat(error.getStatus()).isEqualTo(expectedStatus); assertThat(error.getCode()).isEqualTo(expectedStatus == 409 ? "session_mutation_superseded" : "hosted_harness_unavailable"); }); @@ -570,10 +569,10 @@ class ManagedSessionLifecycleTest { HarnessConnector unavailable = mock(HarnessConnector.class); doReturn(!disabled).when(unavailable).isAvailable(); ManagedAgentService subject = new ManagedAgentService(store, - new RequestDigests(), null, unavailable, null); + new RequestDigests(), null, unavailable, null, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); assertThatThrownBy(() -> subject.renameSession(tenant, null, "refused", sessionId, "new title")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + assertThat(error.getStatus()).isEqualTo(503); assertThat(error.getCode()).isEqualTo(disabled ? "hosted_harness_disabled" : "hosted_harness_unavailable"); }); @@ -591,7 +590,7 @@ class ManagedSessionLifecycleTest { String sessionId = attachedSession(tenant); HarnessConnector controlled = mock(HarnessConnector.class, delegatesTo(harness)); ManagedAgentService subject = new ManagedAgentService(store, - new RequestDigests(), null, controlled, null); + new RequestDigests(), null, controlled, null, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); doReturn(true).when(controlled).isAvailable(); harness.setAvailable(false); try { @@ -605,7 +604,7 @@ class ManagedSessionLifecycleTest { assertThat(store.claimRename(candidate, "peer", Duration.ofMinutes(1))).isEmpty(); assertThatThrownBy(() -> subject.renameSession(tenant, null, "inline", sessionId, "new title")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> - assertThat(error.getStatus()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE)); + assertThat(error.getStatus()).isEqualTo(503)); assertThat(store.findCommand(tenant, "RENAME_SESSION", "inline").orElseThrow().status()).isEqualTo("PENDING"); return new HarnessConnector.Attachment(FixtureHarness.BOOT_ID); }).when(controlled).createOrLoad(anyString(), anyString(), anyBoolean(), anyBoolean()); @@ -884,7 +883,7 @@ class ManagedSessionLifecycleTest { }).when(faulted).beginSessionRename(anyString(), anyString(), anyString(), anyString(), anyString(), any()); ManagedAgentService subject = new ManagedAgentService(faulted, - new RequestDigests(), null, harness, null); + new RequestDigests(), null, harness, null, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); if ("cleanup".equals(phase)) { harness.failNextRename(); } @@ -892,7 +891,7 @@ class ManagedSessionLifecycleTest { assertThatThrownBy(() -> subject.renameSession(tenant, null, "key", sessionId, "renamed")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + assertThat(error.getStatus()).isEqualTo(503); assertThat(error.getCode()).isEqualTo("hosted_harness_unavailable"); }); // Park before pinning the retired state: claimRename takes (PENDING AND @@ -984,7 +983,7 @@ class ManagedSessionLifecycleTest { doThrow(failure).when(faulted).completeSessionRename(any(com.alibaba.qwen.code.managedagent.model.StoreModels.RenameDelivery.class), anyString(), anyString()); ManagedAgentService subject = new ManagedAgentService(faulted, - new RequestDigests(), null, harness, null); + new RequestDigests(), null, harness, null, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); // The 503 used to be the only trace of the failure: zero log lines // left on-call unable to tell a network fault from a daemon bug. ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) @@ -1023,7 +1022,7 @@ class ManagedSessionLifecycleTest { String sessionId = attachedSession(tenant); harness.failNextRename(); ManagedAgentService subject = new ManagedAgentService(store, - new RequestDigests(), null, harness, null); + new RequestDigests(), null, harness, null, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory.getLogger(ManagedAgentService.class); ch.qos.logback.core.read.ListAppender diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageRecordContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageRecordContractTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageRecordContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageRecordContractTest.java index b4e144255b3..de99ea1227c 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageRecordContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageRecordContractTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageStoreTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageStoreTest.java index 69527809ce1..d3ec8779d5e 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionMessageStoreTest.java @@ -1,11 +1,11 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; @@ -601,7 +601,7 @@ class ManagedSessionMessageStoreTest { assertThat(error.getCode()).as(label).isEqualTo(code); if (message != null) { assertThat(error.getStatus()).as(label) - .isEqualTo(HttpStatus.CONFLICT); + .isEqualTo(HttpStatus.CONFLICT.value()); assertThat(error.getMessage()).as(label) .contains(message); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationMigrationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationMigrationTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationMigrationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationMigrationTest.java index c5d604f10ff..e6db88a41e0 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationMigrationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationMigrationTest.java @@ -31,13 +31,13 @@ class ManagedSessionOperationMigrationTest { var jdbc = new JdbcTemplate(source); var tx = new TransactionTemplate(new DataSourceTransactionManager(source)); var store = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String session = tx.execute( status -> diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationStoreTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationStoreTest.java index 88ed517dec8..3da1296a2b0 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOperationStoreTest.java @@ -1,19 +1,18 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationTarget; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; @@ -442,7 +441,7 @@ class ManagedSessionOperationStoreTest { ManagedAgentStore store = store(); String sessionId = store.insertSessionCommand(TENANT, "CREATE_SESSION", "create", "digest", "qwen-code", null, null, List.of(), null).sessionId(); - String scope = ManagedSessionStore.sessionScopeKey(TENANT, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, sessionId); String target = ManagedSchemaKeys.key(sessionId); jdbc.update("INSERT INTO ma_operation (operation_key, scope_key, operation_id, operation_kind, tenant_id, session_id," + " target_kind, target_key, request_digest, request_json, state, phase, delivery_state, claim_until," @@ -503,7 +502,7 @@ class ManagedSessionOperationStoreTest { + ";MODE=MySQL;DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE"); ManagedSchema.initialize(dataSource); jdbc = new ManagedSchemaJdbcTemplate(dataSource); - return new ManagedAgentStore(jdbc, new ObjectMapper(), new Clock() { + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), new Clock() { @Override public ZoneId getZone() { return ZoneOffset.UTC; @@ -518,7 +517,7 @@ class ManagedSessionOperationStoreTest { public Instant instant() { return Instant.ofEpochMilli(now.get()); } - }, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + }, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } private List targets(ManagedAgentStore store) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOwnerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOwnerTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOwnerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOwnerTest.java index e979c48dc1e..674343dd24e 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOwnerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionOwnerTest.java @@ -1,9 +1,9 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.ObjectMapper; import org.flywaydb.core.Flyway; @@ -40,9 +40,9 @@ class ManagedSessionOwnerTest { "actor".getBytes(StandardCharsets.UTF_8)); var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - var store = new ManagedAgentStore(jdbc, new ObjectMapper(), + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var transactions = new TransactionTemplate( new DataSourceTransactionManager(source)); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionQueryMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionQueryMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionQueryMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionQueryMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionQueryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionQueryTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionQueryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionQueryTest.java index d3c8a449ec1..ea27dc510d6 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionQueryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionQueryTest.java @@ -40,7 +40,7 @@ class ManagedSessionQueryTest { DataSource source = dataSource(); ManagedSchema.initialize(source); jdbc = new ManagedSchemaJdbcTemplate(source); - sessions = new ManagedSessionStore(jdbc); + sessions = VerificationFixtures.configure(new ManagedSessionStore(jdbc)); tx = new TransactionTemplate(new DataSourceTransactionManager(source)); journal = new ExtensionRecordJournal(sessions, TENANT, WORKSPACE, SESSION); tx.executeWithoutResult(status -> journal.open()); @@ -125,7 +125,7 @@ class ManagedSessionQueryTest { TENANT, SESSION); String replacement = "replacement-writer-token-0123456789"; tx.executeWithoutResult(status -> sessions.acquireWriter(TENANT, SESSION, replacement, - new com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest( + new com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest( WORKSPACE, "replacement", 60_000L))); assertThatThrownBy(() -> sessions.recentTransactions(TENANT, WORKSPACE, SESSION, TOKEN, null, null, 1)) .isInstanceOf(com.alibaba.qwen.code.managedagent.exception.ManagedAgentException.class); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreBindingTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreBindingTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreBindingTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreBindingTest.java index 7970946a135..26bd180941d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreBindingTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreBindingTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreContractFixtureTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreContractFixtureTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreContractFixtureTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreContractFixtureTest.java index c759d4a2e6f..330a1aecf41 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreContractFixtureTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreContractFixtureTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreIntegrationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreIntegrationTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreIntegrationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreIntegrationTest.java index 504f39eb0d9..a360e4839cc 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreIntegrationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreIntegrationTest.java @@ -1,15 +1,15 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore.TaskRow; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.TaskRow; import com.alibaba.qwen.code.runtimebroker.ManagedCheckpointHistory; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -366,7 +366,7 @@ class ManagedSessionStoreIntegrationTest { Files.copy(stream, script); } Process process = new ProcessBuilder(System.getProperty("node.executable", "node"), - script.toString(), input.toString(), output.toString()).directory(Path.of("../..").toRealPath().toFile()) + script.toString(), input.toString(), output.toString()).directory(VerificationFixtures.repositoryRoot().toRealPath().toFile()) .redirectErrorStream(true).redirectOutput(log.toFile()).start(); try { assertThat(process.waitFor(30, TimeUnit.SECONDS)).as("client census generator finishes").isTrue(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreProcessFixtureMain.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreProcessFixtureMain.java similarity index 90% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreProcessFixtureMain.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreProcessFixtureMain.java index 3e103520965..2e4692a09e7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreProcessFixtureMain.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionStoreProcessFixtureMain.java @@ -1,15 +1,15 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitReceipt; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RestoreHead; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.WriterGrant; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitReceipt; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.RestoreHead; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.WriterGrant; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -48,8 +48,8 @@ public final class ManagedSessionStoreProcessFixtureMain { System.getenv().getOrDefault("D1_MYSQL_PASSWORD", "")); TransactionTemplate transactions = new TransactionTemplate( new DataSourceTransactionManager(dataSource)); - ManagedSessionStore store = new ManagedSessionStore( - new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource)); + ManagedSessionStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore( + new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource))); String action = required("D1_PROCESS_ACTION"); switch (action) { case "commit-and-crash" -> commitAndCrash(store, transactions); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionToolProfileMigrationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionToolProfileMigrationTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionToolProfileMigrationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedSessionToolProfileMigrationTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskCancelOperationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskCancelOperationTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskCancelOperationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskCancelOperationTest.java index 267027f9d73..bb38f445ff6 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskCancelOperationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskCancelOperationTest.java @@ -1,6 +1,9 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.TaskCancelOutcome; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.AcceptanceVerdict; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; @@ -10,17 +13,13 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationTarget; import com.alibaba.qwen.code.managedagent.model.StoreModels.ParkedOperation; import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore.TaskCancelOutcome; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.BeforeEach; @@ -91,8 +90,8 @@ class ManagedTaskCancelOperationTest { }; ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - store = new ManagedAgentStore(jdbc, new ObjectMapper(), clock, - ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), clock, + ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); sessionId = store.insertSessionCommand(TENANT, "CREATE_SESSION", "create", "digest", "qwen-code", null, null, List.of(), null).sessionId(); task(RUNNING_CHILD, "child_run", "child_agent", "running"); @@ -505,7 +504,7 @@ class ManagedTaskCancelOperationTest { void theStopStateReadsTheCommittedBody() { String recordKey = ManagedExtensionProjection.recordKey(sessionId, "child_run", "run-stop"); - String scope = ManagedSessionStore.sessionScopeKey(TENANT, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, sessionId); jdbc.update("INSERT INTO ma_session_record" + " (session_scope_key, record_key, tenant_id," + " workspace_id, session_id, domain, record_id," @@ -536,7 +535,7 @@ class ManagedTaskCancelOperationTest { + " task_kind, task_state, gmt_create)" + " VALUES (?, ?, ?, 'workspace', ?, ?, ?, ?, 1, ?," + " ?, ?, 1)", - ManagedSessionStore.sessionScopeKey(TENANT, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, sessionId), recordKey, TENANT, sessionId, domain, "record-" + recordKey, recordKey, "resource-" + recordKey, kind, state); } @@ -569,7 +568,7 @@ class ManagedTaskCancelOperationTest { String code) { assertThatThrownBy(call::run).isInstanceOfSatisfying( ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(status); + assertThat(error.getStatus()).isEqualTo(status.value()); assertThat(error.getCode()).isEqualTo(code); }); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskEventJournalTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskEventJournalTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskEventJournalTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskEventJournalTest.java index 305b9669101..c8f2a694229 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskEventJournalTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTaskEventJournalTest.java @@ -3,8 +3,8 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedTaskEventStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedTaskEventStore.EventPage; -import com.alibaba.qwen.code.managedagent.persistence.ManagedTaskEventStore.TaskEvent; +import com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.EventPage; +import com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.TaskEvent; import com.fasterxml.jackson.databind.JsonNode; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -81,11 +81,11 @@ class ManagedTaskEventJournalTest { } // A cursor is pure durable state: a rebuild derives the same string. TaskEvent first = page.events().getFirst(); - assertThat(ManagedTaskEventStore.encodeCursor(TENANT, sessionId, taskId, + assertThat(com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.encodeCursor(TENANT, sessionId, taskId, first.sequence())).isEqualTo( - ManagedTaskEventStore.encodeCursor(TENANT, sessionId, taskId, 1)); - assertThat(ManagedTaskEventStore.decodeCursor(TENANT, sessionId, taskId, - ManagedTaskEventStore.encodeCursor(TENANT, sessionId, taskId, + com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.encodeCursor(TENANT, sessionId, taskId, 1)); + assertThat(com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.decodeCursor(TENANT, sessionId, taskId, + com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.encodeCursor(TENANT, sessionId, taskId, first.sequence()))).isEqualTo(first.sequence()); // The first revision's view opens the task; a settled chain end // shows the terminal state of the last committed revision. @@ -172,7 +172,7 @@ class ManagedTaskEventJournalTest { taskId, outputSequence)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.CONFLICT); + .isEqualTo(HttpStatus.CONFLICT.value()); assertThat(error.getCode()).isEqualTo( ManagedTaskEventStore.CODE_FLOOR_PINNED); }); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamRecordContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamRecordContractTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamRecordContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamRecordContractTest.java index adcd911163e..f372e502e8c 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamRecordContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamRecordContractTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedTeamRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedTeamRecords; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamStoreTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamStoreTest.java index e666c3ec9e0..3a17d4d3728 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTeamStoreTest.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; @@ -432,7 +432,7 @@ class ManagedTeamStoreTest { assertThat(error.getCode()).as(label).isEqualTo(code); if (message != null) { assertThat(error.getStatus()).as(label) - .isEqualTo(HttpStatus.CONFLICT); + .isEqualTo(HttpStatus.CONFLICT.value()); assertThat(error.getMessage()).as(label) .contains(message); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedToolDetailsMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedToolDetailsMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedToolDetailsMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedToolDetailsMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedToolStageContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedToolStageContractTest.java similarity index 90% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedToolStageContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedToolStageContractTest.java index f75907d2b0b..b8b841e1871 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedToolStageContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedToolStageContractTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecords.InvalidRecordException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedToolStageRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords.InvalidRecordException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedToolStageRecords; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationMySqlIT.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationMySqlIT.java index 598ae567b3f..1c7318864bc 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationMySqlIT.java @@ -3,8 +3,7 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.model.StoreModels.Admission; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.persistence.IsolatedManagedSchemaMySqlTestDatabase; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -179,7 +178,7 @@ class ManagedTurnCommandOperationMySqlIT extends ManagedTurnCommandOperationTest } private void settledCancels(String session, String turnId, int count) { - String scope = ManagedSessionStore.sessionScopeKey(TENANT, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, session); String target = ManagedSchemaKeys.key(turnId); jdbc.batchUpdate("INSERT INTO ma_operation (operation_key, scope_key," + " session_scope_key, operation_id, operation_kind," diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationTest.java index 28d596cbd00..0fa866d9b9a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnCommandOperationTest.java @@ -12,9 +12,9 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.ProjectedEvent; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.session.CancelEvidence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.persistence.evidence.CancelEvidence; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; import org.h2.jdbcx.JdbcDataSource; @@ -87,9 +87,9 @@ class ManagedTurnCommandOperationTest { return Instant.ofEpochMilli(now.get()); } }; - store = new ManagedAgentStore(jdbc, new ObjectMapper(), clock, + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), clock, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); sessionId = store.insertSessionCommand(TENANT, "CREATE_SESSION", "create", "digest", "qwen-code", null, null, List.of(), null) .sessionId(); @@ -275,10 +275,10 @@ class ManagedTurnCommandOperationTest { return result; } }; - ManagedAgentStore racing = new ManagedAgentStore(racingJdbc, + ManagedAgentStore racing = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(racingJdbc, new ObjectMapper(), clock, ignored -> {}, new ManagedWorkspaceRegistry(racingJdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); assertThat(racing.turnInputAcceptance(TENANT, sessionId, admitted.operationId())).isEqualTo(AcceptanceVerdict.unknown()); assertThat(raced).isTrue(); @@ -832,7 +832,7 @@ class ManagedTurnCommandOperationTest { String code) { assertThatThrownBy(call::run).isInstanceOfSatisfying( ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(status); + assertThat(error.getStatus()).isEqualTo(status.value()); assertThat(error.getCode()).isEqualTo(code); }); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnQueryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnQueryTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnQueryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnQueryTest.java index 0392981e33d..56b4aee1c47 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnQueryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedTurnQueryTest.java @@ -140,9 +140,9 @@ class ManagedTurnQueryTest { assertThat(latest).containsKey(sessionId); var freshSession = new FreshSessionBeforeExecute( jdbc.getDataSource(), "environment.turn_id) IN ("); - var recompiling = new ManagedAgentStore( + var recompiling = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( new ManagedSchemaJdbcTemplate(freshSession), - objectMapper, Clock.systemUTC(), ignored -> {}, registry, new ManagedAgentProperties()); + objectMapper, Clock.systemUTC(), ignored -> {}, registry, new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); assertThat(recompiling.findLatestEnvironmentEvents(tenant, latest) .get(sessionId)) diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedUserMessageMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedUserMessageMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedUserMessageMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedUserMessageMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedUserMessageStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedUserMessageStoreTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedUserMessageStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedUserMessageStoreTest.java index e0d0480a83d..5a05a389f9d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedUserMessageStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedUserMessageStoreTest.java @@ -5,9 +5,9 @@ import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedUserMessageStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedUserMessageStore.Command; -import com.alibaba.qwen.code.managedagent.persistence.ManagedUserMessageStore.Message; -import com.alibaba.qwen.code.managedagent.persistence.ManagedUserMessageStore.Snapshot; +import com.alibaba.qwen.code.managedagent.repository.ManagedUserMessageStore.Command; +import com.alibaba.qwen.code.managedagent.repository.ManagedUserMessageStore.Message; +import com.alibaba.qwen.code.managedagent.repository.ManagedUserMessageStore.Snapshot; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; @@ -64,7 +64,7 @@ class ManagedUserMessageStoreTest { when(queueAgents.supportsUserMessageQueue()).thenReturn(true); when(queueAgents.userMessageInput(anyList())) .thenAnswer(call -> agents.userMessageInput(call.getArgument(0))); - messages = new ManagedUserMessageStore(jdbc, json, digests, queueAgents); + messages = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedUserMessageStore(jdbc, json, digests, queueAgents)); } @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWebShellRenameTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWebShellRenameTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWebShellRenameTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWebShellRenameTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceAdmissionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceAdmissionTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceAdmissionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceAdmissionTest.java index d2b2703aaf9..189ec533780 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceAdmissionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceAdmissionTest.java @@ -1,32 +1,30 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.daemon.DaemonHttpException; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.Attachment; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.Attachment; import com.alibaba.qwen.code.managedagent.harness.UnavailableHarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; import com.alibaba.qwen.code.managedagent.model.ApiModels.CommandAdmission; import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionMutationKind; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedUserMessageStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; import com.alibaba.qwen.code.managedagent.session.HarnessCoordinator; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.junit.jupiter.api.Test; @@ -110,7 +108,7 @@ class ManagedWorkspaceAdmissionTest { properties.getHarness().setWorkspaceShellEnabled(true); assertThat(properties.getHarness().isWorkspaceChildAgentsEnabled()).isTrue(); properties.getHarness().setApprovalMode("default"); - var enabled = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); + var enabled = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var tx = new TransactionTemplate(transactionManager); var selection = new WorkspaceSelection("ws-public-child", "."); var old = tx.execute(status -> enabled.insertWorkspaceSessionCommand(tenant, "actor-a", "old", "old-digest", @@ -130,7 +128,7 @@ class ManagedWorkspaceAdmissionTest { jdbc.update("UPDATE ma_session SET selected_approval_mode = 'auto', approval_revision = 3 WHERE tenant_id = ? AND session_id = ?", tenant, parent.sessionId()); properties.getHarness().setWorkspaceChildAgentsEnabled(false); - var disabled = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); + var disabled = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var lineage = new com.alibaba.qwen.code.managedagent.model.StoreModels.SessionLineage(parent.sessionId(), parent.sessionId(), "run-public", 1); var input = List.>of(Map.of("type", "text", "text", "write a file")); var autoSnapshot = ((com.fasterxml.jackson.databind.node.ObjectNode) snapshot.deepCopy()).put("approvalMode", "auto"); @@ -167,7 +165,7 @@ class ManagedWorkspaceAdmissionTest { properties.getHarness().setApprovalMode(mode); properties.getRuntimeBroker().setWorkspaceMounts(List.of( new ManagedAgentProperties.RuntimeBroker.WorkspaceMount(tenant, "storage-lead", "/workspace"))); - var admitted = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); + var admitted = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var transaction = new TransactionTemplate(transactionManager); var selection = new WorkspaceSelection("ws-lead", "."); var first = transaction.execute(status -> admitted.insertInternalTeamLeadSessionCommand( @@ -188,7 +186,7 @@ class ManagedWorkspaceAdmissionTest { properties.getHarness().setApprovalMode("default"); properties.getHarness().setWorkspaceShellEnabled(!shellEnabled); properties.getHarness().setWorkspaceFilesEnabled(false); - var changed = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); + var changed = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var replay = transaction.execute(status -> changed.insertInternalTeamLeadSessionCommand( tenant, "actor-a", "internal", "digest", "ignored", selection)); assertThat(replay.replayed()).isTrue(); @@ -216,7 +214,7 @@ class ManagedWorkspaceAdmissionTest { assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ma_turn WHERE tenant_id = ?", Integer.class, tenant)).isEqualTo(1); var projection = new ManagedAgentService(admitted, new RequestDigests(), mock(HarnessCoordinator.class), - new UnavailableHarnessConnector(), registry); + new UnavailableHarnessConnector(), registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); assertThat(projection.getPublicSession(tenant, "actor-a", first.sessionId()).capabilities().foregroundShell()).isNull(); var lineage = new com.alibaba.qwen.code.managedagent.model.StoreModels.SessionLineage( first.sessionId(), first.sessionId(), "member-run", 1); @@ -254,7 +252,7 @@ class ManagedWorkspaceAdmissionTest { if ("migration".equals(refusal)) { fenceStorage(tenant, "storage-lead"); } - var defensive = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); + var defensive = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); assertThatThrownBy(() -> new TransactionTemplate(transactionManager).execute(status -> defensive.insertInternalTeamLeadSessionCommand(tenant, "actor-a", "internal", "digest", null, new WorkspaceSelection("ws-lead", ".")))) @@ -276,15 +274,15 @@ class ManagedWorkspaceAdmissionTest { properties.getHarness().setWorkspaceFilesEnabled(true); properties.getHarness().setWorkspaceShellEnabled(true); properties.getHarness().setApprovalMode("default"); - var enabled = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); + var enabled = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String session = new TransactionTemplate(transactionManager).execute(status -> enabled.insertWorkspaceSessionCommand(tenant, "actor-a", "create", "digest", "qwen-code", null, null, List.of(), null, new WorkspaceSelection("ws-shell", ".")).sessionId()); jdbc.update("UPDATE ma_session SET approval_mode = ? WHERE tenant_id = ? AND session_id = ?", mode, tenant, session); properties.getHarness().setWorkspaceFilesEnabled(filesEnabled); - var defensive = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); + var defensive = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var projection = new ManagedAgentService(defensive, new RequestDigests(), mock(HarnessCoordinator.class), - new UnavailableHarnessConnector(), registry); + new UnavailableHarnessConnector(), registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); assertThat(projection.getPublicSession(tenant, "actor-a", session).capabilities().foregroundShell()).isFalse(); assertThat(projection.getWebShellSession(tenant, "actor-a", session).capabilities().foregroundShell()).isFalse(); } @@ -342,7 +340,7 @@ class ManagedWorkspaceAdmissionTest { properties.getHarness().setApprovalMode(mode); properties.getRuntimeBroker().setWorkspaceMounts(List.of( new ManagedAgentProperties.RuntimeBroker.WorkspaceMount(tenant, "storage-shell", "/workspace"))); - ManagedAgentStore enabled = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); + ManagedAgentStore enabled = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var transaction = new TransactionTemplate(transactionManager); String digest = "sha256:" + "a".repeat(64); var selection = new WorkspaceSelection("ws-shell", "."); @@ -366,13 +364,13 @@ class ManagedWorkspaceAdmissionTest { public boolean isWorkspaceFilesAvailable() { return true; } }; var coordinator = mock(com.alibaba.qwen.code.managedagent.session.HarnessCoordinator.class); - ManagedAgentService admitted = new ManagedAgentService(enabled, new RequestDigests(), coordinator, harness, registry); + ManagedAgentService admitted = new ManagedAgentService(enabled, new RequestDigests(), coordinator, harness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); List input = List.of(new InputBlock("text", "go")); var original = transaction.execute(status -> admitted.submitTurn(tenant, "actor-a", "original", session, input)); assertThat(admitted.getPublicSession(tenant, "actor-a", session).capabilities().foregroundShell()).isTrue(); properties.getHarness().setWorkspaceShellEnabled(false); - ManagedAgentStore disabled = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); - ManagedAgentService stopped = new ManagedAgentService(disabled, new RequestDigests(), coordinator, harness, registry); + ManagedAgentStore disabled = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); + ManagedAgentService stopped = new ManagedAgentService(disabled, new RequestDigests(), coordinator, harness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); assertThat(transaction.execute(status -> disabled.insertWorkspaceSessionCommand(tenant, "actor-a", "create-shell", digest, "qwen-code", null, null, List.of(), null, selection)).sessionId()) .isEqualTo(session); @@ -433,7 +431,7 @@ class ManagedWorkspaceAdmissionTest { grant(tenant, "ws-migration", "actor-a", true); ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - ManagedAgentStore enabled = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); + ManagedAgentStore enabled = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var transaction = new TransactionTemplate(transactionManager); String digest = "sha256:" + "a".repeat(64); String session = transaction.execute(status -> enabled.insertWorkspaceSessionCommand( @@ -848,8 +846,8 @@ class ManagedWorkspaceAdmissionTest { .sessionId(); ManagedAgentProperties enabled = new ManagedAgentProperties(); enabled.getHarness().setWorkspaceFilesEnabled(true); - ManagedAgentStore gated = new ManagedAgentStore(jdbc, mapper, - Clock.systemUTC(), ignored -> {}, registry, enabled); + ManagedAgentStore gated = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, + Clock.systemUTC(), ignored -> {}, registry, enabled, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); UnavailableHarnessConnector harness = new UnavailableHarnessConnector() { @Override @@ -864,7 +862,7 @@ class ManagedWorkspaceAdmissionTest { }; ManagedAgentService service = new ManagedAgentService(gated, new RequestDigests(), mock(HarnessCoordinator.class), harness, - registry); + registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); // The plain store instance is no Spring proxy, so its @Transactional // boundaries come from the template. TransactionTemplate transaction = new TransactionTemplate( @@ -917,8 +915,8 @@ class ManagedWorkspaceAdmissionTest { .sessionId(); ManagedAgentProperties enabled = new ManagedAgentProperties(); enabled.getHarness().setWorkspaceFilesEnabled(true); - ManagedAgentStore gated = new ManagedAgentStore(jdbc, mapper, - Clock.systemUTC(), ignored -> {}, registry, enabled); + ManagedAgentStore gated = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, + Clock.systemUTC(), ignored -> {}, registry, enabled, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); UnavailableHarnessConnector harness = new UnavailableHarnessConnector() { @Override @@ -949,7 +947,7 @@ class ManagedWorkspaceAdmissionTest { }; ManagedAgentService service = new ManagedAgentService(gated, new RequestDigests(), mock(HarnessCoordinator.class), harness, - registry); + registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); TransactionTemplate transaction = new TransactionTemplate( transactionManager); transaction.executeWithoutResult(status -> assertThat( @@ -1008,8 +1006,8 @@ class ManagedWorkspaceAdmissionTest { .sessionId(); ManagedAgentProperties enabled = new ManagedAgentProperties(); enabled.getHarness().setWorkspaceFilesEnabled(true); - ManagedAgentStore gated = new ManagedAgentStore(jdbc, mapper, - Clock.systemUTC(), ignored -> {}, registry, enabled); + ManagedAgentStore gated = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, + Clock.systemUTC(), ignored -> {}, registry, enabled, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); UnavailableHarnessConnector harness = new UnavailableHarnessConnector() { @Override @@ -1040,7 +1038,7 @@ class ManagedWorkspaceAdmissionTest { }; ManagedAgentService service = new ManagedAgentService(gated, new RequestDigests(), mock(HarnessCoordinator.class), harness, - registry); + registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); TransactionTemplate transaction = new TransactionTemplate( transactionManager); transaction.executeWithoutResult(status -> assertThat( @@ -1066,7 +1064,7 @@ class ManagedWorkspaceAdmissionTest { title)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.NOT_FOUND); + .isEqualTo(HttpStatus.NOT_FOUND.value()); assertThat(error.getCode()) .isEqualTo("session_not_found"); })); @@ -1115,7 +1113,7 @@ class ManagedWorkspaceAdmissionTest { assertThat(error.getCode()) .isEqualTo("idempotency_conflict"); assertThat(error.getStatus()) - .isEqualTo(HttpStatus.CONFLICT); + .isEqualTo(HttpStatus.CONFLICT.value()); })); assertThat(jdbc.queryForObject( "SELECT title FROM ma_session WHERE tenant_id = ?" @@ -1152,8 +1150,8 @@ class ManagedWorkspaceAdmissionTest { // refuses, so the row is written through an enabled one. ManagedAgentProperties enabled = new ManagedAgentProperties(); enabled.getHarness().setWorkspaceFilesEnabled(true); - ManagedAgentStore gated = new ManagedAgentStore(jdbc, mapper, - Clock.systemUTC(), ignored -> {}, registry, enabled); + ManagedAgentStore gated = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, + Clock.systemUTC(), ignored -> {}, registry, enabled, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String digest = new RequestDigests().digest(Map.of("sessionId", sessionId, "title", "new title")); transaction.executeWithoutResult(status -> gated.beginSessionRename( @@ -1167,7 +1165,7 @@ class ManagedWorkspaceAdmissionTest { "actor-b", "rename", sessionId, "new title")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.CONFLICT); + .isEqualTo(HttpStatus.CONFLICT.value()); assertThat(error.getCode()) .isEqualTo("workspace_unavailable"); })); @@ -1213,8 +1211,8 @@ class ManagedWorkspaceAdmissionTest { transactionManager); ManagedAgentProperties enabled = new ManagedAgentProperties(); enabled.getHarness().setWorkspaceFilesEnabled(true); - ManagedAgentStore gated = new ManagedAgentStore(jdbc, mapper, - Clock.systemUTC(), ignored -> {}, registry, enabled); + ManagedAgentStore gated = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, + Clock.systemUTC(), ignored -> {}, registry, enabled, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String digest = new RequestDigests().digest(Map.of("sessionId", sessionId, "title", "new title")); transaction.executeWithoutResult(status -> gated.beginSessionRename( @@ -1225,7 +1223,7 @@ class ManagedWorkspaceAdmissionTest { "actor-b", "rename", sessionId, "other title")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.CONFLICT); + .isEqualTo(HttpStatus.CONFLICT.value()); assertThat(error.getCode()) .isEqualTo("workspace_unavailable"); })); @@ -1272,8 +1270,8 @@ class ManagedWorkspaceAdmissionTest { selection); ManagedAgentProperties changed = new ManagedAgentProperties(); changed.setAgentRevision("2"); - ManagedAgentStore upgraded = new ManagedAgentStore(jdbc, mapper, - Clock.systemUTC(), ignored -> {}, registry, changed); + ManagedAgentStore upgraded = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, + Clock.systemUTC(), ignored -> {}, registry, changed, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); assertThat(upgraded.insertWorkspaceSessionCommand(tenant, "actor-a", "key", digest, "qwen-code", "1", null, List.of(), null, @@ -1423,8 +1421,8 @@ class ManagedWorkspaceAdmissionTest { ManagedAgentProperties enabled = new ManagedAgentProperties(); enabled.getHarness().setWorkspaceFilesEnabled(true); - ManagedAgentStore gated = new ManagedAgentStore(jdbc, mapper, - Clock.systemUTC(), ignored -> {}, registry, enabled); + ManagedAgentStore gated = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, + Clock.systemUTC(), ignored -> {}, registry, enabled, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); TransactionTemplate transaction = new TransactionTemplate( transactionManager); var admission = transaction.execute(status -> @@ -1506,7 +1504,7 @@ class ManagedWorkspaceAdmissionTest { } }; ManagedAgentService enabled = new ManagedAgentService(store, - new RequestDigests(), null, enabledHarness, registry); + new RequestDigests(), null, enabledHarness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); assertThat(enabled.getWebShellSession(tenant, "actor-a", pinnedId) .capabilities().workspaceTurns()).isTrue(); @@ -1523,7 +1521,7 @@ class ManagedWorkspaceAdmissionTest { assertThat(enabled.getWebShellSession(tenant, "actor-a", controlId) .capabilities().workspaceTurns()).isTrue(); ManagedAgentService disabled = new ManagedAgentService(store, - new RequestDigests(), null, new UnavailableHarnessConnector(), registry); + new RequestDigests(), null, new UnavailableHarnessConnector(), registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); assertThat(disabled.getWebShellSession(tenant, "actor-a", controlId) .capabilities().workspaceTurns()).isFalse(); } @@ -1542,8 +1540,8 @@ class ManagedWorkspaceAdmissionTest { .sessionId(); ManagedAgentProperties enabled = new ManagedAgentProperties(); enabled.getHarness().setWorkspaceFilesEnabled(true); - ManagedAgentStore gated = new ManagedAgentStore(jdbc, mapper, - Clock.systemUTC(), ignored -> {}, registry, enabled); + ManagedAgentStore gated = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, + Clock.systemUTC(), ignored -> {}, registry, enabled, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); // The Workspace authority refuses the first attach and admits the // second, so a rename after the refused one is observable. UnavailableHarnessConnector harness = @@ -1564,7 +1562,7 @@ class ManagedWorkspaceAdmissionTest { public Attachment createOrLoad(String tenantId, String sessionId, boolean loadExisting) { if (attaches++ == 0) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } return new Attachment("boot"); } @@ -1580,7 +1578,7 @@ class ManagedWorkspaceAdmissionTest { } }; ManagedAgentService service = new ManagedAgentService(gated, - new RequestDigests(), null, harness, registry); + new RequestDigests(), null, harness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); TransactionTemplate transaction = new TransactionTemplate( transactionManager); @@ -1591,7 +1589,7 @@ class ManagedWorkspaceAdmissionTest { "actor-a", "rename-1", sessionId, "first")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.CONFLICT); + .isEqualTo(HttpStatus.CONFLICT.value()); assertThat(error.getCode()) .isEqualTo("workspace_unavailable"); })); @@ -1657,12 +1655,12 @@ class ManagedWorkspaceAdmissionTest { .sessionId(); ManagedAgentProperties enabled = new ManagedAgentProperties(); enabled.getHarness().setWorkspaceFilesEnabled(true); - ManagedAgentStore gated = new ManagedAgentStore(jdbc, mapper, - Clock.systemUTC(), ignored -> {}, registry, enabled); + ManagedAgentStore gated = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, + Clock.systemUTC(), ignored -> {}, registry, enabled, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); // A Harness that lost the Session answers the rename with a 4xx, - // which the client surfaces as a DaemonHttpException — a permanent + // which the client surfaces as a HarnessHttpFailure — a permanent // failure, but not a broker refusal. - DaemonHttpException lost = mock(DaemonHttpException.class); + HarnessHttpFailure lost = mock(HarnessHttpFailure.class); UnavailableHarnessConnector harness = new UnavailableHarnessConnector() { private int renames; @@ -1697,7 +1695,7 @@ class ManagedWorkspaceAdmissionTest { } }; ManagedAgentService service = new ManagedAgentService(gated, - new RequestDigests(), null, harness, registry); + new RequestDigests(), null, harness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); TransactionTemplate transaction = new TransactionTemplate( transactionManager); @@ -1706,7 +1704,7 @@ class ManagedWorkspaceAdmissionTest { "actor-a", "rename-1", sessionId, "first")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value()); assertThat(error.getCode()) .isEqualTo("hosted_harness_unavailable"); })); @@ -1751,8 +1749,8 @@ class ManagedWorkspaceAdmissionTest { tenant, "storage-policy", "/unused/policy"), new ManagedAgentProperties.RuntimeBroker.WorkspaceMount( otherTenant, "storage-shared", "/unused/shared"))); - ManagedAgentStore gated = new ManagedAgentStore(jdbc, mapper, - Clock.systemUTC(), ignored -> {}, registry, enabled); + ManagedAgentStore gated = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, + Clock.systemUTC(), ignored -> {}, registry, enabled, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); // An unproxied store has no @Transactional; creation resolves the // Workspace only inside a transaction, as the Spring bean provides. TransactionTemplate transaction = new TransactionTemplate( @@ -1774,7 +1772,7 @@ class ManagedWorkspaceAdmissionTest { assertThat(error.getMessage()).isEqualTo( "Hosted Workspace execution is not available."); assertThat(error.getStatus()) - .isEqualTo(HttpStatus.CONFLICT); + .isEqualTo(HttpStatus.CONFLICT.value()); assertThat(error.getCode()) .isEqualTo("workspace_unavailable"); }); @@ -2039,7 +2037,7 @@ class ManagedWorkspaceAdmissionTest { when(harness.supportsContextCompression()).thenReturn(true); var enabled = boundServiceWith(harness); org.springframework.test.util.ReflectionTestUtils.setField(enabled, "runtimeWarmer", - mock(com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer.class)); + mock(com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer.class)); var summary = enabled.getWebShellSession(tenant, "operator2", session); assertThat(summary.capabilities().cwdChange()).isFalse(); assertThat(summary.capabilities().contextCompression()).isTrue(); @@ -2391,7 +2389,7 @@ class ManagedWorkspaceAdmissionTest { when(harness.supportsFencedTitles()).thenReturn(true); when(harness.supportsUserMessageQueue()).thenReturn(true); var enabled = boundServiceWith(harness); - var messages = new ManagedUserMessageStore(jdbc, mapper, new RequestDigests(), enabled); + var messages = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedUserMessageStore(jdbc, mapper, new RequestDigests(), enabled)); var transaction = new TransactionTemplate(transactionManager); var input = List.of(new InputBlock("text", "queued")); var first = transaction.execute(status -> messages.command(tenant, "actor-a", session, "seed", @@ -2419,7 +2417,7 @@ class ManagedWorkspaceAdmissionTest { org.mockito.ArgumentMatchers.anyBoolean(), org.mockito.ArgumentMatchers.anyBoolean()); assertRefused(() -> enabled.authorizeUserMessages(tenant, "reader", session), "session_operation_forbidden"); jdbc.update("UPDATE ma_workspace_access SET role = 'READER' WHERE tenant_id = ? AND workspace_id = ? AND actor_id = ?", - tenant, "ws-a", ManagedWorkspaceRegistry.actorKey(tenant, "actor-a")); + tenant, "ws-a", com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenant, "actor-a")); assertRefused(() -> enabled.authorizeUserMessages(tenant, "operator2", session), "workspace_unavailable"); } @@ -2508,7 +2506,7 @@ class ManagedWorkspaceAdmissionTest { jdbc.update("INSERT INTO ma_session_worktree (tenant_id, session_id, workspace_id, storage_id, physical_key," + " worktree_id, owner_actor_key, state, revision, plan_json, operation_id, gmt_create, gmt_modified)" + " VALUES (?, ?, 'ws-a', 'storage-a', ?, ?, ?, ?, 1, ?, ?, 1, 1)", tenant, session, - "a".repeat(64), id, ManagedWorkspaceRegistry.actorKey(tenant, "actor-a"), state, mapper.writeValueAsString(plan), id); + "a".repeat(64), id, com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenant, "actor-a"), state, mapper.writeValueAsString(plan), id); } private ManagedAgentService workspaceFilesService(String tenant, @@ -2518,15 +2516,15 @@ class ManagedWorkspaceAdmissionTest { properties.getRuntimeBroker().setWorkspaceMounts(List.of( new ManagedAgentProperties.RuntimeBroker.WorkspaceMount(tenant, "storage-a", "/workspace"))); - ManagedAgentStore gated = new ManagedAgentStore(jdbc, mapper, - Clock.systemUTC(), ignored -> {}, registry, properties); + ManagedAgentStore gated = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, + Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); return new ManagedAgentService(gated, new RequestDigests(), coordinator, new UnavailableHarnessConnector() { @Override public boolean isWorkspaceFilesAvailable() { return workspaceFiles; } - }, registry); + }, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); } private ManagedAgentService boundServiceWithWorkingHarness() { @@ -2562,8 +2560,8 @@ class ManagedWorkspaceAdmissionTest { private ManagedAgentService boundServiceWith(HarnessConnector harness) { ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - ManagedAgentStore gated = new ManagedAgentStore(jdbc, mapper, - Clock.systemUTC(), ignored -> {}, registry, properties); + ManagedAgentStore gated = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, + Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); HarnessCoordinator noopCoordinator = new HarnessCoordinator(null, null, null, null, null, Clock.systemUTC(), new ManagedAgentProperties()) { @@ -2578,7 +2576,7 @@ class ManagedWorkspaceAdmissionTest { } }; return new ManagedAgentService(gated, new RequestDigests(), - noopCoordinator, harness, registry); + noopCoordinator, harness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); } private String boundSession(String tenant) { @@ -2595,15 +2593,15 @@ class ManagedWorkspaceAdmissionTest { private ManagedAgentService boundService(boolean workspaceFiles) { ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(workspaceFiles); - ManagedAgentStore gated = new ManagedAgentStore(jdbc, mapper, - Clock.systemUTC(), ignored -> {}, registry, properties); + ManagedAgentStore gated = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, + Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); return new ManagedAgentService(gated, new RequestDigests(), null, new UnavailableHarnessConnector() { @Override public boolean isWorkspaceFilesAvailable() { return workspaceFiles; } - }, registry); + }, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); } private static void assertRefused(ThrowingCallable call, String code) { @@ -2666,9 +2664,9 @@ class ManagedWorkspaceAdmissionTest { jdbc.update("INSERT INTO ma_storage" + " (alias_key,tenant_key,storage_key,tenant_id,storage_id,fence_operation_id)" + " VALUES (?,?,?,?,?,?)", - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storageId), - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageFenceKey(tenant), - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageFenceKey(storageId), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(tenant, storageId), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageFenceKey(tenant), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageFenceKey(storageId), tenant, storageId, UUID.randomUUID().toString()); } @@ -2728,7 +2726,7 @@ class ManagedWorkspaceAdmissionTest { // then the Session row as a tombstone. private void recordDelete(String tenant, String sessionId, String statusBefore) { - String scope = ManagedSessionStore.sessionScopeKey(tenant, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId); String target = ManagedSchemaKeys.key(sessionId); jdbc.update("INSERT INTO ma_operation (operation_key, scope_key," + " session_scope_key, operation_id, operation_kind," diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceRolesMigrationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceRolesMigrationTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceRolesMigrationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceRolesMigrationTest.java index 1d4f3a9f981..f3411bc4cbe 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceRolesMigrationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ManagedWorkspaceRolesMigrationTest.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; @@ -82,9 +82,9 @@ class ManagedWorkspaceRolesMigrationTest { + " 'workspace', ?, 'OPERATOR')", CREATOR); var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - var store = new ManagedAgentStore(jdbc, new ObjectMapper(), + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var transactions = new TransactionTemplate( new DataSourceTransactionManager(source)); String sessionId = transactions.execute(ignored -> store diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/MySqlForkRoutingListener.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/MySqlForkRoutingListener.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/MySqlForkRoutingListener.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/MySqlForkRoutingListener.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/MySqlForkRoutingListenerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/MySqlForkRoutingListenerTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/MySqlForkRoutingListenerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/MySqlForkRoutingListenerTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/MySqlLockProbe.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/MySqlLockProbe.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/MySqlLockProbe.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/MySqlLockProbe.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/OpenApiContract.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/OpenApiContract.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/OpenApiContract.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/OpenApiContract.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/PlannedTaskContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/PlannedTaskContractTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/PlannedTaskContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/PlannedTaskContractTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/PublicationJournalFixture.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/PublicationJournalFixture.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/PublicationJournalFixture.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/PublicationJournalFixture.java index 943157da080..6f81e80159e 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/PublicationJournalFixture.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/PublicationJournalFixture.java @@ -1,14 +1,11 @@ package com.alibaba.qwen.code.managedagent; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; @@ -17,7 +14,10 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -76,7 +76,7 @@ public final class PublicationJournalFixture { private PublicationJournalFixture(DataSource source) { jdbc = new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(source); manager = new DataSourceTransactionManager(source); - sessions = new ManagedSessionStore(jdbc); + sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); bindings = new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("key", new byte[32]), () -> "binding-1"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerBracketKeyBindingTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerBracketKeyBindingTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerBracketKeyBindingTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerBracketKeyBindingTest.java index d2acb9f5ea8..42193521deb 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerBracketKeyBindingTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerBracketKeyBindingTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import org.junit.jupiter.api.Test; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.env.YamlPropertySourceLoader; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerConfigurationIntegrationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerConfigurationIntegrationTest.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerConfigurationIntegrationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerConfigurationIntegrationTest.java index f2f44ef3624..1f6832c74ea 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerConfigurationIntegrationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerConfigurationIntegrationTest.java @@ -1,14 +1,14 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.security.SecretProtector; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerDefaultOnTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerDefaultOnTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerDefaultOnTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerDefaultOnTest.java index 540b27c5ba5..4d9960aa06d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerDefaultOnTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerDefaultOnTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerFlywaySchemaTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerFlywaySchemaTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerFlywaySchemaTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerFlywaySchemaTest.java index 50e5f0986ab..6eda87e79d3 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerFlywaySchemaTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/RuntimeBrokerFlywaySchemaTest.java @@ -1,9 +1,9 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.runtimebroker.JdbcRepositoryContract; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.managedschema.JdbcRuntimeBrokerSchema; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRepositoryContract; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import org.assertj.core.api.SoftAssertions; import org.flywaydb.core.Flyway; import org.flywaydb.core.api.MigrationVersion; @@ -38,7 +38,7 @@ class RuntimeBrokerFlywaySchemaTest { Map actual = describe(migrate(dataSource(), MigrationVersion.LATEST)); assertThat(expected) - .hasSize(com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.TABLES.size()) + .hasSize(com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.TABLES.size()) .containsKeys( "ma_runtime_slot", "ma_runtime_binding", @@ -81,7 +81,7 @@ class RuntimeBrokerFlywaySchemaTest { JdbcRuntimeBrokerSchema.initialize(source); assertThat(executions.findByIdempotencyKey("retention-key")).usingRecursiveComparison().isEqualTo(settled); Map shape = describe(source); - assertThat(shape).hasSize(com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.TABLES.size()); + assertThat(shape).hasSize(com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.TABLES.size()); assertThat(shape.get("ma_runtime_binding").indexes()).contains( "INDEX [binding_state, last_active_at, binding_id]", "INDEX [tenant_id, binding_state]"); assertThat(shape.get("ma_storage_owner").indexes()) diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/SchedulerHold.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/SchedulerHold.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/SchedulerHold.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/SchedulerHold.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/SchedulerHoldTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/SchedulerHoldTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/SchedulerHoldTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/SchedulerHoldTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/SessionMessageRelayStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/SessionMessageRelayStoreTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/SessionMessageRelayStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/SessionMessageRelayStoreTest.java index b46f8a8a62d..4c3ec383633 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/SessionMessageRelayStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/SessionMessageRelayStoreTest.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore; -import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore.JournalTurns; import com.alibaba.qwen.code.managedagent.persistence.SessionMessageRelayStore; -import com.alibaba.qwen.code.managedagent.persistence.SessionMessageRelayStore.MessageRow; -import com.alibaba.qwen.code.managedagent.persistence.SessionMessageRelayStore.PendingMessage; +import com.alibaba.qwen.code.managedagent.repository.ChildResultRelayStore.JournalTurns; +import com.alibaba.qwen.code.managedagent.repository.SessionMessageRelayStore.MessageRow; +import com.alibaba.qwen.code.managedagent.repository.SessionMessageRelayStore.PendingMessage; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -72,8 +72,8 @@ class SessionMessageRelayStoreTest { + " first_sequence)" + " VALUES (?, ?, ?, 'workspace', ?, ?, ?," + " 'h', 1, ?, 'session', ?, ?, ?)", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey(TENANT, sessionId), - com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection.recordKey(sessionId, domain, recordId), TENANT, sessionId, domain, + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, sessionId), + com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.recordKey(sessionId, domain, recordId), TENANT, sessionId, domain, recordId, resource, state, sequence, sequence); } @@ -89,18 +89,18 @@ class SessionMessageRelayStoreTest { } private void session(String sessionId, String status) { - String scope = com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey(TENANT, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, sessionId); jdbc.update("INSERT INTO ma_session (tenant_id, session_id, tenant_key, session_key, session_scope_key, agent_id, status," + " gmt_create, gmt_modified) VALUES (?, ?, ?, ?, ?, 'qwen-code', ?, 0, 0)" + " ON DUPLICATE KEY UPDATE status = VALUES(status)", TENANT, sessionId, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(TENANT), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(sessionId), scope, status); + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(TENANT), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(sessionId), scope, status); } private void resource(String sessionId, String resourceId, byte[] bytes) { - String scope = com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey(TENANT, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, sessionId); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create) VALUES (?, ?, ?, 'workspace', ?, ?, 'managed-message', 1, ?, ?, 'MYSQL_INLINE', ?, 'command', 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ")", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(scope, resourceId), scope, + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scope, resourceId), scope, TENANT, sessionId, resourceId, bytes.length, "a".repeat(64), bytes); } @@ -333,8 +333,7 @@ class SessionMessageRelayStoreTest { message(sender, "msg_a_second", "planned", outbound("to_child", "r")); jdbc.update("UPDATE ma_session_record SET gmt_create = 5" + " WHERE session_scope_key = ?", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore - .sessionScopeKey(TENANT, sender)); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, sender)); assertThat(store.findPendingMessages("owner-a", 1_000, 50).stream() .filter(entry -> entry.senderSessionId().equals(sender)) .map(PendingMessage::messageId)) @@ -477,8 +476,8 @@ class SessionMessageRelayStoreTest { + " VALUES (?, ?, ?, 'workspace', ?," + " 'session_message', 'msg_x', 'h', 1, 'missing'," + " 'session', 'planned', 1)", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey(TENANT, parent), - com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection.recordKey(parent, "session_message", "msg_x"), TENANT, parent); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, parent), + com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.recordKey(parent, "session_message", "msg_x"), TENANT, parent); assertThatThrownBy(() -> records.edgeMessages(TENANT, parent, "run-1", UUID.randomUUID().toString())) .isInstanceOf(IllegalStateException.class); @@ -584,8 +583,8 @@ class SessionMessageRelayStoreTest { String child = UUID.randomUUID().toString(); journal(child, 1, input("turn-api", "hosted-harness")); jdbc.update("INSERT INTO ma_session_head (head_kind, tenant_key, harness_key, harness_session_id, tenant_id, workspace_id, session_id, storage_version, state, writer_generation, journal_revision, committed_sequence, activation_epoch, compacted_through_revision, recovery_status, gmt_create, gmt_modified) VALUES ('MANAGED_SESSION', ?, ?, ?, ?, 'workspace', ?, 1, 'ACTIVE', 1, 1, 1, 1, 1, 'NONE', " + ManagedSchemaTime.SQL_NOW + ", " + ManagedSchemaTime.SQL_NOW + ")", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(TENANT), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(child), child, TENANT, child); + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(TENANT), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(child), child, TENANT, child); assertThatThrownBy(() -> records.journalTurns(TENANT, child)) .isInstanceOf(IllegalStateException.class) .hasMessageContaining("compacted"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/StandaloneDataSourceConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/StandaloneDataSourceConfigurationTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/StandaloneDataSourceConfigurationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/StandaloneDataSourceConfigurationTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/SurfaceAdmissionAcceptanceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/SurfaceAdmissionAcceptanceTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/SurfaceAdmissionAcceptanceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/SurfaceAdmissionAcceptanceTest.java index 4eaf763f12e..b9952679ade 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/SurfaceAdmissionAcceptanceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/SurfaceAdmissionAcceptanceTest.java @@ -7,17 +7,17 @@ import com.alibaba.qwen.code.managedagent.api.SurfaceRegistry.RuleClass; import com.alibaba.qwen.code.managedagent.api.SurfaceRegistry.Surface; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; import com.alibaba.qwen.code.managedagent.api.ToolPublicationController; -import com.alibaba.qwen.code.managedagent.artifact.ManagedArtifactPolicy; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ManagedArtifactPolicy; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedToolResultStore; import com.alibaba.qwen.code.managedagent.persistence.SshWorkspaceStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationAdmissionStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; @@ -67,58 +67,54 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** - * The D6 acceptance probes: the test walks {@link SurfaceRegistry} and fires - * the standard refusal probes per rule class on every route — wrong - * tenant (403 {@code actor_scope_mismatch}), missing actor, below-read (404 - * without a Workspace grant), read-below-OPERATE (403 - * {@code session_operation_forbidden} on the Session mutation families, 403 - * {@code action_forbidden} on Action respond, 403 - * {@code artifact_content_forbidden} on artifact content with the policy - * off) — plus one admitted-shape probe per rule class proving the request - * reached business logic, fired by a non-owner OPERATOR on the OPERATOR - * families and by the recorded owner on lifecycle. Twin public/WebShell - * routes of one capability sit in the same rule class, so the shared - * expectation table makes twin drift fail. The refusal expectations pinned - * here are the post-v1.37 matrix. + * The D6 acceptance probes: the test walks {@link SurfaceRegistry} and fires the standard refusal + * probes per rule class on every route — wrong tenant (403 {@code actor_scope_mismatch}), missing + * actor, below-read (404 without a Workspace grant), read-below-OPERATE (403 {@code + * session_operation_forbidden} on the Session mutation families, 403 {@code action_forbidden} on + * Action respond, 403 {@code artifact_content_forbidden} on artifact content with the policy off) — + * plus one admitted-shape probe per rule class proving the request reached business logic, fired by + * a non-owner OPERATOR on the OPERATOR families and by the recorded owner on lifecycle. Twin + * public/WebShell routes of one capability sit in the same rule class, so the shared expectation + * table makes twin drift fail. The refusal expectations pinned here are the post-v1.37 matrix. * - *

The internal probes run two different credentials: the Session-store - * routes carry the broker-issued writer HMAC (a wrong token is refused at - * the credential check with 403 {@code writer_credential_invalid}), - * the tool-publication routes pass through either that same check or the - * publication-grant redaction, so their exact wrong-token shapes are pinned - * per route where the code falls. + *

The internal probes run two different credentials: the Session-store routes carry the + * broker-issued writer HMAC (a wrong token is refused at the credential check with 403 {@code + * writer_credential_invalid}), the tool-publication routes pass through either that same check or + * the publication-grant redaction, so their exact wrong-token shapes are pinned per route where the + * code falls. * - *

SSE arms ({@code GET …/events?stream=true} and {@code - * events/stream}) are probed below the read grant, where the refusal is - * synchronous before the stream opens; the admitted stream shape is pinned - * by the pre-existing stream suites. + *

SSE arms ({@code GET …/events?stream=true} and {@code events/stream}) are probed below the + * read grant, where the refusal is synchronous before the stream opens; the admitted stream shape + * is pinned by the pre-existing stream suites. */ -@SpringBootTest(properties = { - "spring.datasource.url=jdbc:h2:mem:surface-acceptance;MODE=MySQL;" - + "DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE", - "spring.datasource.driver-class-name=org.h2.Driver", - "spring.datasource.username=sa", - "spring.datasource.password=", - "qwen.managed-agent.harness.enabled=false", - "qwen.managed-agent.dispatch.scan-delay=50ms", - "qwen.managed-agent.events.poll-interval=10ms", - "qwen.managed-agent.events.materialize-interval=10ms", - "qwen.managed-agent.session-store.enabled=true", - "qwen.managed-agent.session-store.binding-key=" - + "0123456789abcdef0123456789abcdef", - "qwen.managed-agent.runtime-broker.ssh.enabled=false", - "qwen.managed-agent.tool-publication.entry-concurrency=4", - "qwen.managed-agent.artifacts.enabled=true", - // The scheduled projector would race the publication-chain fixture - // as it installs the artifact rows; the routes this test drives - // only read the projected tables. - "qwen.managed-agent.artifacts.projection-interval=86400000" -}) +@SpringBootTest( + properties = { + "spring.datasource.url=jdbc:h2:mem:surface-acceptance;MODE=MySQL;" + + "DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE", + "spring.datasource.driver-class-name=org.h2.Driver", + "spring.datasource.username=sa", + "spring.datasource.password=", + "qwen.managed-agent.harness.enabled=false", + "qwen.managed-agent.dispatch.scan-delay=50ms", + "qwen.managed-agent.events.poll-interval=10ms", + "qwen.managed-agent.events.materialize-interval=10ms", + "qwen.managed-agent.session-store.enabled=true", + "qwen.managed-agent.session-store.binding-key=" + "0123456789abcdef0123456789abcdef", + "qwen.managed-agent.runtime-broker.ssh.enabled=false", + "qwen.managed-agent.tool-publication.entry-concurrency=4", + "qwen.managed-agent.artifacts.enabled=true", + // The scheduled projector would race the publication-chain fixture + // as it installs the artifact rows; the routes this test drives + // only read the projected tables. + "qwen.managed-agent.artifacts.projection-interval=86400000" + }) @AutoConfigureMockMvc -@Import({ManagedAgentServerIntegrationTest.FixtureConfiguration.class, - SurfaceAdmissionAcceptanceTest.TestBeans.class}) +@Import({ + ManagedAgentServerIntegrationTest.FixtureConfiguration.class, + SurfaceAdmissionAcceptanceTest.TestBeans.class +}) @TestInstance(TestInstance.Lifecycle.PER_CLASS) -class SurfaceAdmissionAcceptanceTest { +public class SurfaceAdmissionAcceptanceTest { private static final ObjectMapper JSON = new ObjectMapper(); private static final String OWNER = "owner"; private static final String READER = "reader"; @@ -128,8 +124,7 @@ class SurfaceAdmissionAcceptanceTest { private static final String OWNER_RANK = "owner-rank"; private static final String STRANGER = "stranger"; private static final String FOREIGN = "foreign-tenant"; - private static final String WRONG_TOKEN = - "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"; + private static final String WRONG_TOKEN = "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"; private static final String WRITER_TOKEN = "X-Qwen-Managed-Writer-Token"; private static final String ARTIFACT_TENANT = "tenant-1"; private static final String ARTIFACT_WORKSPACE = "workspace-1"; @@ -181,7 +176,7 @@ class SurfaceAdmissionAcceptanceTest { private String registrationOperation; @BeforeAll - void graph() throws Exception { + public void graph() throws Exception { // The artifact family reuses the publication-chain fixture. It // projects one tool result with artifacts onto the shared schema // and asserts while doing so, so it builds before this graph's own @@ -257,7 +252,7 @@ class SurfaceAdmissionAcceptanceTest { // caller who lost the role or the Session gets 403 or 404, never the // replay of their own admitted cancel. @Test - void aRetainedCancelKeyNeverBypassesCurrentAccess() throws Exception { + public void aRetainedCancelKeyNeverBypassesCurrentAccess() throws Exception { String session = createSession(tenant, OWNER, true); String recordKey = "e".repeat(64); jdbc.update("INSERT INTO ma_session_record" @@ -269,8 +264,7 @@ class SurfaceAdmissionAcceptanceTest { + " VALUES (?, ?, ?, 'ws', ?, 'child_run'," + " 'run-retained', ?, 1, 'resource-retained'," + " 'child_agent', 'running', 'session', 'planned', 1)", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore - .sessionScopeKey(tenant, session), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), recordKey, tenant, session, "f".repeat(64)); String key = nextKey(); java.util.function.Supplier cancel = @@ -321,12 +315,11 @@ class SurfaceAdmissionAcceptanceTest { + " 'run-settled', ?, 1, 'resource-settled'," + " 'child_agent', 'completed', 'session'," + " 'consumed', 1, 2)", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore - .sessionScopeKey(tenant, session), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), recordKey, tenant, session, "b".repeat(64)); // The task view reads a child task's committed body for its child // Session link (#13746 F2), so the list probes need one. - String scope = com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); byte[] body = "{\"childSessionId\":null}".getBytes( StandardCharsets.UTF_8); jdbc.update("INSERT INTO ma_resource (resource_key," @@ -337,27 +330,26 @@ class SurfaceAdmissionAcceptanceTest { + " ?, ?, 'ws', ?, 'resource-settled'," + " 'managed-child_run', 1, ?, ?, 'MYSQL_INLINE', ?," + " 'command', 'REFERENCED', 1)", - com.alibaba.qwen.code.runtimebroker.managedschema - .ManagedSchemaKeys.key(scope, "resource-settled"), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scope, "resource-settled"), scope, tenant, session, body.length, org.apache.commons.codec.digest.DigestUtils.sha256Hex(body), body); return "task_" + recordKey; } - static Stream everyRoute() { + public static Stream everyRoute() { return Stream.of(SurfaceRegistry.values()); } - static Stream refusalProbedRoutes() { + public static Stream refusalProbedRoutes() { return Stream.of(SurfaceRegistry.values()).filter(entry -> entry.ruleClass() != RuleClass.TENANT_SCOPED); } @ParameterizedTest @MethodSource("everyRoute") - void wrongTenantIsRefusedOnEveryRoute(SurfaceRegistry entry) - throws Exception { + public void wrongTenantIsRefusedOnEveryRoute( + SurfaceRegistry entry) throws Exception { MvcResult result = mvc.perform(requestFor(entry, FOREIGN, tenant)) .andReturn(); assertThat(result.getResponse().getStatus()) @@ -370,8 +362,9 @@ class SurfaceAdmissionAcceptanceTest { @ParameterizedTest @MethodSource("refusalProbedRoutes") - void belowReadAndBelowFamilyProbesFollowTheRuleClass(SurfaceRegistry entry) - throws Exception { + public void + belowReadAndBelowFamilyProbesFollowTheRuleClass(SurfaceRegistry entry) + throws Exception { switch (entry.ruleClass()) { case SESSION_WORKTREE -> { expect(entry, null, 401, "actor_required"); @@ -585,8 +578,7 @@ class SurfaceAdmissionAcceptanceTest { } @Test - void workspaceCreateAdmitsItsGrantHolderAcrossBothSurfaces() - throws Exception { + public void workspaceCreateAdmitsItsGrantHolderAcrossBothSurfaces() throws Exception { String createBody = "{\"agent_id\":\"qwen-code\",\"input\":[]," + "\"workspace\":{\"workspace_id\":\"ws\"}}"; MvcResult created = mvc.perform(post("/v1/agents/sessions") @@ -638,7 +630,7 @@ class SurfaceAdmissionAcceptanceTest { // reader's task list really serves the child task whose committed body // the view reads for its child Session link (#13746 F2). @Test - void aReadersTaskListServesTheChildTaskThroughItsBody() throws Exception { + public void aReadersTaskListServesTheChildTaskThroughItsBody() throws Exception { mvc.perform(get("/v1/agents/sessions/" + bound + "/tasks") .header(TenantContextFilter.HEADER, tenant) .principal(actor(tenant, READER))) @@ -650,8 +642,7 @@ class SurfaceAdmissionAcceptanceTest { } @Test - void readerRoutesAnswerTheBoundGrantHolderAndTheLegacyArm() - throws Exception { + public void readerRoutesAnswerTheBoundGrantHolderAndTheLegacyArm() throws Exception { mvc.perform(get("/v1/agents/sessions/" + bound) .header(TenantContextFilter.HEADER, tenant) .principal(actor(tenant, READER))) @@ -682,7 +673,7 @@ class SurfaceAdmissionAcceptanceTest { } @Test - void submitterRoutesAdmitTheLegacyArmAndAnOperatorHitsDomainRefusal() + public void submitterRoutesAdmitTheLegacyArmAndAnOperatorHitsDomainRefusal() throws Exception { // Every admitted mutation gets its own legacy Session, so no // in-progress operation or Turn collides with the next probe. @@ -746,7 +737,7 @@ class SurfaceAdmissionAcceptanceTest { } @Test - void ownerRoutesAdmitTheRecordedOwnerAndTheLegacyArmWithDomainProofs() + public void ownerRoutesAdmitTheRecordedOwnerAndTheLegacyArmWithDomainProofs() throws Exception { // Lifecycle admission keys on the owner record, not the Workspace // role: an owner holding only READER on the Workspace still @@ -812,8 +803,7 @@ class SurfaceAdmissionAcceptanceTest { // Session's owner answers a pending approval on both surfaces, and the // READER below keeps the refusal. @Test - void secondOperatorAnswersPendingApprovalsOnBothSurfaces() - throws Exception { + public void secondOperatorAnswersPendingApprovalsOnBothSurfaces() throws Exception { String publicAction = insertAction(tenant, bound); String webAction = insertAction(tenant, bound); mvc.perform(post("/v1/agents/sessions/" + bound + "/actions/" @@ -860,7 +850,7 @@ class SurfaceAdmissionAcceptanceTest { // caller and the recorded create-command actor alike — and the // restored creator unblocks a fresh pending Action for 202. @Test - void respondCertifiesTheCreatorsExecutionFacts() throws Exception { + public void respondCertifiesTheCreatorsExecutionFacts() throws Exception { String body = "{\"kind\":\"permission\",\"input_revision\":1," + "\"policy_revision\":\"policy\",\"option_id\":\"allow\"}"; for (String caller : List.of(OPERATOR, OWNER)) { @@ -900,7 +890,7 @@ class SurfaceAdmissionAcceptanceTest { // drops between admission and a retry still resolves the recorded // answer, while a fresh key meets the ordinary 403. @Test - void aDemotedResponderStillReplaysTheirAdmittedAnswer() throws Exception { + public void aDemotedResponderStillReplaysTheirAdmittedAnswer() throws Exception { String action = insertAction(tenant, bound); String key = "respond-replay-1"; String body = "{\"kind\":\"permission\",\"input_revision\":1," @@ -945,8 +935,7 @@ class SurfaceAdmissionAcceptanceTest { } @Test - void actionRespondOnLegacyRefusesTheNonOwnerWithTheFamilyCode() - throws Exception { + public void actionRespondOnLegacyRefusesTheNonOwnerWithTheFamilyCode() throws Exception { mvc.perform(post("/api/agent/web-shell/v1/actions/respond") .header(TenantContextFilter.HEADER, tenant) .principal(actor(tenant, STRANGER)) @@ -963,7 +952,7 @@ class SurfaceAdmissionAcceptanceTest { } @Test - void discoveryRequiresAnActorAndFiltersToTheCallersReadableRows() + public void discoveryRequiresAnActorAndFiltersToTheCallersReadableRows() throws Exception { mvc.perform(get("/v1/agents/workspaces") .header(TenantContextFilter.HEADER, tenant) @@ -1002,7 +991,7 @@ class SurfaceAdmissionAcceptanceTest { } @Test - void definitionsStayTenantWideAcrossTheArmSplit() throws Exception { + public void definitionsStayTenantWideAcrossTheArmSplit() throws Exception { String body = "{\"model\":{\"id\":\"qwen3-coder-plus\"}," + "\"instructions\":\"Review code.\",\"tools\":[]," + "\"permission_policy\":{\"mode\":\"default\"}}"; @@ -1034,8 +1023,7 @@ class SurfaceAdmissionAcceptanceTest { } @Test - void artifactContentIsPolicyGatedBeyondTheWorkspaceReadGrant() - throws Exception { + public void artifactContentIsPolicyGatedBeyondTheWorkspaceReadGrant() throws Exception { // Metadata routes admit the readable actor, including the access // flag the deployment policy computes. mvc.perform(get("/v1/agents/sessions/" + ARTIFACT_SESSION @@ -1092,7 +1080,7 @@ class SurfaceAdmissionAcceptanceTest { // deliberately different for once, lifecycle and respond both follow // owner_actor_key, and the original creator is refused. @Test - void theOwnerColumnTakesPrecedenceOverTheCreator() throws Exception { + public void theOwnerColumnTakesPrecedenceOverTheCreator() throws Exception { String scratch = createSession(tenant, OWNER, true); jdbc.update("UPDATE ma_session SET owner_actor_key = ?" + " WHERE tenant_id = ? AND session_id = ?", @@ -1123,7 +1111,7 @@ class SurfaceAdmissionAcceptanceTest { // retains that authority; a missing creator is a corrupt binding and // historical request receipts cannot replace its execution identity. @Test - void aBoundSessionKeepsItsCreatorAuthorityAndRefusesAMissingCreator() + public void aBoundSessionKeepsItsCreatorAuthorityAndRefusesAMissingCreator() throws Exception { String withCommand = createSession(tenant, OWNER, true); jdbc.update("UPDATE ma_session SET owner_actor_key =" @@ -1192,7 +1180,7 @@ class SurfaceAdmissionAcceptanceTest { // The walk's HTTP cells cannot reach the facts (no files opt-in), so // the store is the witness. @Test - void anOwnerRankCreatorSatisfiesTheExecutionFacts() throws Exception { + public void anOwnerRankCreatorSatisfiesTheExecutionFacts() throws Exception { String boundRank = createSession(tenant, OWNER_RANK, true); assertThat(store.sessionsWithExecutionRegistryFacts(tenant, List.of(boundRank))) @@ -1200,7 +1188,7 @@ class SurfaceAdmissionAcceptanceTest { } @Test - void internalWriterRoutesEnforceTheSessionCredential() throws Exception { + public void internalWriterRoutesEnforceTheSessionCredential() throws Exception { String session = createSession(tenant, OWNER, true); String token = credentials.issue(tenant, "ws", session); String base = "/internal/managed-session-store/v1/sessions/" @@ -1295,8 +1283,7 @@ class SurfaceAdmissionAcceptanceTest { // token is what decides. A malformed one ("Invalid publication token") // or a removed check (a later domain error) both fail this pin. @Test - void publicationTokenDecidesThroughTheStoredGrantComparison() - throws Exception { + public void publicationTokenDecidesThroughTheStoredGrantComparison() throws Exception { String real = PublicationJournalFixture.PUBLICATION_TOKEN; // "BBBB" because the fixture token is forty-three "A"s — a probe // must actually differ. @@ -1845,8 +1832,8 @@ class SurfaceAdmissionAcceptanceTest { jdbc.update("INSERT INTO ma_session_record (record_kind, session_scope_key, record_key, domain, record_id, revision, tenant_id," + " session_id, action_id, action_version, state, options_json, gmt_create)" + " VALUES ('ACTION', ?, ?, 'action', ?, 1, ?, ?, ?, 1, 'requested', ?, 0)", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey(tenant, session), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(tenant, session, "action", action), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(tenant, session, "action", action), action, tenant, session, action, JSON.writeValueAsString(Map.of("expiresAt", System.currentTimeMillis() + 3_600_000L, "inputRevision", 1, "policyRevision", "policy", @@ -1881,20 +1868,20 @@ class SurfaceAdmissionAcceptanceTest { } @TestConfiguration - static class TestBeans { + public static class TestBeans { /** - * The artifact reader's backing store: publication object bytes - * never leave the fixture database in this boot. + * The artifact reader's backing store: publication object bytes never leave the fixture + * database in this boot. */ @Bean - ToolPublicationObjectStore acceptanceObjects() { + public ToolPublicationObjectStore acceptanceObjects() { return mock(ToolPublicationObjectStore.class); } /** The content policy admits "owner" only, so the gate is visible. */ @Bean @Primary - ManagedArtifactPolicy acceptancePolicy() { + public ManagedArtifactPolicy acceptancePolicy() { return new ManagedArtifactPolicy() { @Override public String version() { @@ -1922,18 +1909,19 @@ class SurfaceAdmissionAcceptanceTest { } /** - * The scheduled projector with its tick removed: it would race the - * publication-chain fixture as it installs the artifact rows, and - * the routes this test drives only read the projected tables. + * The scheduled projector with its tick removed: it would race the publication-chain + * fixture as it installs the artifact rows, and the routes this test drives only read the + * projected tables. */ @Bean @Primary - com.alibaba.qwen.code.managedagent.persistence.ManagedToolResultProjector + public com.alibaba.qwen.code.managedagent.persistence + .ManagedToolResultProjector acceptanceProjector( ManagedToolResultStore store, @Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, - org.springframework.beans.factory.ObjectProvider< - ToolPublicationDataStore> publications, + org.springframework.beans.factory.ObjectProvider + publications, com.alibaba.qwen.code.managedagent.persistence.ManagedArtifactReader reader, ManagedArtifactPolicy acceptancePolicy, ManagedAgentProperties properties) { @@ -1946,11 +1934,11 @@ class SurfaceAdmissionAcceptanceTest { } /** - * The publication grant store, real over the fixture H2, so the - * writer credential flows through the same code the service mounts. + * The publication grant store, real over the fixture H2, so the writer credential flows + * through the same code the service mounts. */ @Bean - ToolPublicationStore acceptancePublicationGrants( + public ToolPublicationStore acceptancePublicationGrants( @Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, @Qualifier("managedAgentTransactionManager") PlatformTransactionManager manager, ManagedSessionStore sessions) { @@ -1962,12 +1950,11 @@ class SurfaceAdmissionAcceptanceTest { } /** - * The publication data store as a bean: it is also what makes the - * artifact reader's {@code supported()} — and with it the whole - * artifact route family — live in this boot. + * The publication data store as a bean: it is also what makes the artifact reader's {@code + * supported()} — and with it the whole artifact route family — live in this boot. */ @Bean - ToolPublicationDataStore acceptancePublicationData( + public ToolPublicationDataStore acceptancePublicationData( @Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, @Qualifier("managedAgentTransactionManager") PlatformTransactionManager manager, ToolPublicationStore acceptancePublicationGrants, @@ -1981,13 +1968,12 @@ class SurfaceAdmissionAcceptanceTest { } /** - * The internal publication controller, registered directly for the - * reason the gate names: the {@code tool-publication.enabled} - * chain requires a provisioned OSS client and Runtime Broker store - * beans. + * The internal publication controller, registered directly for the reason the gate names: + * the {@code tool-publication.enabled} chain requires a provisioned OSS client and Runtime + * Broker store beans. */ @Bean - ToolPublicationController acceptanceToolPublication( + public ToolPublicationController acceptanceToolPublication( @Qualifier("managedJdbcTemplate") JdbcTemplate jdbc, @Qualifier("managedAgentTransactionManager") PlatformTransactionManager manager, ToolPublicationStore acceptancePublicationGrants, @@ -2002,8 +1988,8 @@ class SurfaceAdmissionAcceptanceTest { } } - static java.util.Map reservedRegistration( - com.alibaba.qwen.code.managedagent.persistence.SshWorkspaceStore.Intent intent, String tenant) { + public static java.util.Map reservedRegistration( + com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.Intent intent, String tenant) { java.util.Map identity = java.util.Map.of("root", "/work/project", "hostId", "host-" + tenant, "device", "1", "inode", "2", "birthTime", "3"); var marker = new java.util.LinkedHashMap(identity); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationConfigurationTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationConfigurationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationConfigurationTest.java index 0c857a7ca69..02b8d3d069b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationConfigurationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationConfigurationTest.java @@ -1,9 +1,8 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.config.AliyunToolPublicationStorageAutoConfiguration; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ToolPublicationConfiguration; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; @@ -76,7 +75,7 @@ class ToolPublicationConfigurationTest { settings.setServiceBaseUrl("https://test.invalid"); var credentials = new com.aliyun.oss.common.auth.DefaultCredentialProvider( new com.aliyun.oss.common.auth.DefaultCredentials("test", "test")); - var client = (com.aliyun.oss.OSSClient) AliyunToolPublicationStorageAutoConfiguration.buildOss(properties, credentials); + var client = (com.aliyun.oss.OSSClient) com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient.buildOss(properties, credentials); try { assertThat(client.getCredentialsProvider()).isSameAs(credentials); assertThat(client.getClientConfiguration().getSignatureVersion()) @@ -93,7 +92,7 @@ class ToolPublicationConfigurationTest { client.shutdown(); } settings.setOssEndpoint("http://oss-cn-hangzhou.aliyuncs.com"); - assertThatThrownBy(() -> AliyunToolPublicationStorageAutoConfiguration.buildOss(properties, credentials)) + assertThatThrownBy(() -> com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient.buildOss(properties, credentials)) .isInstanceOf(IllegalStateException.class).hasMessageContaining("regional HTTPS"); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationContractTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationContractTest.java index ebaf174aee3..ce72e638694 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationContractTest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLifecycleMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLifecycleMySqlIT.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLifecycleMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLifecycleMySqlIT.java index ffd5f74fdac..457f2f7fe0b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLifecycleMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLifecycleMySqlIT.java @@ -1,16 +1,16 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationAdmissionStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.ObjectMapper; @@ -120,8 +120,8 @@ class ToolPublicationLifecycleMySqlIT { var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - var agents = new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + var agents = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var beans = new DefaultListableBeanFactory(); beans.registerSingleton("bindings", journal.bindings); var lifecycle = new WorkspaceLifecycleStore(jdbc, JSON, beans.getBeanProvider(RuntimeBindingRepository.class)); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLockOrderMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLockOrderMySqlIT.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLockOrderMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLockOrderMySqlIT.java index 9ac6a662885..23d654b4bab 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLockOrderMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationLockOrderMySqlIT.java @@ -1,29 +1,29 @@ package com.alibaba.qwen.code.managedagent; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; +import com.alibaba.qwen.code.managedagent.maintenance.WorkspaceCsiSessionMain; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.SealWriterRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationAdmissionStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationRetentionStore; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiSessionMain; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.flywaydb.core.Flyway; @@ -267,7 +267,7 @@ class ToolPublicationLockOrderMySqlIT { jdbc.update( "INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", - ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); var properties = new ManagedAgentProperties(); properties.setAgentRevision("reviewed-agent/1"); // A full pinned original: the seal below walks the whole CSI @@ -276,8 +276,8 @@ class ToolPublicationLockOrderMySqlIT { String sessionId = createCsiOriginal(jdbc, manager, JSON, properties, registration); var bindings = new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("test", new byte[32])); - var managed = new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + var managed = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var transaction = new TransactionTemplate(manager); var request = transaction.execute(status -> requireCsiRequest(managed, registration, sessionId)); @@ -293,7 +293,7 @@ class ToolPublicationLockOrderMySqlIT { new RuntimeResourceHandle("kubernetes-workspace", 3, Map.of("podUid", "fixture-original")), Instant.now(), Instant.now())); - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); transaction.executeWithoutResult(status -> sessions.acquireWriter("tenant", sessionId, WRITER_TOKEN, new AcquireWriterRequest("workspace", "writer", 60_000L))); // InnoDB rolls back the transaction with the smaller undo log; @@ -326,7 +326,7 @@ class ToolPublicationLockOrderMySqlIT { .as("The retention owner is the deadlock victim at its root wait") .isInstanceOf(ExecutionException.class) .cause().isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus().value()).isEqualTo(503); + assertThat(error.getStatus()).isEqualTo(503); assertThat(error.getCode()).isEqualTo("tenant_guard_contended"); }); assertThatThrownBy(() -> subject.get(30, TimeUnit.SECONDS)) @@ -423,10 +423,10 @@ class ToolPublicationLockOrderMySqlIT { try { var create = WorkspaceCsiSessionMain.class.getDeclaredMethod("create", JdbcTemplate.class, PlatformTransactionManager.class, ObjectMapper.class, - ManagedAgentProperties.class, WorkspaceCsiSessionMain.Request.class); + ManagedAgentProperties.class, com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request.class); create.setAccessible(true); var created = create.invoke(null, jdbc, manager, json, properties, - new WorkspaceCsiSessionMain.Request(registration, "actor", "create", null, null, + new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request(registration, "actor", "create", null, null, new WorkspaceSelection("workspace", "."))); var sessionId = created.getClass().getDeclaredMethod("sessionId"); sessionId.setAccessible(true); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationRecoveryMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationRecoveryMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationRecoveryMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationRecoveryMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationStoreTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationStoreTest.java index 85c0a1f9e8e..7795233081f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/ToolPublicationStoreTest.java @@ -4,30 +4,30 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; -import com.alibaba.qwen.code.managedagent.artifact.ManagedArtifactPolicy; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ManagedArtifactPolicy; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedArtifactReader; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.persistence.ManagedToolResultProjector; import com.alibaba.qwen.code.managedagent.persistence.ManagedToolResultStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationAdmissionStore; -import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -40,7 +40,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DelegatingDataSource; @@ -137,8 +136,8 @@ class ToolPublicationStoreTest { projectionProperties = new ManagedAgentProperties(); projectionProperties.getArtifacts().setEnabled(true); publicWorkspaces = org.mockito.Mockito.mock(ManagedWorkspaceRegistry.class); - publicSessions = new ManagedAgentStore(jdbc, JSON, java.time.Clock.systemUTC(), events -> {}, - publicWorkspaces, projectionProperties); + publicSessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, JSON, java.time.Clock.systemUTC(), events -> {}, + publicWorkspaces, projectionProperties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); publicResults = new ManagedToolResultStore(jdbc, manager, publicSessions); sessions.setToolResults(publicResults); } @@ -1992,7 +1991,7 @@ class ToolPublicationStoreTest { .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getCode()) .isEqualTo("managed_session_journal_corrupt"); - assertThat(error.getStatus().is5xxServerError()).isTrue(); + assertThat(error.getStatus() >= 500 && error.getStatus() < 600).isTrue(); }); } @@ -2014,7 +2013,7 @@ class ToolPublicationStoreTest { .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getCode()) .isEqualTo("managed_session_journal_corrupt"); - assertThat(error.getStatus().is5xxServerError()).isTrue(); + assertThat(error.getStatus() >= 500 && error.getStatus() < 600).isTrue(); }); } @@ -2031,7 +2030,7 @@ class ToolPublicationStoreTest { .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getCode()) .isEqualTo("managed_session_journal_corrupt"); - assertThat(error.getStatus().is5xxServerError()).isTrue(); + assertThat(error.getStatus() >= 500 && error.getStatus() < 600).isTrue(); }); } @@ -2415,7 +2414,7 @@ class ToolPublicationStoreTest { // A refusal that used bytes alone cause is a 409; any other is a 429. private static void assertRefusal(ManagedAgentException error, boolean retained, String limit) { - assertThat(error.getStatus()).isEqualTo(retained ? HttpStatus.CONFLICT : HttpStatus.TOO_MANY_REQUESTS); + assertThat(error.getStatus()).isEqualTo(retained ? 409 : 429); assertThat(error.getCode()).isEqualTo(retained ? "managed_tool_publication_retention_exhausted" : "managed_tool_publication_capacity_exhausted"); assertThat(error.getMessage()).isEqualTo("Tool publication reservation refused at the " + limit + " limit"); @@ -3633,7 +3632,7 @@ class ToolPublicationStoreTest { jdbc.update( "DELETE FROM ma_resource_ref WHERE session_scope_key = ?" + " AND owner_kind = 'ARTIFACT' AND owner_key = ?", - row[0], com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(row[0], row[1])); + row[0], com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(row[0], row[1])); jdbc.update( "DELETE FROM ma_artifact WHERE scope_key = ? AND" + " artifact_id = ?", row[0], row[1]); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/TurnEventLines.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/TurnEventLines.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/TurnEventLines.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/TurnEventLines.java diff --git a/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/VerificationFixtures.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/VerificationFixtures.java new file mode 100644 index 00000000000..3f16518f5d8 --- /dev/null +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/VerificationFixtures.java @@ -0,0 +1,69 @@ +package com.alibaba.qwen.code.managedagent; + +import com.alibaba.qwen.code.managedagent.persistence.*; + +import java.nio.file.Path; + +public final class VerificationFixtures { + private VerificationFixtures() { } + + public static Path moduleRoot() { + return Path.of(System.getProperty("managed.module.root", "..")).toAbsolutePath().normalize(); + } + + public static Path repositoryRoot() { + return moduleRoot().resolve("../..").normalize(); + } + + public static ManagedAgentStore configure(ManagedAgentStore store) { + store.configureWorktreeFiles( + new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles()); + return store; + } + + public static ManagedSessionStore configure(ManagedSessionStore store) { + store.configureHarnessRules( + new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); + store.configureReaderVersion( + new com.alibaba.qwen.code.managedagent.config.ManagedSessionReaderVersion()); + return store; + } + + public static SessionWorktreeStore configure(SessionWorktreeStore store) { + store.configureWorktreeFiles( + new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles()); + return store; + } + + public static WorkspaceFileStore configure(WorkspaceFileStore store) { + store.configureWorktreeFiles( + new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles()); + store.configureLocalFiles( + new com.alibaba.qwen.code.runtimebroker.provisioning.WorkspaceFileProcess()); + return store; + } + + public static WorkspaceFileUploadStore configure(WorkspaceFileUploadStore store) { + store.configureLocalFiles( + new com.alibaba.qwen.code.runtimebroker.provisioning.WorkspaceFileProcess()); + return store; + } + + public static ManagedUserMessageStore configure(ManagedUserMessageStore store) { + store.configureWorktreeFiles( + new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles()); + return store; + } + + public static WorkspaceExecutionStore configure(WorkspaceExecutionStore store) { + store.configureWorktreeFiles( + new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles()); + return store; + } + + public static com.alibaba.qwen.code.managedagent.workspace.SessionWorktreeService configure(com.alibaba.qwen.code.managedagent.workspace.SessionWorktreeService service) { + service.configureWorktreeFiles(new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles(), + (git, timeout) -> new com.alibaba.qwen.code.managedagent.adapters.workspace.SessionWorktreeGit(new com.alibaba.qwen.code.managedagent.adapters.workspace.ChildWorktreeGit(git, timeout))); + return service; + } +} diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceCreationProcessFixtureMain.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceCreationProcessFixtureMain.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceCreationProcessFixtureMain.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceCreationProcessFixtureMain.java index 48c666d7cf4..a00970a5ddf 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceCreationProcessFixtureMain.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceCreationProcessFixtureMain.java @@ -28,9 +28,9 @@ public final class WorkspaceCreationProcessFixtureMain { required("W0_MYSQL_URL"), required("W0_MYSQL_USER"), System.getenv().getOrDefault("W0_MYSQL_PASSWORD", "")); JdbcTemplate jdbc = new JdbcTemplate(dataSource); - ManagedAgentStore store = new ManagedAgentStore(jdbc, + ManagedAgentStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); TransactionTemplate transactions = new TransactionTemplate( new DataSourceTransactionManager(dataSource)); String tenant = required("W0_TENANT"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseMySqlIT.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseMySqlIT.java index bae85f30211..d8e0439e4b9 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseMySqlIT.java @@ -1,17 +1,17 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RenewWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.RenewWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.SealWriterRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; @@ -77,11 +77,11 @@ class WorkspaceSessionCloseMySqlIT { } private void seedAction(String tenant, String session, String action, String state, String options) { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); jdbc.update("INSERT INTO ma_session_record (session_scope_key,record_key,tenant_id,session_id,record_kind," + "domain,record_id,action_id,revision,action_version,state,options_json,gmt_create,first_sequence)" + " VALUES (?,?,?,?,'ACTION','action',?,?,1,1,?,?,0,1)", - scope, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(scope, "ACTION", action), + scope, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scope, "ACTION", action), tenant, session, action, action, state, options); } @@ -110,7 +110,7 @@ class WorkspaceSessionCloseMySqlIT { void closeSerializesNewWriterAndPreservesAnAlreadyAcquiredWriter() throws Exception { var first = store(Clock.systemUTC()); var second = store(Clock.systemUTC()); - var journal = new ManagedSessionStore(jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var tenant = "mysql-close-writer-" + UUID.randomUUID(); var session = create(first, tenant); try (var pool = Executors.newSingleThreadExecutor()) { @@ -211,8 +211,8 @@ class WorkspaceSessionCloseMySqlIT { @Test void closeWriterCheckWaitsForAnUncommittedRenewalBeforeEvaluatingExpiry() throws Exception { var first = store(Clock.systemUTC()); - var journal = new ManagedSessionStore(jdbc); - var otherJournal = new ManagedSessionStore(new ManagedSchemaJdbcTemplate(source)); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); + var otherJournal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(new ManagedSchemaJdbcTemplate(source))); var tenant = "mysql-close-renew-" + UUID.randomUUID(); var session = create(first, tenant); long generation = transaction(() -> journal.acquireWriter(tenant, session, TOKEN, @@ -271,7 +271,7 @@ class WorkspaceSessionCloseMySqlIT { ManagedSchema.initialize(source); var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - return new ManagedAgentStore(jdbc, mapper, clock, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, clock, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } private String create(ManagedAgentStore store, String tenant) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseTest.java index ce96f9da019..a43fe2cc431 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionCloseTest.java @@ -2,19 +2,19 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.harness.UnavailableHarnessConnector; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.SealWriterRequest; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -175,11 +175,11 @@ class WorkspaceSessionCloseTest { private void seedAction(String tenant, String session, String options) { String action = "tool_approval_" + UUID.randomUUID().toString().replace("-", ""); - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); jdbc.update("INSERT INTO ma_session_record (session_scope_key,record_key,tenant_id,session_id,record_kind," + "domain,record_id,action_id,revision,action_version,state,options_json,gmt_create,first_sequence)" + " VALUES (?,?,?,?,'ACTION','action',?,?,1,1,'requested',?,0,1)", - scope, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(scope, "ACTION", action), + scope, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scope, "ACTION", action), tenant, session, action, action, options); } @@ -226,7 +226,7 @@ class WorkspaceSessionCloseTest { com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry registry) { var properties = new com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - return new ManagedAgentStore(jdbc, mapper, java.time.Clock.systemUTC(), ignored -> {}, registry, properties); + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, java.time.Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionMySqlIT.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionMySqlIT.java index 928c4b30ff1..e0cc57b1631 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionMySqlIT.java @@ -1,19 +1,19 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RenewWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.RenewWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.SealWriterRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedToolResultStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationRetentionStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; @@ -197,7 +197,7 @@ class WorkspaceSessionRetentionMySqlIT { assertThat(jdbc.queryForObject("SELECT recovery_protected FROM ma_session WHERE retirement_generation > 0", Boolean.class)).isTrue(); var retention = new ToolPublicationRetentionStore(jdbc, new DataSourceTransactionManager(source)); assertThat(retention.observe(Duration.ZERO)).singleElement().extracting(ToolPublicationRetentionStore.Candidate::blocker).isEqualTo("recovery_protected"); - assertThatThrownBy(() -> transaction(() -> new ManagedSessionStore(jdbc).acquireWriter(tenant, session, TOKEN, + assertThatThrownBy(() -> transaction(() -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).acquireWriter(tenant, session, TOKEN, new AcquireWriterRequest("workspace", "new-writer", 60_000L)))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("tool_output_session_retired")); } @@ -207,8 +207,8 @@ class WorkspaceSessionRetentionMySqlIT { var original = store(Clock.systemUTC()); String tenant = "config-removed"; String session = closed(original, tenant, false); - var disabled = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + var disabled = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); assertThat(disabled.workspaceFilesEnabled()).isFalse(); transaction(() -> disabled.beginWorkspaceLifecycle(tenant, session, OperationKind.ARCHIVE, OWNER, ACTOR_DIGEST, "archive", "archive-digest", false)); @@ -228,9 +228,9 @@ class WorkspaceSessionRetentionMySqlIT { publication(tenant, session); var deletion = deleteClaim(store, tenant, session); String digest = "c".repeat(64); - String projectionScope = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(tenant, session); - String projectionKey = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.operationKey( - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind.TOOL_RESULT_PROJECTION, + String projectionScope = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(tenant, session); + String projectionKey = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.operationKey( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind.TOOL_RESULT_PROJECTION, projectionScope, digest, "result-1"); jdbc.update("INSERT INTO ma_operation (operation_key, scope_key, session_scope_key, target_kind, target_key, operation_kind, operation_id, tenant_id, workspace_id, session_id, execution_key, request_json, request_digest, state, phase, claim_generation, claim_until, gmt_create, gmt_modified) VALUES (?, ?, ?, 'TOOL_EXECUTION', ?, 'TOOL_RESULT_PROJECTION', 'result-1', ?, 'workspace', ?, ?, '{}', ?, 'LEASED', 'PROJECTING', 1, (" + ManagedSchemaTime.SQL_NOW + " + 60000), 0, 0)", projectionKey, projectionScope, projectionScope, digest, tenant, session, digest, digest); @@ -263,7 +263,7 @@ class WorkspaceSessionRetentionMySqlIT { awaitLatch(completingPublicLock); var writer = pool.submit(() -> { try { - return transaction(() -> new ManagedSessionStore(jdbc).acquireWriter(tenant, session, TOKEN, + return transaction(() -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).acquireWriter(tenant, session, TOKEN, new AcquireWriterRequest("workspace", "late", 60_000L))); } catch (ManagedAgentException rejected) { return rejected; @@ -324,7 +324,7 @@ class WorkspaceSessionRetentionMySqlIT { MySqlLockProbe.awaitLockWaits(jdbc, schema, 1, "the queued writer renewal"); var renewal = pool.submit(() -> { try { - return transaction(() -> new ManagedSessionStore(jdbc).renewWriter(tenant, session, TOKEN, + return transaction(() -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).renewWriter(tenant, session, TOKEN, new RenewWriterRequest("workspace", "writer", 1, 60_000L))); } catch (ManagedAgentException rejected) { return rejected; @@ -351,7 +351,7 @@ class WorkspaceSessionRetentionMySqlIT { // Restore a residual private writer to exercise the retirement safety check. jdbc.update("UPDATE ma_session_head SET state = 'ACTIVE', writer_lease_until = (" + ManagedSchemaTime.SQL_NOW + " + 60000)"); var deletion = deleteClaim(store, tenant, session); - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var renewed = new CountDownLatch(1); var release = new CountDownLatch(1); try (var pool = Executors.newFixedThreadPool(2)) { @@ -440,7 +440,7 @@ class WorkspaceSessionRetentionMySqlIT { private String closed(ManagedAgentStore store, String tenant, boolean journal) { String session = create(store, tenant); if (journal) { - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var grant = transaction(() -> sessions.acquireWriter(tenant, session, TOKEN, new AcquireWriterRequest("workspace", "writer", 60_000L))); transaction(() -> sessions.sealWriter(tenant, session, TOKEN, @@ -471,8 +471,8 @@ class WorkspaceSessionRetentionMySqlIT { return super.update(sql, arguments); } }; - return new ManagedAgentStore(observedJdbc, mapper, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(observedJdbc, mapper, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } private ManagedAgentStore faultStore(String sqlPrefix) { @@ -488,7 +488,7 @@ class WorkspaceSessionRetentionMySqlIT { }; var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - return new ManagedAgentStore(fault, mapper, Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(fault, mapper, Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } private void publication(String tenant, String session) { @@ -499,7 +499,7 @@ class WorkspaceSessionRetentionMySqlIT { + " capture_held_bytes, producer_held_bytes, admission_held_bytes, capture_used_bytes)" + " VALUES (?, ?, ?, 'workspace', ?, 'pub-1', ?, 'capture-1', '{}', ?, ?, 'FENCED'," + " 1000, 1000, 1000, 'REFERENCED', TRUE, TRUE, 1000, 1000, 1000, 123)", - key, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(tenant), tenant, session, key, key, key); + key, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(tenant), tenant, session, key, key, key); } private static byte[] sha256(String value) { @@ -514,7 +514,7 @@ class WorkspaceSessionRetentionMySqlIT { ManagedSchema.initialize(source); var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - return new ManagedAgentStore(jdbc, mapper, clock, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, clock, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } private String create(ManagedAgentStore store, String tenant) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionTest.java index 6280c60cc31..a1344973a6d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/WorkspaceSessionRetentionTest.java @@ -3,22 +3,22 @@ package com.alibaba.qwen.code.managedagent; import com.alibaba.qwen.code.managedagent.api.AuthenticatedTenantActor; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.SealWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.harness.UnavailableHarnessConnector; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.SealWriterRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.session.SessionLifecycleCoordinator; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; @@ -92,9 +92,9 @@ class WorkspaceSessionRetentionTest { var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); properties.getHarness().setWorkspaceShellEnabled(true); - var enabled = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); + var enabled = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var projection = new com.alibaba.qwen.code.managedagent.session.ManagedAgentService(enabled, - new com.alibaba.qwen.code.managedagent.model.RequestDigests(), coordinator, harness, registry); + new com.alibaba.qwen.code.managedagent.model.RequestDigests(), coordinator, harness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); org.springframework.test.util.ReflectionTestUtils.setField(projection, "runtimeWarmer", runtime); assertThat(projection.getPublicSession(tenant, "owner", session).capabilities().sessionClose()).isTrue(); assertThat(projection.getWebShellSession(tenant, "owner", session).capabilities().sessionDelete()).isTrue(); @@ -419,7 +419,7 @@ class WorkspaceSessionRetentionTest { + " actor_digest, idempotency_key, request_digest, state, admission_stage, delivery_state," + " session_status_before, available_at, gmt_create, gmt_modified)" + " VALUES (?, ?, 'SESSION', ?, ?, '{}', 'RECOVERY_BLOCKED', ?, ?, ?, 'CLOSE', '', 'blocked', 'digest', 'RECOVERY_BLOCKED', 'JAVA_DURABLE'," - + " 'BLOCKED', 'ACTIVE', ?, 0, 0)", ManagedSchemaKeys.key("blocked", tenant, session), ManagedSessionStore.sessionScopeKey(tenant, session), ManagedSchemaKeys.key(session), session, tenant, session, "op_" + UUID.randomUUID(), Long.MAX_VALUE); + + " 'BLOCKED', 'ACTIVE', ?, 0, 0)", ManagedSchemaKeys.key("blocked", tenant, session), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), ManagedSchemaKeys.key(session), session, tenant, session, "op_" + UUID.randomUUID(), Long.MAX_VALUE); web("archive", tenant, session, "owner", "fresh").andExpect(status().isConflict()) .andExpect(jsonPath("$.error.code").value("session_operation_active")); web("delete", tenant, session, "owner", "fresh").andExpect(status().isConflict()) @@ -516,7 +516,7 @@ class WorkspaceSessionRetentionTest { ObjectMapper mapper, ManagedWorkspaceRegistry registry) { var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - return new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties); + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/LiteTopicEventTransportTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/event/LiteTopicEventTransportTest.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/LiteTopicEventTransportTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/event/LiteTopicEventTransportTest.java index 6b83984ecf6..b666ff8ccd0 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/LiteTopicEventTransportTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/event/LiteTopicEventTransportTest.java @@ -1,5 +1,7 @@ -package com.alibaba.qwen.code.managedagent.event; +package com.alibaba.qwen.code.managedagent.adapters.event; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelope; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelopes; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.rocketmq.client.apis.ClientServiceProvider; import org.apache.rocketmq.client.apis.message.Message; @@ -21,7 +23,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -class LiteTopicEventTransportTest { +public class LiteTopicEventTransportTest { private static final ObjectMapper JSON = new ObjectMapper(); private static final String TENANT = "t1"; private static final String WORKSPACE = "w1"; @@ -56,7 +58,7 @@ class LiteTopicEventTransportTest { } @Test - void envelopesOfExtractsEventsAndSkipsTheCommitMarker() { + public void envelopesOfExtractsEventsAndSkipsTheCommitMarker() { byte[] recordBytes = journalRecord( eventRecord(1, "evt-1", "input.accepted"), eventRecord(2, "evt-2", "wake.requested"), @@ -75,7 +77,7 @@ class LiteTopicEventTransportTest { } @Test - void envelopeSerializesToTheCanonicalWireForm() throws Exception { + public void envelopeSerializesToTheCanonicalWireForm() throws Exception { byte[] recordBytes = journalRecord( eventRecord(7, "evt-7", "input.accepted"), commitMarker()); ManagedEventEnvelope envelope = @@ -91,7 +93,7 @@ class LiteTopicEventTransportTest { } @Test - void publishCommittedSendsOneMessagePerEventToTheSessionLiteTopic() { + public void publishCommittedSendsOneMessagePerEventToTheSessionLiteTopic() { Producer producer = mock(Producer.class); CompletableFuture ok = CompletableFuture.completedFuture(null); when(producer.sendAsync(any())).thenReturn(ok); @@ -118,7 +120,7 @@ class LiteTopicEventTransportTest { // MQ2c: a wake nobody owns has no subscriber on its Session's LiteTopic, // so the same envelope also rides the shared bootstrap lane. @Test - void aWakeAlsoRidesTheBootstrapLiteTopic() { + public void aWakeAlsoRidesTheBootstrapLiteTopic() { Producer producer = mock(Producer.class); when(producer.sendAsync(any())) .thenReturn(CompletableFuture.completedFuture(null)); @@ -140,7 +142,7 @@ class LiteTopicEventTransportTest { } @Test - void aSynchronouslyRefusedSendNeitherPropagatesNorSkipsTheRest() { + public void aSynchronouslyRefusedSendNeitherPropagatesNorSkipsTheRest() { Producer producer = mock(Producer.class); when(producer.sendAsync(any())) .thenThrow(new IllegalStateException("producer closing")) @@ -155,7 +157,7 @@ class LiteTopicEventTransportTest { } @Test - void publishCommittedIsolatesASendFailure() { + public void publishCommittedIsolatesASendFailure() { Producer producer = mock(Producer.class); CompletableFuture failed = new CompletableFuture<>(); failed.completeExceptionally(new IllegalStateException("broker down")); @@ -170,7 +172,7 @@ class LiteTopicEventTransportTest { } @Test - void publishCommittedNeverThrowsOnAMalformedRecord() { + public void publishCommittedNeverThrowsOnAMalformedRecord() { Producer producer = mock(Producer.class); LiteTopicEventTransport transport = new LiteTopicEventTransport( producer, ClientServiceProvider.loadService(), "journal-parent", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/LiteTopicPublicEventTransportTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/event/LiteTopicPublicEventTransportTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/LiteTopicPublicEventTransportTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/event/LiteTopicPublicEventTransportTest.java index d6d611ca05b..0748f7c524b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/LiteTopicPublicEventTransportTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/event/LiteTopicPublicEventTransportTest.java @@ -1,5 +1,7 @@ -package com.alibaba.qwen.code.managedagent.event; +package com.alibaba.qwen.code.managedagent.adapters.event; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelope; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelopes; import com.alibaba.qwen.code.managedagent.model.StoreModels.EventRecord; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.rocketmq.client.apis.ClientServiceProvider; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentWorkspaceProvisionerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/runtime/ResidentWorkspaceProvisionerTest.java similarity index 89% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentWorkspaceProvisionerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/runtime/ResidentWorkspaceProvisionerTest.java index 0f84117c75e..7b9328a36f8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentWorkspaceProvisionerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/runtime/ResidentWorkspaceProvisionerTest.java @@ -1,13 +1,11 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.adapters.runtime; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ResidentWorkspaces; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryToolExecutionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; @@ -15,6 +13,9 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesHttpRuntimeClient; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; @@ -61,7 +62,7 @@ class ResidentWorkspaceProvisionerTest { "pvc-uid", "volume", "pv-uid", "diskplugin.csi.alibabacloud.com", "handle", "backend", "serial", "/workspace", 1); var mapping = new ManagedAgentProperties.RuntimeBroker.ResidentWorkspace("workspace", 1, registration, - new WorkspaceCsiResourceGuard.ProtectionIdentity("namespace", "policy", "policy-uid", 1, + new com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity("namespace", "policy", "policy-uid", 1, "binding", "binding-uid", 1), "worker@sha256:" + "a".repeat(64), List.of("node", "/worker/cli.js"), List.of()); properties = new ManagedAgentProperties(); @@ -83,7 +84,7 @@ class ResidentWorkspaceProvisionerTest { .thenReturn(List.of(new ObjectMapper().writeValueAsString(registration))); bindings = mock(JdbcRuntimeBindingRepository.class); when(bindings.usesDataSource(source)).thenReturn(true); - wrapper = new ResidentWorkspaceProvisioner(properties, jdbc, mock(PlatformTransactionManager.class), bindings); + wrapper = new ResidentWorkspaceProvisioner(properties, new com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore(jdbc, mock(PlatformTransactionManager.class), new ObjectMapper()), bindings); request = new RuntimeProvisionRequest(ResidentWorkspaces.scope(mapping), null, "kubernetes-workspace", "storage"); } @@ -106,7 +107,7 @@ class ResidentWorkspaceProvisionerTest { WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1); var foreign = new ContextBinding("tenant", "other-workspace", 1, "storage", ".", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1); - try (var broker = new EmbeddedRuntimeBroker(mock(ManagedAgentStore.class), properties, + try (var broker = com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedBrokerFixture.create(mock(ManagedAgentStore.class), properties, bindings, new InMemoryRuntimeSessionRepository(), new InMemoryToolExecutionRepository(), mock(WorkspaceExecutionStore.class), null, null, jdbc, mock(PlatformTransactionManager.class), wrapper)) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiResourceGuardTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiResourceGuardTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiResourceGuardTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiResourceGuardTest.java index 5dbfbc7dd89..f88361ca5c0 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiResourceGuardTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiResourceGuardTest.java @@ -1,8 +1,8 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.adapters.runtime; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesRuntimeClient; +import com.alibaba.qwen.code.runtimebroker.spi.KubernetesRuntimeClient; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; @@ -21,8 +21,8 @@ class WorkspaceCsiResourceGuardTest { private final WorkspaceCsiRegistration registration = new WorkspaceCsiRegistration("tenant", "storage", "cluster", "runtime", "claim", "pvc-uid", "volume", "pv-uid", "diskplugin.csi.alibabacloud.com", "opaque-handle", "backend", "serial", "/workspace", 1); - private final WorkspaceCsiResourceGuard.ProtectionIdentity protection = - new WorkspaceCsiResourceGuard.ProtectionIdentity("ns-uid", "protect", "policy-uid", 2, + private final com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity protection = + new com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity("ns-uid", "protect", "policy-uid", 2, "protect-binding", "binding-uid", 1); @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiRuntimeIdentityTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiRuntimeIdentityTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiRuntimeIdentityTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiRuntimeIdentityTest.java index d98780493cc..0a7afa793ec 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiRuntimeIdentityTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiRuntimeIdentityTest.java @@ -1,9 +1,8 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.adapters.runtime; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeAttestation; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; @@ -14,11 +13,13 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.StoreFailureException; import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesRuntimeClient; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.spi.KubernetesRuntimeClient; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiProtocol; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -62,7 +63,7 @@ class WorkspaceCsiRuntimeIdentityTest { private static final List COMMAND = List.of("node", "/opt/qwen/dist/cli.js"); private final WorkspaceCsiRegistration registration = new WorkspaceCsiRegistration("tenant", "storage", "cluster", "runtime", "claim", "pvc-uid", "volume", "pv-uid", "diskplugin.csi.alibabacloud.com", "handle", "backend", "serial", "/workspace", 1); - private final WorkspaceCsiResourceGuard.ProtectionIdentity protection = new WorkspaceCsiResourceGuard.ProtectionIdentity( + private final com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity protection = new com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity( UID, "protect", UID, 1, "protect-binding", UID, 1); private DriverManagerDataSource source; private JdbcRuntimeBindingRepository bindings; @@ -238,7 +239,8 @@ class WorkspaceCsiRuntimeIdentityTest { original = bindings.compareAndSet(original, original.withResourceHandle(handle, Instant.now())); var lease = join(provider.provision(original.getRequest(), original.getProvisionSeed())); var reservation = storage.inspect(registration); - doThrow(outage).when(storage).inspect(registration); + doThrow(new StoreFailureException("CSI reservation storage is unavailable", outage)) + .when(storage).inspect(registration); for (int retry = 0; retry < 2; retry++) { assertThat(join(provider.reconcile(original.getRequest(), original.getProvisionSeed(), handle, lease)).getOutcome()) .isEqualTo(RuntimeObservation.Outcome.UNKNOWN); @@ -545,7 +547,7 @@ class WorkspaceCsiRuntimeIdentityTest { api.objects.put("configmaps", copy(Map.of("apiVersion", "v1", "kind", "ConfigMap", "immutable", true, "metadata", Map.of("name", "worker-000", "namespace", "runtime", "uid", UID), "binaryData", Map.of("chunk", Base64.getEncoder().encodeToString(chunk))))); - try (var provider = provider(Duration.ofSeconds(5), List.of(new WorkspaceCsiRuntimeProvisioner.WorkerArtifact("worker-000", UID, sha)))) { + try (var provider = provider(Duration.ofSeconds(5), List.of(new com.alibaba.qwen.code.managedagent.contracts.runtime.WorkerArtifact("worker-000", UID, sha)))) { provider.reserveResource(original); var handle = join(provider.ensureResource(original.getRequest(), original.getProvisionSeed(), null)); assertThat(new String(WorkspaceCsiRuntimeIdentity.bytes(api.objects.get("pods")), StandardCharsets.UTF_8)) @@ -583,7 +585,7 @@ class WorkspaceCsiRuntimeIdentityTest { return provider(Duration.ofSeconds(5), List.of()); } - private WorkspaceCsiRuntimeProvisioner provider(Duration timeout, List artifacts) { + private WorkspaceCsiRuntimeProvisioner provider(Duration timeout, List artifacts) { return new WorkspaceCsiRuntimeProvisioner(storage, bindings, registration, api, new WorkspaceCsiResourceGuard(api, "cluster", registration, protection), IMAGE, COMMAND, timeout, artifacts, transport); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiRuntimeProvisionerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiRuntimeProvisionerTest.java similarity index 90% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiRuntimeProvisionerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiRuntimeProvisionerTest.java index 8027e8d4168..968cba00f65 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceCsiRuntimeProvisionerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/runtime/WorkspaceCsiRuntimeProvisionerTest.java @@ -1,19 +1,19 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.adapters.runtime; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorktreeGitTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/workspace/ChildWorktreeGitTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorktreeGitTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/workspace/ChildWorktreeGitTest.java index ec0313671d4..2a14c86c4a4 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorktreeGitTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/adapters/workspace/ChildWorktreeGitTest.java @@ -1,5 +1,6 @@ -package com.alibaba.qwen.code.managedagent.workspace; +package com.alibaba.qwen.code.managedagent.adapters.workspace; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorkspaceException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -32,23 +33,24 @@ import static org.mockito.Mockito.CALLS_REAL_METHODS; import static org.mockito.Mockito.mockStatic; /** - * The physical steps of a child Workspace against real repositories - * (#13753 I1, docs/design/2026-10-09-managed-child-workspace.md). Every - * assertion reads the disk through a separate plain Git, never through the - * runner under test. + * The physical steps of a child Workspace against real repositories (#13753 I1, + * docs/design/2026-10-09-managed-child-workspace.md). Every assertion reads the disk through a + * separate plain Git, never through the runner under test. */ -@DisabledOnOs(value = OS.WINDOWS, disabledReason = "Child Workspaces are not supported on Windows") -class ChildWorktreeGitTest { +@DisabledOnOs( + value = OS.WINDOWS, + disabledReason = "Child Workspaces are not supported on Windows") +public class ChildWorktreeGitTest { private static ChildWorktreeGit git; private static String unavailable; @TempDir - Path temp; + public Path temp; private Path root; private Path project; @BeforeAll - static void probeGit() { + public static void probeGit() { git = new ChildWorktreeGit("git", Duration.ofSeconds(60)); unavailable = unusableGit(git); } @@ -59,7 +61,7 @@ class ChildWorktreeGitTest { * plain Git reports as 2.40 or later fails instead: a broken probe must * not pass for an absent Git. */ - static String unusableGit(ChildWorktreeGit runner) { + public static String unusableGit(ChildWorktreeGit runner) { try { runner.requireSupportedVersion(); return null; @@ -81,12 +83,12 @@ class ChildWorktreeGitTest { } @AfterAll - static void closeGit() { + public static void closeGit() { git.close(); } @BeforeEach - void repository() throws Exception { + public void repository() throws Exception { // Per test, so a host without a usable Git reports every case // skipped instead of an empty class. assumeTrue(unavailable == null, () -> "Git 2.40 or later is unavailable: " + unavailable); @@ -101,7 +103,7 @@ class ChildWorktreeGitTest { } @Test - void snapshotCarriesTheDirtyTreeAndLeavesTheParentUntouched() throws Exception { + public void snapshotCarriesTheDirtyTreeAndLeavesTheParentUntouched() throws Exception { write(project, "f.txt", "A\nb\nc\nd\ne\n"); write(project, "untracked.txt", "u\n"); write(project, "ignored/out.bin", "build\n"); @@ -124,7 +126,7 @@ class ChildWorktreeGitTest { } @Test - void snapshotCarriesTheWorkingTreeWhateverTheParentsIndexSays() throws Exception { + public void snapshotCarriesTheWorkingTreeWhateverTheParentsIndexSays() throws Exception { write(project, "hidden.txt", "h0\n"); write(project, "assumed.txt", "a0\n"); plain(project, "add", "-A"); @@ -148,7 +150,7 @@ class ChildWorktreeGitTest { } @Test - void layoutNamesTheRepositoryAndTheOffsetInsideIt() throws Exception { + public void layoutNamesTheRepositoryAndTheOffsetInsideIt() throws Exception { Files.createDirectories(project.resolve("pkg/a")); write(project, "pkg/a/x.txt", "x\n"); assertThat(git.layout(root, "project")).isEqualTo(new ChildWorktreeGit.Layout("project", ".")); @@ -156,7 +158,7 @@ class ChildWorktreeGitTest { } @Test - void layoutRefusesEveryShapeDecisionTwoRulesOut() throws Exception { + public void layoutRefusesEveryShapeDecisionTwoRulesOut() throws Exception { Files.createDirectory(root.resolve("plain")); assertCode(() -> git.layout(root, "plain"), ChildWorkspaceException.LAYOUT); assertCode(() -> git.layout(root, "missing"), ChildWorkspaceException.LAYOUT); @@ -184,7 +186,7 @@ class ChildWorktreeGitTest { } @Test - void configBooleansAreReadAsGitReadsThem() { + public void configBooleansAreReadAsGitReadsThem() { for (String value : new String[] {null, "true", "Yes", "ON", "1", "2", "-1", "100", "1k", "0x10", "garbage"}) { assertThat(ChildWorktreeGit.truthy(value)).as(String.valueOf(value)).isTrue(); } @@ -194,14 +196,14 @@ class ChildWorktreeGitTest { } @Test - void aStorageRootInsideARepositoryIsRefused() throws Exception { + public void aStorageRootInsideARepositoryIsRefused() throws Exception { plain(root, "init", "-q"); assertCode(() -> git.layout(root, "."), ChildWorkspaceException.LAYOUT); assertCode(() -> git.open(root, "."), ChildWorkspaceException.LAYOUT); } @Test - void anUnsafeConfigIsRefusedAndItsProgramNeverRuns() throws Exception { + public void anUnsafeConfigIsRefusedAndItsProgramNeverRuns() throws Exception { Path marker = temp.resolve("filter-ran"); plain(project, "config", "filter.evil.clean", "touch " + marker + "; cat"); write(project, ".gitattributes", "* filter=evil\n"); @@ -228,7 +230,7 @@ class ChildWorktreeGitTest { } @Test - void anFsmonitorTheRepositoryNamesNeverRuns() throws Exception { + public void anFsmonitorTheRepositoryNamesNeverRuns() throws Exception { Path marker = temp.resolve("fsmonitor-ran"); Path monitor = temp.resolve("fsmonitor"); Files.writeString(monitor, "#!/bin/sh\ntouch " + marker + "\nexit 1\n"); @@ -244,7 +246,7 @@ class ChildWorktreeGitTest { } @Test - void everyCommandRunsWithAPrivateHomeAndNoSystemOrGlobalConfig() throws Exception { + public void everyCommandRunsWithAPrivateHomeAndNoSystemOrGlobalConfig() throws Exception { Path dump = temp.resolve("env"); Path wrapper = temp.resolve("env-git"); Files.writeString(wrapper, "#!/bin/sh\n[ -f '" + dump + "' ] || env > '" + dump + "'\nexec git \"$@\"\n"); @@ -279,7 +281,7 @@ class ChildWorktreeGitTest { } @Test - void aPromisorRemoteNeverRunsItsProgram() throws Exception { + public void aPromisorRemoteNeverRunsItsProgram() throws Exception { Path marker = temp.resolve("upload-pack-ran"); Path remote = Files.createDirectory(temp.toRealPath().resolve("remote")); plain(remote, "init", "-q", "--bare"); @@ -304,7 +306,7 @@ class ChildWorktreeGitTest { } @Test - void theRefusedKeysAreTheOnesTheseCommandsReach() { + public void theRefusedKeysAreTheOnesTheseCommandsReach() { for (String key : List.of("filter.lfs.clean", "filter.x.process", "include.path", "includeIf.gitdir:/x.path", "merge.ours.driver", "diff.external", "diff.pdf.textconv", "diff.pdf.command", "core.worktree", "FILTER.X.SMUDGE", "extensions.partialClone", @@ -319,7 +321,7 @@ class ChildWorktreeGitTest { } @Test - void aChildsOwnWorktreeConfigIsCheckedBeforeAnyCommandRunsInIt() throws Exception { + public void aChildsOwnWorktreeConfigIsCheckedBeforeAnyCommandRunsInIt() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -338,7 +340,7 @@ class ChildWorktreeGitTest { } @Test - void aChildWorktreePointedAtAnotherRepositoryIsRefused() throws Exception { + public void aChildWorktreePointedAtAnotherRepositoryIsRefused() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -368,7 +370,8 @@ class ChildWorktreeGitTest { } @Test - void repositoryHooksNeverRunBecauseEveryCommandOverridesTheirPath() throws Exception { + public void repositoryHooksNeverRunBecauseEveryCommandOverridesTheirPath() + throws Exception { Path hooks = Files.createDirectory(project.resolve(".husky")); Path marker = temp.resolve("hook-ran"); Path hook = hooks.resolve("post-checkout"); @@ -401,7 +404,7 @@ class ChildWorktreeGitTest { } @Test - void aCleanMergeLandsAsUncommittedChangesAndRemovesTheWorktree() throws Exception { + public void aCleanMergeLandsAsUncommittedChangesAndRemovesTheWorktree() throws Exception { write(project, "untracked.txt", "u\n"); var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); @@ -447,7 +450,8 @@ class ChildWorktreeGitTest { } @Test - void theRefSweepDeletesSymbolicRefsThemselvesAndKeepsTheStashes() throws Exception { + public void theRefSweepDeletesSymbolicRefsThemselvesAndKeepsTheStashes() + throws Exception { var repository = git.open(root, "project"); String base = git.base(root, repository, "c1"); git.create(root, repository, "c1", base); @@ -480,7 +484,7 @@ class ChildWorktreeGitTest { } @Test - void theRefSweepStandsDownOnceTheBaseReachesAnotherHistory() throws Exception { + public void theRefSweepStandsDownOnceTheBaseReachesAnotherHistory() throws Exception { var repository = git.open(root, "project"); plain(project, "branch", "parents"); Path other = temp.toRealPath().resolve("other-worktree"); @@ -527,7 +531,7 @@ class ChildWorktreeGitTest { } @Test - void aDiscardSurvivesAFileAtTheContainersName() throws Exception { + public void aDiscardSurvivesAFileAtTheContainersName() throws Exception { write(root, ChildWorktreeGit.CONTAINER, "not a directory\n"); git.discard(root, "project", "c1", false); @@ -537,7 +541,7 @@ class ChildWorktreeGitTest { } @Test - void whateverStandsAtTheRecordsNameGoesWithTheDiscard() throws Exception { + public void whateverStandsAtTheRecordsNameGoesWithTheDiscard() throws Exception { var repository = git.open(root, "project"); git.create(root, repository, "c1", git.base(root, repository, "c1")); Path record = root.resolve(ChildWorktreeGit.CONTAINER + "/c1.refs"); @@ -551,7 +555,8 @@ class ChildWorktreeGitTest { } @Test - void theSweepTakesOnlyTheChildsCheckedOutLineAndOnlyWithAResult() throws Exception { + public void theSweepTakesOnlyTheChildsCheckedOutLineAndOnlyWithAResult() + throws Exception { var repository = git.open(root, "project"); String base = git.base(root, repository, "c1"); git.create(root, repository, "c1", base); @@ -594,7 +599,7 @@ class ChildWorktreeGitTest { } @Test - void siblingsFromOneParentStateHaveBasesAndSweepsOfTheirOwn() throws Exception { + public void siblingsFromOneParentStateHaveBasesAndSweepsOfTheirOwn() throws Exception { var repository = git.open(root, "project"); String first = git.base(root, repository, "c1"); String second = git.base(root, repository, "c2"); @@ -619,7 +624,7 @@ class ChildWorktreeGitTest { } @Test - void aRefRecordTooLargeToReadSweepsNothing() throws Exception { + public void aRefRecordTooLargeToReadSweepsNothing() throws Exception { var repository = git.open(root, "project"); String base = git.base(root, repository, "c1"); git.create(root, repository, "c1", base); @@ -640,7 +645,7 @@ class ChildWorktreeGitTest { } @Test - void lineEndingChecksNeitherRefuseNorFloodTheSnapshot() throws Exception { + public void lineEndingChecksNeitherRefuseNorFloodTheSnapshot() throws Exception { plain(project, "config", "core.safecrlf", "true"); write(project, ".gitattributes", "* text=auto\n"); write(project, "crlf.txt", "one\r\ntwo\r\n"); @@ -651,7 +656,7 @@ class ChildWorktreeGitTest { } @Test - void aMergedPathTheParentIgnoresLandsAndResumes() throws Exception { + public void aMergedPathTheParentIgnoresLandsAndResumes() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -672,7 +677,7 @@ class ChildWorktreeGitTest { } @Test - void aMergeThatWouldOverwriteAnIgnoredFileIsAConflict() throws Exception { + public void aMergeThatWouldOverwriteAnIgnoredFileIsAConflict() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -690,7 +695,8 @@ class ChildWorktreeGitTest { } @Test - void aChildUntrackingAParentsUntrackedFileIsAConflictNotDeletion() throws Exception { + public void aChildUntrackingAParentsUntrackedFileIsAConflictNotDeletion() + throws Exception { var repository = git.open(root, "project"); write(project, "loose.txt", "the parent's only copy\n"); String base = git.snapshot(root, repository, project, null); @@ -708,7 +714,7 @@ class ChildWorktreeGitTest { } @Test - void aChildDeletingATrackedParentFileStillLands() throws Exception { + public void aChildDeletingATrackedParentFileStillLands() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -724,7 +730,7 @@ class ChildWorktreeGitTest { } @Test - void aChildDeletingAnUncommittedEditIsAConflict() throws Exception { + public void aChildDeletingAnUncommittedEditIsAConflict() throws Exception { write(project, "keep.txt", "the parent's uncommitted edit\n"); var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); @@ -741,7 +747,7 @@ class ChildWorktreeGitTest { } @Test - void aChildDeletingAFileTheParentOnlyStagedIsAConflict() throws Exception { + public void aChildDeletingAFileTheParentOnlyStagedIsAConflict() throws Exception { // The index alone does not keep it: the parent's next `git add -A` // would stage the deletion and drop the only entry. write(project, "staged.txt", "staged\n"); @@ -760,7 +766,7 @@ class ChildWorktreeGitTest { } @Test - void aDeletionLandsWhereTheParentKeepsTheContentElsewhere() throws Exception { + public void aDeletionLandsWhereTheParentKeepsTheContentElsewhere() throws Exception { // Taken out of the index, still committed: HEAD keeps it. plain(project, "rm", "-q", "--cached", "keep.txt"); // Never tracked, moved by the child: the merge writes it back. @@ -784,7 +790,7 @@ class ChildWorktreeGitTest { } @Test - void aResumedWriteNeverDeletesContentTheParentStoppedKeeping() throws Exception { + public void aResumedWriteNeverDeletesContentTheParentStoppedKeeping() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -804,7 +810,7 @@ class ChildWorktreeGitTest { } @Test - void aResumeThatOwesNothingPaysNoDeletionProbe() throws Exception { + public void aResumeThatOwesNothingPaysNoDeletionProbe() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -836,7 +842,8 @@ class ChildWorktreeGitTest { } @Test - void aResumedWriteIgnoresAnAlreadyLandedDeletionTheParentStoppedKeeping() throws Exception { + public void aResumedWriteIgnoresAnAlreadyLandedDeletionTheParentStoppedKeeping() + throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -859,7 +866,7 @@ class ChildWorktreeGitTest { } @Test - void aDeletionLandsWhenTheParentOnlyChangedTheMode() throws Exception { + public void aDeletionLandsWhenTheParentOnlyChangedTheMode() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -877,7 +884,7 @@ class ChildWorktreeGitTest { } @Test - void aTrackedFileTheMergeTurnsIntoADirectoryLands() throws Exception { + public void aTrackedFileTheMergeTurnsIntoADirectoryLands() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -893,7 +900,7 @@ class ChildWorktreeGitTest { } @Test - void anIgnoredFileWhereAMergedPathNeedsADirectoryIsAConflict() throws Exception { + public void anIgnoredFileWhereAMergedPathNeedsADirectoryIsAConflict() throws Exception { write(project, ".gitignore", "ignored/\nout\n"); plain(project, "commit", "-qam", "ignore out"); var repository = git.open(root, "project"); @@ -913,7 +920,7 @@ class ChildWorktreeGitTest { } @Test - void namesBeyondAsciiMerge() throws Exception { + public void namesBeyondAsciiMerge() throws Exception { write(project, "中文.txt", "parent\n"); plain(project, "add", "-A"); plain(project, "commit", "-qm", "unicode"); @@ -933,7 +940,8 @@ class ChildWorktreeGitTest { @Test @org.junit.jupiter.api.condition.EnabledOnOs(OS.LINUX) - void aNameThatIsNotUtf8IsAConflict() throws Exception { + public void + aNameThatIsNotUtf8IsAConflict() throws Exception { var repository = git.open(root, "project"); git.create(root, repository, "c2", git.snapshot(root, repository, project, null)); Path second = root.resolve(ChildWorktreeGit.CONTAINER + "/c2"); @@ -949,7 +957,7 @@ class ChildWorktreeGitTest { } @Test - void aSymbolicRefAtAPinsNameIsReplacedNeverFollowed() throws Exception { + public void aSymbolicRefAtAPinsNameIsReplacedNeverFollowed() throws Exception { String main = plain(project, "rev-parse", "refs/heads/main"); var repository = git.open(root, "project"); String base = git.base(root, repository, "c1"); @@ -969,7 +977,7 @@ class ChildWorktreeGitTest { } @Test - void aMovedSubmodulePointerIsAConflictNotASilentMerge() throws Exception { + public void aMovedSubmodulePointerIsAConflictNotASilentMerge() throws Exception { String v1 = "1".repeat(40); String v2 = "2".repeat(40); Files.createDirectory(project.resolve("sub")); @@ -997,7 +1005,7 @@ class ChildWorktreeGitTest { } @Test - void theRepositorysDiffConfigCannotSpoilTheWrite() throws Exception { + public void theRepositorysDiffConfigCannotSpoilTheWrite() throws Exception { write(project, "f.txt", "1\n2\n3\n4\n5\n6\n7\n8\n9\n"); plain(project, "commit", "-qam", "lines"); plain(project, "config", "color.ui", "always"); @@ -1018,7 +1026,7 @@ class ChildWorktreeGitTest { } @Test - void aConflictNamesItsPathsAndWritesNothing() throws Exception { + public void aConflictNamesItsPathsAndWritesNothing() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -1039,7 +1047,7 @@ class ChildWorktreeGitTest { } @Test - void applyResumesFromAHalfWrittenTree() throws Exception { + public void applyResumesFromAHalfWrittenTree() throws Exception { var merge = mergeTwoFiles(); write(project, "f.txt", "a\nb\nc\nd\nCHILD\n"); @@ -1052,7 +1060,7 @@ class ChildWorktreeGitTest { } @Test - void applyNeverOverwritesAPathChangedOutsideTheLease() throws Exception { + public void applyNeverOverwritesAPathChangedOutsideTheLease() throws Exception { var merge = mergeTwoFiles(); write(project, "keep.txt", "someone else\n"); @@ -1064,7 +1072,7 @@ class ChildWorktreeGitTest { } @Test - void createIsIdempotentAndRebuildsAnInterruptedCheckout() throws Exception { + public void createIsIdempotentAndRebuildsAnInterruptedCheckout() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -1084,7 +1092,7 @@ class ChildWorktreeGitTest { } @Test - void aLinkAtTheReservedPathIsRemovedAndNeverFollowed() throws Exception { + public void aLinkAtTheReservedPathIsRemovedAndNeverFollowed() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); Path outside = Files.createDirectory(temp.toRealPath().resolve("outside")); @@ -1106,7 +1114,8 @@ class ChildWorktreeGitTest { } @Test - void aLinkedWorktreesDirectoryIsRefusedAndNothingOutsideIsTouched() throws Exception { + public void aLinkedWorktreesDirectoryIsRefusedAndNothingOutsideIsTouched() + throws Exception { Path outside = Files.createDirectory(temp.toRealPath().resolve("outside")); write(outside, "important/data.txt", "precious\n"); Path admin = project.resolve(".git/worktrees"); @@ -1124,7 +1133,8 @@ class ChildWorktreeGitTest { } @Test - void linkedMetadataIsRefusedBeforeDiscoveryFromTheRepositoryOrNestedCwd() throws Exception { + public void linkedMetadataIsRefusedBeforeDiscoveryFromTheRepositoryOrNestedCwd() + throws Exception { Path outside = Files.createDirectory(temp.toRealPath().resolve("outside")); Path marker = temp.resolve("git-was-started"); write(outside, "important/data.txt", "precious\n"); @@ -1167,7 +1177,8 @@ class ChildWorktreeGitTest { } @Test - void discoveryPreservesRegularGitFilesAndRelativeCommonDirectoryPointers() throws Exception { + public void discoveryPreservesRegularGitFilesAndRelativeCommonDirectoryPointers() + throws Exception { Path separate = Files.createDirectory(root.resolve("separate")); Path gitDir = root.resolve("metadata with trailing space "); plain(separate, "init", "-q", "--separate-git-dir", gitDir.toString()); @@ -1180,7 +1191,7 @@ class ChildWorktreeGitTest { } @Test - void aVanishedObjectEntryDoesNotRejectTheRepository() throws Exception { + public void aVanishedObjectEntryDoesNotRejectTheRepository() throws Exception { Path objects = project.resolve(".git/objects"); Path removed = objects.resolve("removed-object"); try (var files = mockStatic(Files.class, CALLS_REAL_METHODS)) { @@ -1195,7 +1206,7 @@ class ChildWorktreeGitTest { } @Test - void aLinkWhereGitWritesRefsLogsOrObjectsIsRefused() throws Exception { + public void aLinkWhereGitWritesRefsLogsOrObjectsIsRefused() throws Exception { Path outside = Files.createDirectory(temp.toRealPath().resolve("outside")); write(outside, "victim.txt", "precious\n"); var repository = git.open(root, "project"); @@ -1229,7 +1240,7 @@ class ChildWorktreeGitTest { } @Test - void aDiscardForgetsOnlyItsOwnWorktree() throws Exception { + public void aDiscardForgetsOnlyItsOwnWorktree() throws Exception { Path elsewhere = temp.toRealPath().resolve("parents-other-worktree"); plain(project, "worktree", "add", "-q", "--detach", elsewhere.toString()); deleteTree(elsewhere); @@ -1249,7 +1260,7 @@ class ChildWorktreeGitTest { } @Test - void aLinkedContainerIsRefused() throws Exception { + public void aLinkedContainerIsRefused() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); Path outside = Files.createDirectory(temp.toRealPath().resolve("outside")); @@ -1263,7 +1274,8 @@ class ChildWorktreeGitTest { } @Test - void discardKeepsTheResultPinWhenAskedAndSurvivesAGoneRepository() throws Exception { + public void discardKeepsTheResultPinWhenAskedAndSurvivesAGoneRepository() + throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -1284,7 +1296,7 @@ class ChildWorktreeGitTest { } @Test - void aDiscardThatCannotRunGitKeepsTheWorktree() throws Exception { + public void aDiscardThatCannotRunGitKeepsTheWorktree() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -1298,7 +1310,7 @@ class ChildWorktreeGitTest { } @Test - void aDiscardOfARepositoryThatTurnedUnsafeKeepsEverything() throws Exception { + public void aDiscardOfARepositoryThatTurnedUnsafeKeepsEverything() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); git.create(root, repository, "c1", base); @@ -1312,7 +1324,7 @@ class ChildWorktreeGitTest { } @Test - void aStepThatLostItsClaimStartsNoGitAndStopsTheRunningOne() throws Exception { + public void aStepThatLostItsClaimStartsNoGitAndStopsTheRunningOne() throws Exception { var repository = git.open(root, "project"); assertCode(() -> ChildWorktreeGit.whileLive(() -> false, () -> git.snapshot(root, repository, project, null)), ChildWorkspaceException.GIT); @@ -1350,7 +1362,7 @@ class ChildWorktreeGitTest { } @Test - void aCommandPastItsTimeBoundIsStopped() throws Exception { + public void aCommandPastItsTimeBoundIsStopped() throws Exception { Path pid = temp.resolve("hung-pid"); Path kid = temp.resolve("hung-kid"); Path hung = script("hung-git", "echo $$ > '" + pid + "'\nsleep 30 &\necho $! > '" + kid + "'\nwait\n" @@ -1373,7 +1385,7 @@ class ChildWorktreeGitTest { } @Test - void outputPastItsBoundIsRefusedWhileTheCommandRuns() throws Exception { + public void outputPastItsBoundIsRefusedWhileTheCommandRuns() throws Exception { // A slow, endless writer: it never exits, so only the bound checked // while it runs can stop it before the time bound does. for (String stream : List.of("", " >&2")) { @@ -1395,7 +1407,7 @@ class ChildWorktreeGitTest { } @Test - void aGoneRepositoryIsToldApartFromAChangedOne() throws Exception { + public void aGoneRepositoryIsToldApartFromAChangedOne() throws Exception { plain(project, "config", "core.bare", "true"); assertCode(() -> git.open(root, "project"), ChildWorkspaceException.LAYOUT); plain(project, "config", "core.bare", "false"); @@ -1404,7 +1416,7 @@ class ChildWorktreeGitTest { } @Test - void theResultOfAGoneWorktreeIsMissing() throws Exception { + public void theResultOfAGoneWorktreeIsMissing() throws Exception { var repository = git.open(root, "project"); String base = git.snapshot(root, repository, project, null); assertCode(() -> git.result(root, repository, "c1", base), ChildWorkspaceException.MISSING); @@ -1472,7 +1484,7 @@ class ChildWorktreeGitTest { } /** A plain Git of the test's own, independent of the runner under test. */ - static String plain(Path directory, String... args) throws Exception { + public static String plain(Path directory, String... args) throws Exception { List command = new ArrayList<>(List.of("git", "-C", directory.toString())); command.addAll(List.of(args)); ProcessBuilder builder = new ProcessBuilder(command).redirectErrorStream(true); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandlerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandlerTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandlerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandlerTest.java index 7a0a46fbd0e..98d5364cf1c 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandlerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/ApiExceptionHandlerTest.java @@ -4,11 +4,11 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationStorageException; import com.alibaba.qwen.code.managedagent.config.InternalSurfaceConfiguration.RoutingFilter; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationStorageException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; @@ -43,7 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat; class ApiExceptionHandlerTest { private static final String SECRET = "sk_live_PRIVATE_BODY_VALUE"; private static final String CONTRACT = - "com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract"; + "com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract"; @Test void reportsStorageAccessDeniedAsDefiniteRejectionWithoutProviderDetails() { @@ -73,7 +73,7 @@ class ApiExceptionHandlerTest { response.setCommitted(true); assertThat(new ApiExceptionHandler().api(new ManagedAgentException( - HttpStatus.NOT_FOUND, "session_not_found", "gone"), + 404, "session_not_found", "gone"), new MockHttpServletRequest(), response)).isNull(); assertThat(new ApiExceptionHandler().runtime(new RuntimeBrokerException( 409, "workspace_unavailable", "Maintenance", false), diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSnapshotRestoreResponseTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSnapshotRestoreResponseTest.java similarity index 89% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSnapshotRestoreResponseTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSnapshotRestoreResponseTest.java index 44b08ddf14b..1003eab5ddb 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSnapshotRestoreResponseTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSnapshotRestoreResponseTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent.api; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RestoreHead; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.ToolResultResourceRef; import com.alibaba.qwen.code.managedagent.persistence.ManagedNativeJournalSnapshots; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.RestoreHead; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.ToolResultResourceRef; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSurfaceCapture.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSurfaceCapture.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSurfaceCapture.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSurfaceCapture.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSurfaceCaptureTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSurfaceCaptureTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSurfaceCaptureTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/ManagedSurfaceCaptureTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilterTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilterTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilterTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilterTest.java index 4993092a337..9d7e27fdf91 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilterTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/SignatureAuthFilterTest.java @@ -23,13 +23,14 @@ import java.util.HexFormat; import static org.assertj.core.api.Assertions.assertThat; -class SignatureAuthFilterTest { +public class SignatureAuthFilterTest { private static final String KEY = "0123456789abcdef0123456789abcdef"; private static final ObjectMapper JSON = new ObjectMapper(); @Test - void authenticatesExtensionArchivesFromADiskSpoolAndSignsTheirManifestQuery() throws Exception { + public void authenticatesExtensionArchivesFromADiskSpoolAndSignsTheirManifestQuery() + throws Exception { ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getAuth().setMode("signed"); properties.getAuth().setSigningKey(KEY); @@ -64,7 +65,7 @@ class SignatureAuthFilterTest { } @Test - void staysOutOfOpenModeAndRunsBeforeTheHeaderStandIn() throws Exception { + public void staysOutOfOpenModeAndRunsBeforeTheHeaderStandIn() throws Exception { SignatureAuthFilter filter = filter(open()); MockHttpServletRequest request = request("GET", "/v1/agents/sessions"); @@ -78,7 +79,7 @@ class SignatureAuthFilterTest { } @Test - void acceptsAValidSignatureAndInstallsThePrincipal() throws Exception { + public void acceptsAValidSignatureAndInstallsThePrincipal() throws Exception { SignatureAuthFilter filter = filter(signed()); String timestamp = now(); MockHttpServletRequest request = request("POST", @@ -100,7 +101,7 @@ class SignatureAuthFilterTest { } @Test - void rejectsMissingAndWrongSignatures() throws Exception { + public void rejectsMissingAndWrongSignatures() throws Exception { SignatureAuthFilter filter = filter(signed()); String timestamp = now(); @@ -139,7 +140,7 @@ class SignatureAuthFilterTest { } @Test - void localDirectoryImportStillRequiresSignedTenantAndActor() throws Exception { + public void localDirectoryImportStillRequiresSignedTenantAndActor() throws Exception { ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getAuth().setMode("signed"); properties.getAuth().setSigningKey(KEY); @@ -185,8 +186,7 @@ class SignatureAuthFilterTest { } @Test - void doesNotCoverTheInternalSurfaceAndIgnoresAMissingTenant() - throws Exception { + public void doesNotCoverTheInternalSurfaceAndIgnoresAMissingTenant() throws Exception { SignatureAuthFilter filter = filter(signed()); MockHttpServletRequest internal = new MockHttpServletRequest("POST", "/internal/managed-session-store/v1/sessions/s/writers:acquire"); @@ -204,7 +204,7 @@ class SignatureAuthFilterTest { } @Test - void rejectsAnUnparsableTimestamp() throws Exception { + public void rejectsAnUnparsableTimestamp() throws Exception { SignatureAuthFilter filter = filter(signed()); MockHttpServletRequest request = request("GET", "/v1/agents/sessions"); @@ -219,7 +219,7 @@ class SignatureAuthFilterTest { } @Test - void coversBodyQueryAndIdempotencyKey() throws Exception { + public void coversBodyQueryAndIdempotencyKey() throws Exception { SignatureAuthFilter filter = filter(signed()); byte[] body = "{\"agent_id\":\"qwen-code\"}" .getBytes(StandardCharsets.UTF_8); @@ -254,7 +254,7 @@ class SignatureAuthFilterTest { } @Test - void honorsTheConfiguredDriftWindow() throws Exception { + public void honorsTheConfiguredDriftWindow() throws Exception { ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getAuth().setMode("signed"); properties.getAuth().setSigningKey(KEY); @@ -281,7 +281,7 @@ class SignatureAuthFilterTest { } @Test - void coversTheWebShellSurface() throws Exception { + public void coversTheWebShellSurface() throws Exception { SignatureAuthFilter filter = filter(signed()); MockHttpServletResponse unsigned = new MockHttpServletResponse(); filter.doFilter(request("POST", @@ -306,8 +306,7 @@ class SignatureAuthFilterTest { } @Test - void coversTheBareCollectionRouteAndNormalizedSpellings() - throws Exception { + public void coversTheBareCollectionRouteAndNormalizedSpellings() throws Exception { SignatureAuthFilter filter = filter(signed()); for (String path : new String[] {"/v1/agents", "/v1/%61gents", "/v1/agents;jsessionid=abc"}) { @@ -334,7 +333,7 @@ class SignatureAuthFilterTest { } @Test - void requiresEachSignatureHeader() throws Exception { + public void requiresEachSignatureHeader() throws Exception { SignatureAuthFilter filter = filter(signed()); String timestamp = now(); String signature = sign("GET", "/v1/agents/sessions", "tenant-a", @@ -364,7 +363,7 @@ class SignatureAuthFilterTest { } @Test - void rejectsARepeatedIdempotencyKey() throws Exception { + public void rejectsARepeatedIdempotencyKey() throws Exception { SignatureAuthFilter filter = filter(signed()); String timestamp = now(); MockHttpServletRequest request = request("POST", @@ -383,7 +382,7 @@ class SignatureAuthFilterTest { } @Test - void boundsTheBufferedBody() throws Exception { + public void boundsTheBufferedBody() throws Exception { ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getAuth().setMode("signed"); properties.getAuth().setSigningKey(KEY); @@ -424,7 +423,7 @@ class SignatureAuthFilterTest { } @Test - void signsTheRawUriOnANormalizedSpelling() throws Exception { + public void signsTheRawUriOnANormalizedSpelling() throws Exception { SignatureAuthFilter filter = filter(signed()); String timestamp = now(); // Signed over the raw (undecoded) URI: accepted. @@ -453,7 +452,7 @@ class SignatureAuthFilterTest { } @Test - void boundsAChunkedBodyWhoseLengthIsUndeclared() throws Exception { + public void boundsAChunkedBodyWhoseLengthIsUndeclared() throws Exception { ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getAuth().setMode("signed"); properties.getAuth().setSigningKey(KEY); @@ -486,7 +485,7 @@ class SignatureAuthFilterTest { } @Test - void signsTheQueryString() throws Exception { + public void signsTheQueryString() throws Exception { SignatureAuthFilter filter = filter(signed()); String timestamp = now(); String signature = sign("GET", "/v1/agents/sessions", "limit=2", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistry.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistry.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistry.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistry.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistryGateNegativeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistryGateNegativeTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistryGateNegativeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistryGateNegativeTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistryGateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistryGateTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistryGateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/SurfaceRegistryGateTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/TenantContextFilterTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/TenantContextFilterTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/TenantContextFilterTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/TenantContextFilterTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationControllerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationControllerTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationControllerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationControllerTest.java index 1630cf5c2e0..e014797c5e4 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationControllerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/ToolPublicationControllerTest.java @@ -1,16 +1,16 @@ package com.alibaba.qwen.code.managedagent.api; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; import com.alibaba.qwen.code.managedagent.model.TenantContext; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationAdmissionStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; @@ -183,7 +183,7 @@ class ToolPublicationControllerTest { properties.getSessionStore().setBindingKey("0123456789abcdef0123456789abcdef"); properties.getToolPublication().setEntryConcurrency(1); WriterCredentialPolicy credentials = new WriterCredentialPolicy(properties); - ManagedSessionStore sessions = new ManagedSessionStore(jdbc); + ManagedSessionStore sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); sessions.setCredentials(credentials); writerToken = credentials.issue(TENANT, WORKSPACE, SESSION); var grant = new TransactionTemplate(manager).execute(transaction -> sessions.acquireWriter( diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/TrustedActorHeaderFilterTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/TrustedActorHeaderFilterTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/TrustedActorHeaderFilterTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/TrustedActorHeaderFilterTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationControllerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationControllerTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationControllerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationControllerTest.java index 1c8a1f8ec9f..97712cfb2c7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationControllerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/WorkspaceCsiApplicationControllerTest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.api; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiApplicationStore; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/WorkspaceFileControllerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/WorkspaceFileControllerTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/api/WorkspaceFileControllerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/api/WorkspaceFileControllerTest.java diff --git a/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/architecture/ModuleArchitectureTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/architecture/ModuleArchitectureTest.java new file mode 100644 index 00000000000..9d549e035b0 --- /dev/null +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/architecture/ModuleArchitectureTest.java @@ -0,0 +1,59 @@ +package com.alibaba.qwen.code.managedagent.architecture; + +import com.tngtech.archunit.core.domain.JavaClasses; +import com.tngtech.archunit.core.importer.ClassFileImporter; +import org.junit.jupiter.api.Test; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class ModuleArchitectureTest { + @Test + void compiledModulesRespectTheirBoundariesAndOwnTheirPackages() { + var modules = new LinkedHashMap(); + for (String module : List.of("contracts", "control-plane", "runtime-broker", "persistence", "adapters", "server")) { + var classes = new ClassFileImporter().importPath(Path.of("..", module, "target", "classes")); + assertThat(classes).as("compiled classes of %s", module).isNotEmpty(); + modules.put(module, classes); + Path declarations = Path.of("..", module, "target", "classes", "csi-native-file-declarations.json"); + assertThat(Files.exists(declarations)).as("native-tool declarations in %s", module) + .isEqualTo(module.equals("contracts")); + } + Map owners = new HashMap<>(); + Map packages = new HashMap<>(); + modules.forEach((module, classes) -> classes.forEach(type -> { + assertThat(owners.put(type.getName(), module)).as("duplicate class %s", type.getName()).isNull(); + String previous = packages.putIfAbsent(type.getPackageName(), module); + assertThat(previous == null || previous.equals(module)).as("split package %s: %s / %s", type.getPackageName(), previous, module).isTrue(); + })); + modules.forEach((module, classes) -> classes.forEach(type -> type.getDirectDependenciesFromSelf().forEach(dependency -> { + String target = dependency.getTargetClass().getName(); + String owner = owners.get(target); + Set permitted = module.equals("server") ? modules.keySet() : module.equals("contracts") ? Set.of("contracts") : Set.of(module, "contracts"); + assertThat(owner == null || permitted.contains(owner)).as("%s: %s", module, dependency.getDescription()).isTrue(); + if (!module.equals("server") && !module.equals("adapters")) { + assertThat(target).doesNotStartWith("com.alibaba.qwen.code.daemon.").doesNotStartWith("com.aliyun.oss.").doesNotStartWith("org.apache.rocketmq."); + } + if (module.equals("contracts") || module.equals("runtime-broker")) { + assertThat(target).doesNotStartWith("org.springframework."); + } + if (module.equals("contracts") || module.equals("control-plane") || module.equals("runtime-broker")) { + assertThat(target).doesNotStartWith("java.sql.").doesNotStartWith("javax.sql.").doesNotStartWith("org.springframework.jdbc."); + } + if (module.equals("contracts")) { + assertThat(target).doesNotStartWith("java.net.http."); + assertThat(target).isNotEqualTo("java.lang.ProcessBuilder"); + } + if (module.equals("server")) { + assertThat(target).doesNotStartWith("com.alibaba.qwen.code.daemon.").doesNotStartWith("com.aliyun.oss.").doesNotStartWith("org.apache.rocketmq."); + } + }))); + } +} diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/artifact/AliyunToolPublicationObjectStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/artifact/AliyunToolPublicationObjectStoreTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/artifact/AliyunToolPublicationObjectStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/artifact/AliyunToolPublicationObjectStoreTest.java index 2ddb407e49a..aa20c02cf38 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/artifact/AliyunToolPublicationObjectStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/artifact/AliyunToolPublicationObjectStoreTest.java @@ -1,5 +1,7 @@ package com.alibaba.qwen.code.managedagent.artifact; +import com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationStorageException; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSException; import com.aliyun.oss.model.AccessControlList; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactServiceTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactServiceTest.java index 57fbd26c6e0..5eb7e6fde15 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/artifact/ManagedArtifactServiceTest.java @@ -4,7 +4,6 @@ import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.http.HttpStatus; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -43,7 +42,7 @@ class ManagedArtifactServiceTest { for (long size : new long[] {0, 9}) { assertThatThrownBy(() -> ManagedArtifactService.select(range, size)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThat(error.getStatus()).isEqualTo(400); assertThat(error.getCode()).isEqualTo("invalid_range"); assertThat(error.getMessage()).isEqualTo("Invalid byte range."); }); @@ -55,7 +54,7 @@ class ManagedArtifactServiceTest { void refusesEveryCommaBeforeParsingRanges(String range) { assertThatThrownBy(() -> ManagedArtifactService.select(range, 0)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThat(error.getStatus()).isEqualTo(400); assertThat(error.getCode()).isEqualTo("unsupported_range"); }); } @@ -68,7 +67,7 @@ class ManagedArtifactServiceTest { assertThatThrownBy(() -> ManagedArtifactService.select(range, 2_000_000)) .as(range).isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThat(error.getStatus()).isEqualTo(400); String code = range.contains(",") ? "unsupported_range" @@ -90,7 +89,7 @@ class ManagedArtifactServiceTest { assertThatThrownBy(() -> ManagedArtifactService.select(range, 0)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE); + assertThat(error.getStatus()).isEqualTo(416); assertThat(error.getCode()).isEqualTo("range_not_satisfiable"); }); } @@ -98,7 +97,7 @@ class ManagedArtifactServiceTest { assertThatThrownBy(() -> ManagedArtifactService.select(range, 9)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE); + assertThat(error.getStatus()).isEqualTo(416); assertThat(error.getCode()).isEqualTo("range_not_satisfiable"); }); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerConcurrencyMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerConcurrencyMySqlIT.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerConcurrencyMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerConcurrencyMySqlIT.java index 7b2dd1e6fc5..018f7a5b3d3 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerConcurrencyMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerConcurrencyMySqlIT.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.managedagent.automation; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.OccurrenceRow; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.ScheduleRow; import com.alibaba.qwen.code.managedagent.persistence.IsolatedManagedSchemaMySqlTestDatabase; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.OccurrenceRow; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.ScheduleRow; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerTest.java index f561e5399a3..047908ffe8b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/automation/AutomationScannerTest.java @@ -3,25 +3,24 @@ package com.alibaba.qwen.code.managedagent.automation; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; -import com.alibaba.qwen.code.daemon.DaemonException; import com.alibaba.qwen.code.managedagent.AutomationHarnessFake; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessFailure; import com.alibaba.qwen.code.managedagent.model.ApiModels.AutomationDefinitionRequest; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicAutomation; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicAutomationRun; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicList; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.OccurrenceView; -import com.alibaba.qwen.code.managedagent.persistence.AutomationLedgerStore.ScheduleRow; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.OccurrenceView; +import com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.ScheduleRow; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.NullNode; @@ -270,7 +269,7 @@ class AutomationScannerTest { // lost; the gate is revised before the re-drive, which replays the // committed run: that delivery was the old command's. PublicAutomation automation = defineGated("./probe.sh"); - fake.failAfterNextFireCommit = new DaemonException("connection lost"); + fake.failAfterNextFireCommit = new HarnessFailure("connection lost"); clock.set(T0 + MINUTE + 1_000); assertThat(scanner.tick(clock.get())).isEqualTo(0); service.update(tenant, ACTOR, automation.id(), @@ -640,15 +639,14 @@ class AutomationScannerTest { "manual-1").body(); assertThat(first.outcome()).isEqualTo("fired"); assertThat(first.occurrenceKey()).isEqualTo("manual:manual-1"); - assertThat(first.id()).isEqualTo(AutomationLedgerStore - .automationRunId(automation.id(), "manual:manual-1")); + assertThat(first.id()).isEqualTo(com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationRunId(automation.id(), "manual:manual-1")); assertThat(service.run(tenant, ACTOR, automation.id(), "manual-1") .replayed()).isTrue(); assertThat(fake.fires()).isEqualTo(1); assertThatThrownBy(() -> service.run(tenant, ACTOR, automation.id(), "manual-2")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()).isEqualTo("automation_run_skipped"); }); // The first run settles in the Session store: its projection row @@ -660,7 +658,7 @@ class AutomationScannerTest { + " task_kind, task_state, gmt_create)" + " VALUES (?, ?, ?, ?, ?, 'automation_run', ?, ?, 3," + " 'resource', 'automation_run', 'completed', ?)", - ManagedSessionStore.sessionScopeKey(tenant, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId), ManagedExtensionProjection.recordKey(sessionId, "automation_run", first.id()), tenant, WORKSPACE, sessionId, first.id(), "0".repeat(64), clock.get()); @@ -712,10 +710,10 @@ class AutomationScannerTest { new AutomationDefinitionRequest(null, "Reader", null, null, null, null, null, null, null, null))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> - assertThat(error.getStatus()).isEqualTo(HttpStatus.FORBIDDEN)); + assertThat(error.getStatus()).isEqualTo(403)); assertThatThrownBy(() -> service.get(tenant, "stranger", id)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> - assertThat(error.getStatus()).isEqualTo(HttpStatus.NOT_FOUND)); + assertThat(error.getStatus()).isEqualTo(404)); var retired = service.retire(tenant, ACTOR, id, "retire-1"); assertThat(retired.body().state()).isEqualTo("retired"); assertThat(service.retire(tenant, ACTOR, id, "retire-2").replayed()) @@ -872,7 +870,7 @@ class AutomationScannerTest { true); String id = automation.id(); String key = "schedule:2026-06-01T10:01:00Z"; - fake.failNextFire = new DaemonException("answer lost"); + fake.failNextFire = new HarnessFailure("answer lost"); clock.set(T0 + MINUTE + 1_000); assertThat(scanner.tick(clock.get())).isEqualTo(0); AutomationLedgerStore.OccurrenceRow claim = ledger.findOccurrence( @@ -888,7 +886,7 @@ class AutomationScannerTest { // Every later attempt fails too; the delay caps below five minutes. for (int attempt = 2; attempt < AutomationScanner.MAX_FIRE_ATTEMPTS; attempt++) { - fake.failNextFire = new DaemonException("answer lost " + attempt); + fake.failNextFire = new HarnessFailure("answer lost " + attempt); clock.addAndGet(301_000); assertThat(scanner.tick(clock.get())).isEqualTo(0); AutomationLedgerStore.OccurrenceRow again = ledger.findOccurrence( @@ -900,7 +898,7 @@ class AutomationScannerTest { .isBetween(2_000L, 300_000L); } assertThat(ledger.countActive(tenant, id)).isEqualTo(1); - fake.failNextFire = new DaemonException("answer lost, last"); + fake.failNextFire = new HarnessFailure("answer lost, last"); clock.addAndGet(301_000); assertThat(scanner.tick(clock.get())).isEqualTo(0); AutomationLedgerStore.OccurrenceRow given = ledger.findOccurrence( @@ -925,7 +923,7 @@ class AutomationScannerTest { PublicAutomation automation = define("* * * * *", "skip", "none", null, true); String id = automation.id(); - fake.failNextFire = new DaemonException("answer lost"); + fake.failNextFire = new HarnessFailure("answer lost"); clock.set(T0 + MINUTE + 1_000); assertThat(scanner.tick(clock.get())).isEqualTo(0); assertThat(outcomes(id)) @@ -1040,7 +1038,7 @@ class AutomationScannerTest { assertThatThrownBy(() -> service.create(tenant, READER, "shared-key", request)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()).isEqualTo("idempotency_conflict"); }); assertThat(service.create(tenant, ACTOR, "shared-key", request) @@ -1060,11 +1058,11 @@ class AutomationScannerTest { keyA); // The Harness commits revision 1, but the answer never arrives: // the pre-relay claim is the only ledger trace, and no mirror. - fake.failAfterNextDefineCommit = new DaemonException("connection lost"); + fake.failAfterNextDefineCommit = new HarnessFailure("connection lost"); assertThatThrownBy(() -> service.create(tenant, ACTOR, keyA, requestA)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value()); assertThat(error.getCode()) .isEqualTo("automation_operation_unknown"); }); @@ -1115,7 +1113,7 @@ class AutomationScannerTest { PublicAutomation created = define("0 2 * * *", "skip", "none", null, true); String keyR = "lost-" + UUID.randomUUID(); - fake.failAfterNextRetireCommit = new DaemonException("connection lost"); + fake.failAfterNextRetireCommit = new HarnessFailure("connection lost"); assertThatThrownBy( () -> service.retire(tenant, ACTOR, created.id(), keyR)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> @@ -1160,7 +1158,7 @@ class AutomationScannerTest { AutomationDefinitionRequest requestU = new AutomationDefinitionRequest( null, null, "0 2 * * *", null, null, null, null, null, null, null); - fake.failAfterNextDefineCommit = new DaemonException("connection lost"); + fake.failAfterNextDefineCommit = new HarnessFailure("connection lost"); assertThatThrownBy( () -> service.update(tenant, ACTOR, automationId, keyU, requestU)) @@ -1204,7 +1202,7 @@ class AutomationScannerTest { PublicAutomation second = define("15 3 * * *", "skip", "none", null, true); String key = "lost-" + UUID.randomUUID(); - fake.failAfterNextRetireCommit = new DaemonException("connection lost"); + fake.failAfterNextRetireCommit = new HarnessFailure("connection lost"); assertThatThrownBy( () -> service.retire(tenant, ACTOR, first.id(), key)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> @@ -1216,7 +1214,7 @@ class AutomationScannerTest { assertThatThrownBy( () -> service.retire(tenant, ACTOR, second.id(), key)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("idempotency_conflict"); }); @@ -1304,7 +1302,7 @@ class AutomationScannerTest { assertThat(fence).isPositive(); assertThatThrownBy(() -> service.run(tenant, ACTOR, id, "busy-1")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()).isEqualTo("automation_busy"); }); assertThat(fake.fires()).isZero(); @@ -1326,11 +1324,11 @@ class AutomationScannerTest { PublicAutomation automation = define("0 2 * * *", "allow", "none", null, true); String id = automation.id(); - fake.failNextFire = new DaemonException("connection lost"); + fake.failNextFire = new HarnessFailure("connection lost"); assertThatThrownBy(() -> service.run(tenant, ACTOR, id, "lost-1")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value()); assertThat(error.getCode()) .isEqualTo("automation_operation_unknown"); }); @@ -1348,7 +1346,7 @@ class AutomationScannerTest { assertThat(asked.body().id()).isEqualTo(claim.runId()); assertThat(fake.fires()).isEqualTo(2); // A claim nobody asks about again is re-driven by the scanner. - fake.failNextFire = new DaemonException("connection lost"); + fake.failNextFire = new HarnessFailure("connection lost"); assertThatThrownBy(() -> service.run(tenant, ACTOR, id, "lost-2")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()) @@ -1394,11 +1392,11 @@ class AutomationScannerTest { ledger.release(tenant, id, "seed", seedFence, clock.get()); // One more fire would take attempts to MAX_FIRE_ATTEMPTS and settle // the claim unknown; the guard answers 503 without reaching it. - fake.failNextFire = new DaemonException("must not fire"); + fake.failNextFire = new HarnessFailure("must not fire"); assertThatThrownBy(() -> service.run(tenant, ACTOR, id, "seeded")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value()); assertThat(error.getCode()) .isEqualTo("automation_operation_unknown"); }); @@ -1418,11 +1416,11 @@ class AutomationScannerTest { seedDeferred(id, "manual:workspace", 2, "fire failed with HTTP 409: {\"error\":\"workspace_busy\"," + "\"code\":\"workspace_busy\"}"); - fake.failNextFire = new DaemonException("must not fire"); + fake.failNextFire = new HarnessFailure("must not fire"); assertThatThrownBy(() -> service.run(tenant, ACTOR, id, "workspace")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value()); assertThat(error.getCode()).isEqualTo("workspace_busy"); }); assertThat(fake.fires()).isZero(); @@ -1436,10 +1434,10 @@ class AutomationScannerTest { seedDeferred(id, "manual:blocked", 2, "fire failed with HTTP 409: {\"error\":\"hosted_session_blocked\"," + "\"code\":\"hosted_session_blocked\"}"); - fake.failNextFire = new DaemonException("must not fire"); + fake.failNextFire = new HarnessFailure("must not fire"); assertThatThrownBy(() -> service.run(tenant, ACTOR, id, "blocked")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("hosted_session_blocked"); }); @@ -1589,7 +1587,7 @@ class AutomationScannerTest { // loads the Session under overlap skip: the next decision's // reconcile settles the crashed run — and the very slot that // asked fires instead of being recorded skipped forever. - fake.crashedRuns.add(AutomationLedgerStore.automationRunId( + fake.crashedRuns.add(com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationRunId( automation.id(), "schedule:2026-06-01T10:01:00Z")); clock.set(T0 + 3 * MINUTE + 1_000); assertThat(scanner.tick(clock.get())).isEqualTo(1); @@ -1607,7 +1605,7 @@ class AutomationScannerTest { String id = automation.id(); String wedged = "schedule:2026-06-01T10:01:00Z"; String liveKey = "schedule:2026-06-01T10:02:00Z"; - String wedgedRunId = AutomationLedgerStore.automationRunId(id, wedged); + String wedgedRunId = com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationRunId(id, wedged); // A second, genuinely live run beside the wedged one (a manual // run, fired and never settled: the mirror has no terminal row). AutomationLedgerStore.ScheduleRow row = ledger.findSchedule(tenant, id) @@ -1636,7 +1634,7 @@ class AutomationScannerTest { tenantId, sessionId, body); if (!applied && "reconcile_run".equals(body.get("kind"))) { applied = true; - String scope = ManagedSessionStore.sessionScopeKey(tenant, + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId); byte[] bytes = "{}".getBytes(StandardCharsets.UTF_8); jdbc.update("INSERT INTO ma_resource" @@ -1648,8 +1646,8 @@ class AutomationScannerTest { + " VALUES (?, ?, ?, ?, ?, 'wedged-run-rev-3'," + " 'managed-automation-run', 1, ?, ?," + " 'MYSQL_INLINE', ?, 'publish', 'REFERENCED', ?)", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(scope, "wedged-run-rev-3"), scope, tenant, WORKSPACE, sessionId, bytes.length, - AutomationLedgerStore.sha256( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scope, "wedged-run-rev-3"), scope, tenant, WORKSPACE, sessionId, bytes.length, + com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.sha256( new String(bytes, StandardCharsets.UTF_8)), bytes, new java.sql.Timestamp(clock.get())); jdbc.update( @@ -1694,7 +1692,7 @@ class AutomationScannerTest { // into unknown nor fired on a guess. fake.failNextReconcile = AutomationHarnessFake.refusal(503, "automation_operation_failed"); - fake.crashedRuns.add(AutomationLedgerStore.automationRunId( + fake.crashedRuns.add(com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationRunId( automation.id(), "schedule:2026-06-01T10:01:00Z")); clock.set(T0 + 2 * MINUTE + 1_000); assertThat(scanner.tick(clock.get())).isEqualTo(0); @@ -1706,7 +1704,7 @@ class AutomationScannerTest { assertThat(skipped.reason()).isEqualTo(AutomationScanner.REASON_OVERLAP); // The re-drive repairs the run the fault swallowed, and clears the // slot that asked. - fake.crashedRuns.add(AutomationLedgerStore.automationRunId( + fake.crashedRuns.add(com.alibaba.qwen.code.managedagent.repository.AutomationLedgerStore.automationRunId( automation.id(), "schedule:2026-06-01T10:01:00Z")); clock.set(T0 + 3 * MINUTE + 1_000); assertThat(scanner.tick(clock.get())).isEqualTo(1); @@ -1812,7 +1810,7 @@ class AutomationScannerTest { assertThatThrownBy( () -> service.retire(tenant, ACTOR, automation.id(), key)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("automation_operation_conflict"); }); @@ -1845,7 +1843,7 @@ class AutomationScannerTest { "15 3 * * *", "UTC", "Run that.", null, "skip", "none", null, true))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("automation_operation_conflict"); }); @@ -1867,7 +1865,7 @@ class AutomationScannerTest { null, null, null); // The post-commit answer is lost; the request claim and definition // projection are durable while the response receipt is unsettled. - fake.failAfterNextDefineCommit = new DaemonException("connection lost"); + fake.failAfterNextDefineCommit = new HarnessFailure("connection lost"); assertThatThrownBy(() -> service.create(tenant, ACTOR, keyA, requestA)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()) @@ -1888,7 +1886,7 @@ class AutomationScannerTest { "15 3 * * *", "UTC", "Run that.", null, null, null, null, null))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("idempotency_conflict"); }); @@ -1918,7 +1916,7 @@ class AutomationScannerTest { "0 2 * * *", "UTC", "Run it.", null, null, null, null, null))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("idempotency_conflict"); }); @@ -1957,7 +1955,7 @@ class AutomationScannerTest { "UTC", "Run it.", null, "skip", "none", null, true, TextNode.valueOf("sleep 5 &")))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThat(error.getStatus()).isEqualTo(400); assertThat(error.getCode()).isEqualTo("automation_gate_invalid"); }); assertThat(ledger.findCommand(tenant, key)).isEmpty(); @@ -1982,7 +1980,7 @@ class AutomationScannerTest { .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.CONFLICT); + .isEqualTo(HttpStatus.CONFLICT.value()); assertThat(error.getCode()).isEqualTo( "automation_gate_unsupported"); }); @@ -2007,7 +2005,7 @@ class AutomationScannerTest { "0 2 * * *", "UTC", "Run it.", "per_run", null, null, null, true))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("automation_mode_disabled"); }); @@ -2040,7 +2038,7 @@ class AutomationScannerTest { assertThatThrownBy(() -> service.create(tenant, ACTOR, key, requestA)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.BAD_REQUEST); + .isEqualTo(HttpStatus.BAD_REQUEST.value()); assertThat(error.getCode()) .isEqualTo("invalid_automation_operation"); }); @@ -2059,7 +2057,7 @@ class AutomationScannerTest { "15 3 * * *", "UTC", "Run that.", null, null, null, null, null))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("idempotency_conflict"); }); @@ -2089,7 +2087,7 @@ class AutomationScannerTest { "hosted_session_already_attached"); assertThatThrownBy(() -> service.create(tenant, ACTOR, key, requestA)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("hosted_session_already_attached"); }); @@ -2107,7 +2105,7 @@ class AutomationScannerTest { "15 3 * * *", "UTC", "Run that.", null, null, null, null, null))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("idempotency_conflict"); }); @@ -2151,7 +2149,7 @@ class AutomationScannerTest { "15 3 * * *", "UTC", "Run that.", null, null, null, null, null))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("idempotency_conflict"); }); @@ -2176,7 +2174,7 @@ class AutomationScannerTest { assertThatThrownBy( () -> service.run(tenant, ACTOR, automation.id(), "manual-def")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("automation_run_skipped"); assertThat(error.getMessage()) @@ -2213,7 +2211,7 @@ class AutomationScannerTest { () -> service.update(tenant, ACTOR, automation.id(), key, moved)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("idempotency_conflict"); }); @@ -2228,7 +2226,7 @@ class AutomationScannerTest { "key-" + UUID.randomUUID(), unicode)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.BAD_REQUEST); + .isEqualTo(HttpStatus.BAD_REQUEST.value()); assertThat(error.getCode()) .isEqualTo("invalid_automation"); }); @@ -2254,7 +2252,7 @@ class AutomationScannerTest { assertThatThrownBy(() -> service.create(tenant, ACTOR, "key-" + UUID.randomUUID(), body)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("automation_unavailable"); }); @@ -2266,7 +2264,7 @@ class AutomationScannerTest { assertThatThrownBy(() -> offStore.create(tenant, ACTOR, "key-" + UUID.randomUUID(), body)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("automation_unavailable"); }); @@ -2276,7 +2274,7 @@ class AutomationScannerTest { void aFireReDrivenAfterTheDefinitionRetiredAnswersTheCommittedRun() { PublicAutomation automation = define("* * * * *", "allow", "none", null, true); - fake.failAfterNextFireCommit = new DaemonException("connection lost"); + fake.failAfterNextFireCommit = new HarnessFailure("connection lost"); clock.set(T0 + MINUTE + 1_000); assertThat(scanner.tick(clock.get())).isEqualTo(0); // The claim committed at the Harness, its answer lost; the diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/automation/CronSlotsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/automation/CronSlotsTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/automation/CronSlotsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/automation/CronSlotsTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationServiceTest.java similarity index 83% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationServiceTest.java index 4788f97353b..2026337184b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/automation/ManagedAutomationServiceTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.automation; -import com.alibaba.qwen.code.daemon.DaemonHttpException; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -25,7 +25,7 @@ class ManagedAutomationServiceTest { ManagedAgentException conflict = ManagedAutomationService .translate(refusal(409, code)); assertThat(conflict.getStatus()) - .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value()); assertThat(conflict.getCode()).isEqualTo(code); } @@ -34,7 +34,7 @@ class ManagedAutomationServiceTest { void keepsADeterministicHarnessConflictAsConflict() { ManagedAgentException conflict = ManagedAutomationService.translate( refusal(409, "hosted_automations_unavailable")); - assertThat(conflict.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(conflict.getStatus()).isEqualTo(409); assertThat(conflict.getCode()) .isEqualTo("hosted_automations_unavailable"); } @@ -45,23 +45,23 @@ class ManagedAutomationServiceTest { void keepsHostedSessionBlockedAsConflict() { ManagedAgentException conflict = ManagedAutomationService.translate( refusal(409, "hosted_session_blocked")); - assertThat(conflict.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(conflict.getStatus()).isEqualTo(409); assertThat(conflict.getCode()).isEqualTo("hosted_session_blocked"); } // A 409 whose body names no code is not a self-healing conflict. @Test void keepsACodeLessHarnessConflictAsConflict() { - DaemonHttpException error = mock(DaemonHttpException.class); + HarnessHttpFailure error = mock(HarnessHttpFailure.class); when(error.getStatusCode()).thenReturn(409); when(error.getResponseBody()).thenReturn(null); ManagedAgentException conflict = ManagedAutomationService.translate(error); - assertThat(conflict.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(conflict.getStatus()).isEqualTo(409); assertThat(conflict.getCode()).isEqualTo("automation_operation_failed"); } - private static DaemonHttpException refusal(int status, String code) { - DaemonHttpException error = mock(DaemonHttpException.class); + private static HarnessHttpFailure refusal(int status, String code) { + HarnessHttpFailure error = mock(HarnessHttpFailure.class); when(error.getStatusCode()).thenReturn(status); when(error.getResponseBody()).thenReturn( "{\"error\":\"" + code + "\",\"code\":\"" + code + "\"}"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionServiceTest.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionServiceTest.java index 54ccff0d83a..161ac1f7768 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedAgentDefinitionServiceTest.java @@ -5,14 +5,13 @@ import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.AgentDefinitionRequest; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore.Admission; -import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore.ConcurrentWriteException; -import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore.DefinitionRevision; +import com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore.Admission; +import com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore.ConcurrentWriteException; +import com.alibaba.qwen.code.managedagent.repository.ManagedAgentDefinitionStore.DefinitionRevision; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.http.HttpStatus; import java.util.List; import java.util.Map; @@ -95,7 +94,7 @@ class ManagedAgentDefinitionServiceTest { assertThatThrownBy(() -> service.update("tenant", AGENT_ID, "key", REQUEST)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("agent_revision_conflict"); }); @@ -115,7 +114,7 @@ class ManagedAgentDefinitionServiceTest { "The Idempotency-Key was used by a concurrent request.")); } when(store.replayCommitted("tenant", "key", update ? UPDATE_DIGEST : CREATE_DIGEST)) - .thenThrow(new ManagedAgentException(HttpStatus.CONFLICT, + .thenThrow(new ManagedAgentException(409, "idempotency_conflict", "different request")); assertThatThrownBy(() -> { @@ -126,7 +125,7 @@ class ManagedAgentDefinitionServiceTest { } }) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("idempotency_conflict"); }); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogServiceTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogServiceTest.java index 83d0055ac2c..a9252d16e65 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedHookCatalogServiceTest.java @@ -6,7 +6,6 @@ import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; import java.util.Optional; @@ -56,7 +55,7 @@ class ManagedHookCatalogServiceTest { @Test void authorizesBeforeReadingRecordsOrResources() { when(sessions.requireReadableSession("tenant", "actor", "session")) - .thenThrow(new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", "The Session was not found.")); + .thenThrow(new ManagedAgentException(404, "session_not_found", "The Session was not found.")); assertThatThrownBy(() -> service.get("tenant", "actor", "session")).isInstanceOf(ManagedAgentException.class); verifyNoInteractions(records); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogServiceTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogServiceTest.java index 35606d6c80c..b5fd0bffc3a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpCatalogServiceTest.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; import java.util.List; @@ -87,7 +86,7 @@ class ManagedMcpCatalogServiceTest { @Test void authorizesBeforeReadingAnyRecordsOrResources() { when(sessions.requireReadableSession("tenant", "actor", "session")) - .thenThrow(new ManagedAgentException(HttpStatus.NOT_FOUND, "session_not_found", "The Session was not found.")); + .thenThrow(new ManagedAgentException(404, "session_not_found", "The Session was not found.")); assertThatThrownBy(() -> service.get("tenant", "actor", "session")).isInstanceOf(ManagedAgentException.class); verifyNoInteractions(records); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementServiceTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementServiceTest.java index 576c6eb0872..f9d1e74a9bf 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/catalog/ManagedMcpManagementServiceTest.java @@ -1,18 +1,17 @@ package com.alibaba.qwen.code.managedagent.catalog; -import com.alibaba.qwen.code.daemon.DaemonHttpException; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; import com.alibaba.qwen.code.managedagent.model.ApiModels.ManagedMcpManagementCommand; import com.alibaba.qwen.code.managedagent.model.ApiModels.ManagedMcpServerPin; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; -import org.springframework.http.HttpStatus; import java.util.List; import java.util.Map; @@ -76,7 +75,7 @@ class ManagedMcpManagementServiceTest { @Test void operatorAdmissionPrecedesInventoryAndAStatusReadNeverSubmits() { when(store.requireMcpManagement("tenant", "reader", "session", null)).thenThrow( - new ManagedAgentException(HttpStatus.FORBIDDEN, "session_operation_forbidden", "Forbidden")); + new ManagedAgentException(403, "session_operation_forbidden", "Forbidden")); assertThatThrownBy(() -> service.get("tenant", "reader", "session")).isInstanceOf(ManagedAgentException.class); verify(harness, never()).mcpManagement(anyString(), anyString()); String operation = "11111111-1111-4111-8111-111111111111"; @@ -87,13 +86,13 @@ class ManagedMcpManagementServiceTest { @Test void reportsTheRequiredExplicitLoadWithoutRetryingTheOriginalOperation() { - var refusal = mock(DaemonHttpException.class); + var refusal = mock(HarnessHttpFailure.class); when(refusal.getStatusCode()).thenReturn(409); when(refusal.getErrorCode()).thenReturn("hosted_mcp_session_load_required"); when(harness.mcpManagement("tenant", "session")).thenThrow(refusal); assertThatThrownBy(() -> service.get("tenant", "actor", "session")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()).isEqualTo("hosted_mcp_session_load_required"); assertThat(error.getMessage()).isEqualTo("Continue or recover the Session before managing MCP servers."); }); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelServiceTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelServiceTest.java index 3b96d43f7ba..a363905bed0 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/channel/ManagedChannelServiceTest.java @@ -1,22 +1,22 @@ package com.alibaba.qwen.code.managedagent.channel; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ChannelPolicy; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ClaimResponse; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ClaimedDelivery; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.DeliveryView; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.InboundAdmission; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.InboundAttachment; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.InboundEventRequest; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ReceiptRequest; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.RegisterChannelRequest; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.ResendResponse; -import com.alibaba.qwen.code.managedagent.channel.ChannelAdapterModels.RouteScope; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ChannelPolicy; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ClaimResponse; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ClaimedDelivery; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.DeliveryView; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.InboundAdmission; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.InboundAttachment; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.InboundEventRequest; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ReceiptRequest; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.RegisterChannelRequest; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.ResendResponse; +import com.alibaba.qwen.code.managedagent.contracts.channel.ChannelAdapterModels.RouteScope; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicChannel; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicChannelDelivery; import com.alibaba.qwen.code.managedagent.model.ApiModels.PublicList; -import com.alibaba.qwen.code.managedagent.persistence.ChannelDeliveryRepository; import com.alibaba.qwen.code.managedagent.persistence.ChannelInstanceStore; import com.alibaba.qwen.code.managedagent.persistence.JdbcChannelDeliveryRepository; import com.alibaba.qwen.code.managedagent.persistence.JdbcChannelRouteRepository; @@ -426,9 +426,9 @@ class ManagedChannelServiceTest { // A deterministic input refusal is not a server failure: widening // the hop's 400 to 503 would hide it from the adapter's // permanent-refusal branch, so the classification must survive. - com.alibaba.qwen.code.daemon.DaemonHttpException refused = + com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure refused = org.mockito.Mockito.mock( - com.alibaba.qwen.code.daemon.DaemonHttpException.class); + com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure.class); org.mockito.Mockito.when(refused.getStatusCode()).thenReturn(400); org.mockito.Mockito.when(refused.getResponseBody()) .thenReturn("{\"error\":{\"code\":\"invalid_channel_operation\"}}"); @@ -437,8 +437,7 @@ class ManagedChannelServiceTest { event(1, "1700:98", "hello"))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(org.springframework.http.HttpStatus - .BAD_REQUEST); + .isEqualTo(400); assertThat(error.getCode()) .isEqualTo("invalid_channel_operation"); }); @@ -463,8 +462,7 @@ class ManagedChannelServiceTest { event(1, "1700:97", "hello"))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(org.springframework.http.HttpStatus - .SERVICE_UNAVAILABLE); + .isEqualTo(503); assertThat(error.getCode()) .isEqualTo("managed_session_writer_conflict"); }); @@ -472,8 +470,7 @@ class ManagedChannelServiceTest { event(1, "1700:97", "hello"))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(org.springframework.http.HttpStatus - .CONFLICT); + .isEqualTo(409); assertThat(error.getCode()) .isEqualTo("channel_operation_conflict"); }); @@ -501,8 +498,7 @@ class ManagedChannelServiceTest { assertThatThrownBy(() -> service.register(TENANT, channel, moved)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(org.springframework.http.HttpStatus - .CONFLICT); + .isEqualTo(409); assertThat(error.getCode()) .isEqualTo("channel_ownership_conflict"); }); @@ -693,11 +689,11 @@ class ManagedChannelServiceTest { .extracting(row -> row.state()).isEqualTo("unknown"); } - private static com.alibaba.qwen.code.daemon.DaemonHttpException harnessError( + private static com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure harnessError( int status, String body) { - com.alibaba.qwen.code.daemon.DaemonHttpException error = + com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure error = org.mockito.Mockito.mock( - com.alibaba.qwen.code.daemon.DaemonHttpException.class); + com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure.class); org.mockito.Mockito.when(error.getStatusCode()).thenReturn(status); org.mockito.Mockito.when(error.getResponseBody()).thenReturn(body); return error; @@ -713,8 +709,7 @@ class ManagedChannelServiceTest { event(1, "1700:99", "hello"))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(org.springframework.http.HttpStatus - .SERVICE_UNAVAILABLE); + .isEqualTo(503); assertThat(error.getCode()) .isEqualTo("channel_operation_failed"); }); @@ -908,7 +903,7 @@ class ManagedChannelServiceTest { """.formatted(deliveryId, routeId, "a".repeat(64), deliveryId, "b".repeat(64), deliveryId, deliveryId); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create) VALUES (?, ?, ?, ?, ?, ?, 'managed-channel_delivery', 1, ?, ?, 'MYSQL_INLINE', ?, ?, 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ")", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(scopeKey(sessionId), resourceId), scopeKey(sessionId), TENANT, WORKSPACE, sessionId, resourceId, + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scopeKey(sessionId), resourceId), scopeKey(sessionId), TENANT, WORKSPACE, sessionId, resourceId, body.length(), "c".repeat(64), body.getBytes(StandardCharsets.UTF_8), "publish-" + resourceId); jdbc.update("INSERT INTO ma_session_record" @@ -920,7 +915,7 @@ class ManagedChannelServiceTest { + " VALUES (?, ?, ?, ?, ?, 'channel_delivery', ?, ?," + " 1, ?, NULL, NULL, 'channel', 'planned', ?)", scopeKey(sessionId), - com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection + com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection .recordKey(sessionId, "channel_delivery", deliveryId), TENANT, WORKSPACE, sessionId, deliveryId, "d".repeat(64), resourceId, clock.get()); @@ -968,11 +963,11 @@ class ManagedChannelServiceTest { } } - private List routes() { + private List routes() { return new JdbcChannelRouteRepository(jdbc).listByChannel(TENANT, channel, null, 100).routes(); } - private com.alibaba.qwen.code.managedagent.persistence.ChannelRouteRepository.ChannelRoute route(String eventId) { + private com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelRouteRepository.ChannelRoute route(String eventId) { return routes().stream().filter(route -> eventId.equals(route.platformEventId())).findFirst().orElseThrow(); } @@ -985,7 +980,7 @@ class ManagedChannelServiceTest { } private static String scopeKey(String sessionId) { - return com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey(TENANT, sessionId); + return com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, sessionId); } /** Answers the channel operations the way the hosted funnel does. */ @@ -1007,7 +1002,7 @@ class ManagedChannelServiceTest { boolean loseClaimAnswerOnce; boolean loseReceiptAnswerOnce; boolean throwDaemonOnceOnSubmit; - java.util.Queue + java.util.Queue submitErrors = new java.util.ArrayDeque<>(); RecordingHarness(Projection projection) { @@ -1067,7 +1062,7 @@ class ManagedChannelServiceTest { } if (throwDaemonOnceOnSubmit) { throwDaemonOnceOnSubmit = false; - throw new com.alibaba.qwen.code.daemon.DaemonException( + throw new com.alibaba.qwen.code.managedagent.harness.contracts.HarnessFailure( "generation changed"); } String inputId = String.valueOf(body.get("inputId")); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/BrokerSecurityTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/BrokerSecurityTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/BrokerSecurityTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/BrokerSecurityTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfigurationTest.java similarity index 90% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfigurationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfigurationTest.java index 327e0f69b65..548dda3ca65 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfigurationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfigurationTest.java @@ -1,13 +1,13 @@ package com.alibaba.qwen.code.managedagent.config; -import com.alibaba.qwen.code.managedagent.event.EventTransport; +import com.alibaba.qwen.code.managedagent.adapters.event.LiteTopicEventTransport; +import com.alibaba.qwen.code.managedagent.adapters.event.LiteTopicPublicEventTransport; +import com.alibaba.qwen.code.managedagent.contracts.event.EventTransport; +import com.alibaba.qwen.code.managedagent.contracts.event.JournalEventTransport; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.event.EventTransportConsumer; -import com.alibaba.qwen.code.managedagent.event.JournalEventTransport; -import com.alibaba.qwen.code.managedagent.event.LiteTopicEventTransport; -import com.alibaba.qwen.code.managedagent.event.LiteTopicPublicEventTransport; import com.alibaba.qwen.code.managedagent.event.MessageMaterializer; import com.alibaba.qwen.code.managedagent.event.TransportWakeActivator; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.rocketmq.client.apis.ClientException; @@ -99,7 +99,7 @@ class EventTransportConfigurationTest { "qwen.managed-agent.event-transport.topic=" + TOPIC) // Bound from the property values, as in a deployment. .withUserConfiguration(PropertiesConfiguration.class) - .withBean(ClientServiceProvider.class, () -> provider) + .withBean(com.alibaba.qwen.code.managedagent.adapters.event.RocketMqEventClients.class, () -> new com.alibaba.qwen.code.managedagent.adapters.event.RocketMqEventClients(provider)) .withBean("managedJdbcTemplate", JdbcTemplate.class, () -> mock(JdbcTemplate.class)) .withBean("managedAgentClock", Clock.class, Clock::systemUTC) .withBean("managedAgentObjectMapper", ObjectMapper.class, ObjectMapper::new) @@ -119,8 +119,8 @@ class EventTransportConfigurationTest { + flag); context.run(started -> { assertThat(started).hasNotFailed(); - assertThat(started).doesNotHaveBean(Producer.class); - assertThat(started).doesNotHaveBean(LitePushConsumer.class); + assertThat(started).doesNotHaveBean(com.alibaba.qwen.code.managedagent.adapters.event.RocketMqEventClients.Publisher.class); + assertThat(started).doesNotHaveBean(com.alibaba.qwen.code.managedagent.contracts.event.EventSubscriptions.class); assertThat(started).doesNotHaveBean(EventTransport.class); assertThat(started).doesNotHaveBean( JournalEventTransport.class); @@ -136,7 +136,7 @@ class EventTransportConfigurationTest { void aFlagOnDeploymentWiresBothWiresAndBothLanes() throws Exception { enabled().run(started -> { assertThat(started).hasNotFailed(); - assertThat(started).hasSingleBean(Producer.class); + assertThat(started).hasSingleBean(com.alibaba.qwen.code.managedagent.adapters.event.RocketMqEventClients.Publisher.class); assertThat(started).hasSingleBean(LiteTopicEventTransport.class); assertThat(started).getBean(JournalEventTransport.class) .isSameAs(started.getBean(LiteTopicEventTransport.class)); @@ -145,7 +145,7 @@ class EventTransportConfigurationTest { LiteTopicPublicEventTransport.class)); assertThat(started).hasSingleBean(EventTransportConsumer.class); assertThat(started).hasSingleBean(TransportWakeActivator.class); - assertThat(started.getBeansOfType(LitePushConsumer.class)) + assertThat(started.getBeansOfType(com.alibaba.qwen.code.managedagent.contracts.event.EventSubscriptions.class)) .containsOnlyKeys("eventTransportOwnerConsumer", "eventTransportWakeBootstrapConsumer"); }); diff --git a/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/HarnessConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/HarnessConfigurationTest.java new file mode 100644 index 00000000000..a7b8ad8a013 --- /dev/null +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/HarnessConfigurationTest.java @@ -0,0 +1,114 @@ +package com.alibaba.qwen.code.managedagent.config; + +import com.alibaba.qwen.code.daemon.CreateHarnessSession; +import com.alibaba.qwen.code.daemon.HarnessSessionRef; +import com.alibaba.qwen.code.daemon.HostedHarnessCapabilities; +import com.alibaba.qwen.code.daemon.HostedHarnessClient; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.ToolResultResourceRef; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionLineage; +import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; +import com.alibaba.qwen.code.managedagent.persistence.HarnessPlacementStore; +import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore; +import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore; +import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; +import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionSkills; +import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; +import com.alibaba.qwen.code.managedagent.persistence.SshWorkspaceStore; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; +import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.RETURNS_SELF; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class HarnessConfigurationTest { + @Test + void standaloneConnectorPublishesSkillsAndInheritedExtensions() throws Exception { + String sessionId = "33333333-3333-4333-8333-333333333333"; + String parentId = "22222222-2222-4222-8222-222222222222"; + String bootId = "11111111-1111-4111-8111-111111111111"; + var properties = new ManagedAgentProperties(); + properties.getHarness().setToken("test-harness-token"); + properties.getHarness().setCapabilityDigest("sha256:" + "a".repeat(64)); + properties.getHarness().setWorkspaceFilesEnabled(true); + properties.getSessionStore().setEnabled(true); + properties.getSessionStore().setBaseUrl("https://store.example"); + properties.getSessionStore().setWorkspaceId("workspace"); + var sessions = mock(AgentStateStore.class); + when(sessions.requireSession("tenant", sessionId)).thenReturn(new SessionRecord( + "tenant", sessionId, "qwen-code", null, null, "ACTIVE", null, null, + 0, 0, 0, 1, 1, null, 1, new ContextBinding("tenant", "workspace", 1, + "storage", ".", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1), + "default", "hosted-workspace-shell/1", null, 1)); + when(sessions.findChildLineage("tenant", sessionId)) + .thenReturn(new SessionLineage(parentId, parentId, "child-run", 1)); + var actions = mock(ManagedActionStore.class); + when(actions.approvalMode("tenant", sessionId)).thenReturn("default"); + var skills = mock(ManagedSessionSkills.class); + when(skills.snapshot("tenant", sessionId)).thenReturn(List.of()); + var extensions = mock(ManagedExtensionRecordStore.class); + var resources = mock(ManagedSessionStore.class); + var json = new ObjectMapper(); + var run = json.readTree("{\"inputRef\":{\"resourceId\":\"launch\"}}"); + var launch = json.readTree(""" + {"publicLaunch":{"version":2, + "modelRoute":{"authType":"api-key","endpointDigest":"endpoint","providerPin":"provider"}, + "extensionAgent":{"bundleRef":{"resourceId":"bundle"}}}} + """); + var extensionAgent = launch.path("publicLaunch").path("extensionAgent"); + var bundleRef = extensionAgent.path("bundleRef"); + var bundle = mock(StoredResource.class); + when(extensions.findRecord("tenant", parentId, "child_run", "child-run")).thenReturn(Optional.of(run)); + when(extensions.readRecordResource("tenant", parentId, run.path("inputRef"))).thenReturn(launch); + when(extensions.readRecordResource("tenant", parentId, bundleRef)).thenReturn(json.readTree("{\"packages\":[]}")); + when(extensions.readCommittedResource("tenant", parentId, bundleRef)).thenReturn(bundle); + when(resources.publishInheritedExtensionResource("tenant", "workspace", sessionId, bundle)) + .thenReturn(new ToolResultResourceRef("copied-bundle", "extension-bundle", 1, 2, "digest")); + var client = mock(HostedHarnessClient.class); + var capabilities = mock(HostedHarnessCapabilities.class); + when(client.capabilities()).thenReturn(capabilities); + when(capabilities.getBootId()).thenReturn(bootId); + when(capabilities.getFeatures()).thenReturn(List.of("public-child-agents/1", "approval-selection/1", "session-skills/2")); + when(capabilities.getLifecycleV2Profiles()).thenReturn(List.of("hosted-workspace-shell/1")); + var attached = mock(HarnessSessionRef.class); + when(attached.getHarnessBootId()).thenReturn(bootId); + when(attached.getApprovalMode()).thenReturn("default"); + when(client.createSession(any())).thenReturn(attached); + var builder = mock(HostedHarnessClient.Builder.class, RETURNS_SELF); + when(builder.build()).thenReturn(client); + try (var sdk = mockStatic(HostedHarnessClient.class, CALLS_REAL_METHODS)) { + sdk.when(HostedHarnessClient::builder).thenReturn(builder); + try (var connector = new HarnessConfiguration().hostedHarnessConnector(properties, + sessions, mock(WorkspaceExecutionStore.class), actions, mock(ManagedAgentDefinitionStore.class), + resources, mock(SshWorkspaceStore.class), mock(HarnessPlacementStore.class), skills, json, extensions)) { + assertThat(connector.createOrLoad("tenant", sessionId, false).bootId()).isEqualTo(bootId); + var request = ArgumentCaptor.forClass(CreateHarnessSession.class); + verify(client).createSession(request.capture()); + Map wire = ReflectionTestUtils.invokeMethod(request.getValue(), "toJson"); + assertThat(wire).containsEntry("sessionId", sessionId).containsKey("sessionSkillsRef"); + assertThat(wire.get("publicChildExtensionAgent")).isEqualTo(Map.of( + "extensionAgent", Map.of("bundleRef", Map.of("resourceId", "bundle")), + "effectiveConfigRef", Map.of("resourceId", "copied-bundle", "kind", "extension-bundle", + "schemaVersion", 1, "byteLength", 2L, "digest", "digest"))); + verify(resources).publishAgentSkills("tenant", "workspace", sessionId, List.of()); + verify(resources).publishInheritedExtensionResource("tenant", "workspace", sessionId, bundle); + } + } + } +} diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/HarnessCredentialsFileTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/HarnessCredentialsFileTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/HarnessCredentialsFileTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/HarnessCredentialsFileTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/InternalSurfaceConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/InternalSurfaceConfigurationTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/InternalSurfaceConfigurationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/InternalSurfaceConfigurationTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentEmbeddingTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentEmbeddingTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentEmbeddingTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentEmbeddingTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalEmbeddingTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalEmbeddingTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalEmbeddingTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalEmbeddingTest.java index 41c35820ac9..6fe466cb56b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalEmbeddingTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentInternalEmbeddingTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.config; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentPropertiesTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentPropertiesTest.java similarity index 89% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentPropertiesTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentPropertiesTest.java index 306b07ad324..1014108df0d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentPropertiesTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentPropertiesTest.java @@ -17,6 +17,8 @@ import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.Scheduled; +import java.time.Duration; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -25,13 +27,72 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; -class ManagedAgentPropertiesTest { +public class ManagedAgentPropertiesTest { private static final String DURABLE_KEY = "qwen.managed-agent.runtime-broker.durable-local-process"; private static final String TRUSTED_KEY = "qwen.managed-agent.runtime-broker.trusted-local-reboot-recovery"; @Test - void verifiedFileRuntimeCanResolvePersistedImportsWithNewImportsDisabled() { + void generatedAccessorsKeepNestedBindingAndInternalStatePrivate() { + new ApplicationContextRunner() + .withUserConfiguration(PropertiesConfiguration.class) + .withPropertyValues( + "qwen.managed-agent.service-user= worker ", + "qwen.managed-agent.model-catalog[0].model-id=model-one", + "qwen.managed-agent.model-catalog[0].label=Model One", + "qwen.managed-agent.harness.enabled=false", + "qwen.managed-agent.harness.load-timeout=12", + "qwen.managed-agent.automation.scan-delay=1200", + "qwen.managed-agent.runtime-broker.retention.batch-size=42", + "qwen.managed-agent.harness-credentials-applied=true", + "qwen.managed-agent.harness-broker-url=http://untrusted") + .run(started -> { + assertThat(started).hasNotFailed(); + var properties = started.getBean(ManagedAgentProperties.class); + assertThat(properties.getServiceUser()).isEqualTo("worker"); + assertThat(properties.getModelCatalog()).singleElement().satisfies(model -> { + assertThat(model.getModelId()).isEqualTo("model-one"); + assertThat(model.getLabel()).isEqualTo("Model One"); + }); + assertThat(properties.getHarness().getLoadTimeout()).isEqualTo(Duration.ofSeconds(12)); + assertThat(properties.getAutomation().getScanDelay()).isEqualTo(Duration.ofMillis(1200)); + assertThat(properties.getRuntimeBroker().getRetention().getBatchSize()).isEqualTo(42); + assertThat(properties.harnessCredentialsApplied()).isFalse(); + assertThat(properties.harnessBrokerUrl()).isEmpty(); + }); + } + + @Test + void customAccessorsKeepNormalizationCopiesAndSharedCollections() { + var properties = new ManagedAgentProperties(); + properties.setServiceUser(null); + assertThat(properties.getServiceUser()).isEmpty(); + properties.setServiceUser(" worker "); + assertThat(properties.getServiceUser()).isEqualTo("worker"); + properties.getInternalServer().setAddress(" "); + assertThat(properties.getInternalServer().getAddress()).isEqualTo("127.0.0.1"); + + var model = new ManagedAgentProperties.ModelOption(); + model.setModelId("model-one"); + var catalog = new ArrayList<>(List.of(model)); + properties.setModelCatalog(catalog); + catalog.clear(); + assertThat(properties.getModelCatalog()).containsExactly(model); + var actors = new ArrayList<>(List.of("actor")); + properties.getExtensions().setAllowedActors(actors); + actors.clear(); + assertThat(properties.getExtensions().getAllowedActors()).containsExactly("actor"); + + var mounts = new ArrayList(); + properties.getRuntimeBroker().setWorkspaceMounts(mounts); + assertThat(properties.getRuntimeBroker().getWorkspaceMounts()).isSameAs(mounts); + var nodes = new ArrayList(); + properties.getRuntimeBroker().getSsh().setNodes(nodes); + assertThat(properties.getRuntimeBroker().getSsh().getNodes()).isNotSameAs(nodes).isEmpty(); + } + + @Test + public void verifiedFileRuntimeCanResolvePersistedImportsWithNewImportsDisabled() { ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getHarness().setEnabled(true); properties.getHarness().setWorkspaceFilesEnabled(true); @@ -46,7 +107,7 @@ class ManagedAgentPropertiesTest { } @Test - void applicationYmlBindsTheDurableAndTrustedDefaults() throws java.io.IOException { + public void applicationYmlBindsTheDurableAndTrustedDefaults() throws java.io.IOException { var values = applicationYmlValues(); // The named contract itself: reverting either fallback flips the // binding below red; renaming or typoing either variable shows up here. @@ -69,7 +130,7 @@ class ManagedAgentPropertiesTest { } @Test - void theDocumentedEnvNamesOverrideTheYmlDefaults() throws java.io.IOException { + public void theDocumentedEnvNamesOverrideTheYmlDefaults() throws java.io.IOException { var values = applicationYmlValues(); var ambient = new java.util.LinkedHashMap(); System.getenv().forEach((name, value) -> { @@ -105,7 +166,7 @@ class ManagedAgentPropertiesTest { "qwen.managed-agent.runtime-broker.external-startup-timeout"; @Test - void applicationYmlBindsTheExternalRuntimeDefaults() throws java.io.IOException { + public void applicationYmlBindsTheExternalRuntimeDefaults() throws java.io.IOException { var values = applicationYmlValues(); // The named contract itself: renaming or typoing a variable shows up // here, and dropping the '2m' unit flips the startup bind to PT2S @@ -163,7 +224,7 @@ class ManagedAgentPropertiesTest { "Automation.lookback"); @Test - void everyDurationFieldDeclaresABindingUnit() { + public void everyDurationFieldDeclaresABindingUnit() { // A unit-less numeric override binds as milliseconds unless the // field declares its unit; this pin keeps the sweep complete for // future fields too. getDeclaredClasses() sees direct members only, @@ -213,7 +274,7 @@ class ManagedAgentPropertiesTest { } @Test - void unitLessNumericOverridesBindInTheDeclaredUnit() { + public void unitLessNumericOverridesBindInTheDeclaredUnit() { // Without @DurationUnit this binds PT0.12S — a 120 ms lease renewed // every 20 s invites double execution. The v3 result window is the // documented case: a stale milliseconds-style 1800000 meant as 30 @@ -254,7 +315,8 @@ class ManagedAgentPropertiesTest { @Test @ExtendWith(OutputCaptureExtension.class) - void aMillisecondScaleOverrideWarnsAtStartup(CapturedOutput output) { + public void + aMillisecondScaleOverrideWarnsAtStartup(CapturedOutput output) { // A bare 1800000 meant as 30 minutes in milliseconds binds PT500H // under the seconds convention — the stale-override signature the // startup warning names. @@ -335,8 +397,8 @@ class ManagedAgentPropertiesTest { @Test @ExtendWith(OutputCaptureExtension.class) - void theUnitSweepKeysOnTheWrittenShapeNotTheMagnitude( - CapturedOutput output) { + public void + theUnitSweepKeysOnTheWrittenShapeNotTheMagnitude(CapturedOutput output) { // 600000 binds PT166H40M — only ~333x the 30m default, inside no // magnitude band, so the written shape is the only signal that // names it stale. @@ -371,7 +433,8 @@ class ManagedAgentPropertiesTest { @Test @ExtendWith(OutputCaptureExtension.class) - void aZeroOverrideStaysOutsideTheSweep(CapturedOutput output) { + public void aZeroOverrideStaysOutsideTheSweep( + CapturedOutput output) { // PT0S is the documented per-chunk / per-event re-verification // setting; the sweep must never name it. new ApplicationContextRunner() @@ -400,7 +463,7 @@ class ManagedAgentPropertiesTest { } @Test - void materializeIntervalDrivesTheScheduledCadence() throws java.io.IOException { + public void materializeIntervalDrivesTheScheduledCadence() throws java.io.IOException { // The typed field is the cadence's only driving source: // ManagedArtifactConfiguration.messageMaterializerTask schedules // the pass with it on the dedicated single-thread scheduler, and a @@ -420,7 +483,7 @@ class ManagedAgentPropertiesTest { } @Test - void theProjectionIntervalTicksOnTheArtifactScheduler() { + public void theProjectionIntervalTicksOnTheArtifactScheduler() { // The README's projection-interval row publishes the :1000 fallback // as a 1s cadence; @Scheduled reads a bare number as timeUnit() — // milliseconds by default — so the placeholder, the scheduler @@ -440,7 +503,7 @@ class ManagedAgentPropertiesTest { } @Test - void theChildRelayScanDelayReadsBareNumbersAsMilliseconds() { + public void theChildRelayScanDelayReadsBareNumbersAsMilliseconds() { // The third @Scheduled placeholder cadence the README's unit // rule names: it has no typed field, so the annotation fallback // is the deployed default and the startup sweep can never see @@ -459,7 +522,7 @@ class ManagedAgentPropertiesTest { } @Test - void theWakeRecoveryScanUsesItsDedicatedSchedulerAndMillisecondCadence() { + public void theWakeRecoveryScanUsesItsDedicatedSchedulerAndMillisecondCadence() { assertThat(schedulesOn(WakeRecovery.class, "wake-recovery.scan-delay")) .hasSize(1).allSatisfy(scheduled -> { assertThat(scheduled.fixedDelayString()).isEqualTo( @@ -470,7 +533,7 @@ class ManagedAgentPropertiesTest { } @Test - void theFourScanDelaySchedulesShareOneFallback() throws java.io.IOException { + public void theFourScanDelaySchedulesShareOneFallback() throws java.io.IOException { // The Dispatch comment claims the four sites read the identical // "${...scan-delay:1s}" placeholder; only this pin keeps the claim // true when one fallback is retuned without the others. The unit @@ -503,7 +566,7 @@ class ManagedAgentPropertiesTest { } @Test - void theReadmeNamesEveryPlaceholderOnlyCadence() throws Exception { + public void theReadmeNamesEveryPlaceholderOnlyCadence() throws Exception { // The README's unit rule names the cadences that live only in // @Scheduled placeholders. A hand-maintained list goes stale the // next time the base adds one — it already falsified the sentence @@ -560,7 +623,7 @@ class ManagedAgentPropertiesTest { } @Test - void theReadmeNamesEveryMillisBoundSetting() throws Exception { + public void theReadmeNamesEveryMillisBoundSetting() throws Exception { // The unit rule's millisecond exceptions are only true while the // sentence names every MILLIS-bound field, so derive the expected // set from the properties graph instead of pinning names: dropping @@ -574,31 +637,35 @@ class ManagedAgentPropertiesTest { } private static List> moduleClasses() throws Exception { - // Every class the module compiles, loaded without initialization — - // a hand-maintained owner list is how the earlier cadence pins - // missed a base-side addition. - var root = java.nio.file.Path.of(ManagedAgentProperties.class - .getProtectionDomain().getCodeSource().getLocation() - .toURI()); - try (var paths = java.nio.file.Files.walk(root)) { - return paths - .filter(path -> path.toString().endsWith(".class")) - .map(root::relativize).map(java.nio.file.Path::toString) - .filter(name -> !name.equals("module-info.class")) - .map(name -> name - .substring(0, name.length() - ".class".length()) - .replace(java.io.File.separatorChar, '.')) - .>map(name -> { - try { - return Class.forName(name, false, - ManagedAgentProperties.class - .getClassLoader()); - } catch (ClassNotFoundException missing) { - throw new IllegalStateException(missing); - } - }) - .toList(); + var classes = new java.util.ArrayList>(); + for (Class marker : List.of(ManagedAgentProperties.class, + com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings.class, + com.alibaba.qwen.code.managedagent.session.ManagedAgentService.class, + com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService.class, + com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore.class, + com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules.class)) { + var root = java.nio.file.Path.of(marker.getProtectionDomain().getCodeSource().getLocation().toURI()); + List names; + if (java.nio.file.Files.isDirectory(root)) { + try (var paths = java.nio.file.Files.walk(root)) { + names = paths.map(root::relativize) + .map(path -> path.toString().replace(java.io.File.separatorChar, '/')) + .toList(); + } + } else { + try (var jar = new java.util.jar.JarFile(root.toFile())) { + names = jar.stream().map(java.util.jar.JarEntry::getName).toList(); + } + } + var classNames = names.stream().filter(name -> name.endsWith(".class")) + .filter(name -> !name.equals("module-info.class")).toList(); + assertThat(classNames).as("classes containing %s", marker).isNotEmpty(); + for (String name : classNames) { + classes.add(Class.forName(name.substring(0, name.length() - ".class".length()) + .replace('/', '.'), false, marker.getClassLoader())); + } } + return classes; } private static boolean hasTypedField(String key) { @@ -695,7 +762,7 @@ class ManagedAgentPropertiesTest { private static String readmeText() throws java.io.IOException { // Surefire runs from the module directory; an IDE run from the // repository root needs the module path spelled out. - var path = java.nio.file.Path.of("README.md"); + var path = java.nio.file.Path.of("..", "README.md"); if (!java.nio.file.Files.exists(path)) { path = java.nio.file.Path.of("packages", "managed-agent-server", "README.md"); } @@ -761,7 +828,7 @@ class ManagedAgentPropertiesTest { } @Test - void droppedConfigSurfacesStayDropped() { + public void droppedConfigSurfacesStayDropped() { // The kubernetes* and cliEntry blocks had no consumer; they come // back only together with their provisioner/invocation. The // Dispatch and Channels scanDelay fields are the other dropped @@ -782,7 +849,8 @@ class ManagedAgentPropertiesTest { } @Test - void sshCatalogBindsAndRetainsExistingRuntimeConfigurationWhenNewAdmissionIsDisabled() { + public void + sshCatalogBindsAndRetainsExistingRuntimeConfigurationWhenNewAdmissionIsDisabled() { var remote = new ApplicationContextRunner().withUserConfiguration(PropertiesConfiguration.class) .withPropertyValues("qwen.managed-agent.harness.enabled=true", "qwen.managed-agent.harness.workspace-files-enabled=true", @@ -829,7 +897,7 @@ class ManagedAgentPropertiesTest { } @Test - void validatesWorkspaceFilesWhenSpringInitializesTheProperties() { + public void validatesWorkspaceFilesWhenSpringInitializesTheProperties() { ApplicationContextRunner context = new ApplicationContextRunner() .withUserConfiguration(PropertiesConfiguration.class); context.run(started -> assertThat(started).hasNotFailed()); @@ -860,7 +928,7 @@ class ManagedAgentPropertiesTest { } @Test - void springBindsResidentMappingsAndRequiresTheExplicitResidentDeploymentShape() { + public void springBindsResidentMappingsAndRequiresTheExplicitResidentDeploymentShape() { String prefix = "qwen.managed-agent.runtime-broker."; String entry = prefix + "resident-workspaces[0]."; var values = new java.util.ArrayList(List.of( @@ -913,7 +981,7 @@ class ManagedAgentPropertiesTest { } @Test - void publicChildrenDefaultOnWithoutEnablingUnsupportedRuntimeProfiles() { + public void publicChildrenDefaultOnWithoutEnablingUnsupportedRuntimeProfiles() { var properties = new ManagedAgentProperties(); assertThat(properties.getHarness().isWorkspaceChildAgentsEnabled()).isTrue(); assertThatCode(properties::validateWorkspaceFiles).doesNotThrowAnyException(); @@ -934,7 +1002,7 @@ class ManagedAgentPropertiesTest { } @Test - void publicChildEnvironmentOverrideDisablesTheShippedDefault() throws Exception { + public void publicChildEnvironmentOverrideDisablesTheShippedDefault() throws Exception { String key = "qwen.managed-agent.harness.workspace-child-agents-enabled"; String variable = "QWEN_MANAGED_AGENT_WORKSPACE_CHILD_AGENTS_ENABLED"; var values = applicationYmlValues(); @@ -962,7 +1030,7 @@ class ManagedAgentPropertiesTest { } @Test - void shellAdmissionIsDefaultOffAndRequiresAnAskingModeAndFiles() throws Exception { + public void shellAdmissionIsDefaultOffAndRequiresAnAskingModeAndFiles() throws Exception { String key = "qwen.managed-agent.harness.workspace-shell-enabled"; assertThat(applicationYmlValues()).containsEntry(key, "${QWEN_MANAGED_AGENT_WORKSPACE_SHELL_ENABLED:false}"); @@ -1001,7 +1069,8 @@ class ManagedAgentPropertiesTest { } @Test - void boundSessionIsolationDefaultsToSessionAndWorkspaceRequiresADurableLocalBrokerWithoutChildren() { + public void + boundSessionIsolationDefaultsToSessionAndWorkspaceRequiresADurableLocalBrokerWithoutChildren() { ManagedAgentProperties plain = new ManagedAgentProperties(); assertThat(plain.getRuntimeBroker().getBoundSessionIsolationClass()).isEqualTo("session"); plain.validateWorkspaceFiles(); @@ -1035,7 +1104,7 @@ class ManagedAgentPropertiesTest { } @Test - void sessionCreateWarmDefaultsOffAndRequiresWorkspaceIsolationWithoutSshNodes() { + public void sessionCreateWarmDefaultsOffAndRequiresWorkspaceIsolationWithoutSshNodes() { ManagedAgentProperties plain = new ManagedAgentProperties(); assertThat(plain.getRuntimeBroker().isSessionCreateWarm()).isFalse(); @@ -1059,12 +1128,12 @@ class ManagedAgentPropertiesTest { } @Configuration(proxyBeanMethods = false) - @EnableConfigurationProperties(ManagedAgentProperties.class) - static class PropertiesConfiguration { - } + @EnableConfigurationProperties( + ManagedAgentProperties.class) + public static class PropertiesConfiguration {} @Test - void relaxationDefaultsMatchTheShippedConfiguration() throws Exception { + public void relaxationDefaultsMatchTheShippedConfiguration() throws Exception { ManagedAgentProperties properties = new ManagedAgentProperties(); assertThat(properties.getEvents().getReadGrantRecheckInterval()) .isEqualTo(java.time.Duration.ofSeconds(5)); @@ -1113,7 +1182,7 @@ class ManagedAgentPropertiesTest { } @Test - void fileAdmissionRequiresTheCompleteTrustedLocalDeployment() { + public void fileAdmissionRequiresTheCompleteTrustedLocalDeployment() { assertThatCode(() -> new ManagedAgentProperties().validateWorkspaceFiles()).doesNotThrowAnyException(); List> invalid = List.of( p -> p.getHarness().setEnabled(false), @@ -1144,7 +1213,7 @@ class ManagedAgentPropertiesTest { } @Test - void automationTunablesBindFromTheShippedConfiguration() throws Exception { + public void automationTunablesBindFromTheShippedConfiguration() throws Exception { var yaml = new org.springframework.boot.env.YamlPropertySourceLoader() .load("application.yml", new org.springframework.core.io.ClassPathResource( @@ -1204,7 +1273,7 @@ class ManagedAgentPropertiesTest { } @Test - void automationTunablesRefuseNonPositiveValuesAtStartup() { + public void automationTunablesRefuseNonPositiveValuesAtStartup() { List> invalid = List.of( p -> p.getAutomation().setMaxSlotsPerTick(0), p -> p.getAutomation().setConcurrency(0), @@ -1234,7 +1303,7 @@ class ManagedAgentPropertiesTest { // #13753 I3: snapshots are read-only child Workspaces, so their own // switch needs the capability's. @Test - void childSnapshotsAreOffByDefaultAndNeedChildWorkspaces() { + public void childSnapshotsAreOffByDefaultAndNeedChildWorkspaces() { assertThat(new ManagedAgentProperties().getRuntimeBroker().isChildSnapshotsEnabled()).isFalse(); ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getRuntimeBroker().setChildSnapshotsEnabled(true); @@ -1252,7 +1321,7 @@ class ManagedAgentPropertiesTest { } @Test - void childWorkspacesAreOffByDefaultAndNeedTheMountingBroker() { + public void childWorkspacesAreOffByDefaultAndNeedTheMountingBroker() { assertThat(new ManagedAgentProperties().getRuntimeBroker().isChildWorkspacesEnabled()).isFalse(); assertThat(new ManagedAgentProperties().getRuntimeBroker().getChildWorkspaceGitTimeout()) .isEqualTo(java.time.Duration.ofMinutes(2)); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedArtifactConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedArtifactConfigurationTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedArtifactConfigurationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedArtifactConfigurationTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedLocalConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedLocalConfigurationTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedLocalConfigurationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedLocalConfigurationTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSchemaConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSchemaConfigurationTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSchemaConfigurationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSchemaConfigurationTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfigurationTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfigurationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfigurationTest.java index 081a94eb64a..24bf82d1ff4 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfigurationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionJsonConfigurationTest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.config; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.fasterxml.jackson.core.exc.StreamConstraintsException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionReaderVersionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionReaderVersionTest.java similarity index 80% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionReaderVersionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionReaderVersionTest.java index a568fdd31be..87dd915abd8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionReaderVersionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ManagedSessionReaderVersionTest.java @@ -1,11 +1,10 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.config; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.http.HttpStatus; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -35,28 +34,28 @@ class ManagedSessionReaderVersionTest { request.setRequestURI("/app" + path); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); - assertDoesNotThrow(() -> ManagedSessionReaderVersion.requireReadable(1)); + assertDoesNotThrow(() -> new ManagedSessionReaderVersion().requireReadable(1)); assertUnsupported(); request.addHeader(ManagedSessionReaderVersion.HEADER, "1"); assertUnsupported(); request.removeHeader(ManagedSessionReaderVersion.HEADER); request.addHeader(ManagedSessionReaderVersion.HEADER, "2"); - assertDoesNotThrow(() -> ManagedSessionReaderVersion.requireReadable(2)); + assertDoesNotThrow(() -> new ManagedSessionReaderVersion().requireReadable(2)); } @Test void publicAndInProcessReadersRetainBothFormats() { - assertDoesNotThrow(() -> ManagedSessionReaderVersion.requireReadable(2)); + assertDoesNotThrow(() -> new ManagedSessionReaderVersion().requireReadable(2)); var request = new MockHttpServletRequest(); request.setRequestURI("/v1/agents/agent/sessions/session"); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); - assertDoesNotThrow(() -> ManagedSessionReaderVersion.requireReadable(2)); + assertDoesNotThrow(() -> new ManagedSessionReaderVersion().requireReadable(2)); } private static void assertUnsupported() { var failure = assertThrows(ManagedAgentException.class, - () -> ManagedSessionReaderVersion.requireReadable(2)); - assertEquals(HttpStatus.CONFLICT, failure.getStatus()); + () -> new ManagedSessionReaderVersion().requireReadable(2)); + assertEquals(409, failure.getStatus()); assertEquals("managed_session_reader_version_unsupported", failure.getCode()); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfigurationTest.java similarity index 89% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfigurationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfigurationTest.java index 13ab46470e9..bc758362cb1 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfigurationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeBrokerConfigurationTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.config; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.context.annotation.Bean; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfigurationTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfigurationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfigurationTest.java index d4d6d0c7b10..923223d6190 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfigurationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/RuntimeRetentionConfigurationTest.java @@ -1,13 +1,13 @@ package com.alibaba.qwen.code.managedagent.config; import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeRetentionJob; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/SessionStoreDefaultsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/SessionStoreDefaultsTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/SessionStoreDefaultsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/SessionStoreDefaultsTest.java index 825f01fc894..acc46dbbce4 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/SessionStoreDefaultsTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/SessionStoreDefaultsTest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.config; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; import org.apache.commons.codec.digest.DigestUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationStorageConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationStorageConfigurationTest.java similarity index 88% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationStorageConfigurationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationStorageConfigurationTest.java index 68aad8a07e3..c906d1849ba 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationStorageConfigurationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/ToolPublicationStorageConfigurationTest.java @@ -1,11 +1,15 @@ package com.alibaba.qwen.code.managedagent.config; -import com.alibaba.qwen.code.managedagent.artifact.AliyunToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient; +import com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.StorageReadPolicy; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; +import com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRules; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationCollector; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; import com.aliyun.oss.OSS; @@ -42,6 +46,8 @@ class ToolPublicationStorageConfigurationTest { .withBean("managedJdbcTemplate", JdbcTemplate.class, () -> mock(JdbcTemplate.class)) .withBean("managedAgentTransactionManager", PlatformTransactionManager.class, () -> mock(PlatformTransactionManager.class)) .withBean(ManagedSessionStore.class, () -> mock(ManagedSessionStore.class)) + .withBean(HarnessRules.class, SdkHarnessRules::new) + .withBean(StorageReadPolicy.class, ManagedSessionReaderVersion::new) .withBean(WriterCredentialPolicy.class, WriterCredentialPolicy::unbound) .withBean(ToolExecutionRepository.class, () -> mock(ToolExecutionRepository.class)) .withBean(RuntimeBindingRepository.class, () -> mock(RuntimeBindingRepository.class)) @@ -110,6 +116,7 @@ class ToolPublicationStorageConfigurationTest { @Test void theDefaultAdapterOwnsAndClosesItsOssClient() { var client = mock(OSS.class); + var managedClient = new AliyunObjectStoreClient(client); var acl = new AccessControlList(); acl.setCannedACL(CannedAccessControlList.Private); when(client.getBucketAcl("private-test-bucket")).thenReturn(acl); @@ -117,11 +124,12 @@ class ToolPublicationStorageConfigurationTest { .withBean("unrelatedOss", OSS.class, () -> mock(OSS.class)) .withBean(BeanFactoryPostProcessor.class, () -> beanFactory -> ((AbstractBeanDefinition) beanFactory.getBeanDefinition("toolPublicationOss")) - .setInstanceSupplier(() -> client)) + .setInstanceSupplier(() -> managedClient)) .run(context -> { assertThat(context).hasNotFailed().hasSingleBean(ToolPublicationObjectStore.class); - assertThat(context.getBeansOfType(OSS.class)).hasSize(2); - assertThat(context.getBean("toolPublicationOss", OSS.class)).isSameAs(client); + assertThat(context.getBeansOfType(OSS.class)).containsOnlyKeys("unrelatedOss"); + assertThat(context.getBean("toolPublicationOss", AliyunObjectStoreClient.class)) + .isSameAs(managedClient); assertThat(context.getBean(ToolPublicationObjectStore.class)) .isInstanceOf(AliyunToolPublicationObjectStore.class); }); diff --git a/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/runtime/EmbeddedBrokerFixture.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/runtime/EmbeddedBrokerFixture.java new file mode 100644 index 00000000000..414b88538b1 --- /dev/null +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/runtime/EmbeddedBrokerFixture.java @@ -0,0 +1,17 @@ +package com.alibaba.qwen.code.managedagent.config.runtime; +public final class EmbeddedBrokerFixture { + private EmbeddedBrokerFixture() { } + public static EmbeddedRuntimeBroker create(com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore store, + com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties properties, + com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository bindings, + com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository sessions, + com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository executions, + com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore workspaces, + com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore publications, + com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore data, + org.springframework.jdbc.core.JdbcTemplate jdbc, + org.springframework.transaction.PlatformTransactionManager manager, + com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner provisioner) { + return new EmbeddedRuntimeBroker(store, properties, bindings, sessions, executions, workspaces, publications, data, jdbc, manager, provisioner); + } +} diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ExternalWorkspaceFactoryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/runtime/ExternalWorkspaceFactoryTest.java similarity index 58% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ExternalWorkspaceFactoryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/runtime/ExternalWorkspaceFactoryTest.java index dfa6f4fff3e..8b5684e2a0f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ExternalWorkspaceFactoryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/config/runtime/ExternalWorkspaceFactoryTest.java @@ -1,11 +1,9 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.config.runtime; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalStorageRegistry; -import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalWorkspaceProvisioner; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.spi.ExternalStorageRegistry; import org.junit.jupiter.api.Test; import java.time.Duration; @@ -38,10 +36,10 @@ class ExternalWorkspaceFactoryTest { @Test void theWorkspaceKindGetsTheStorageBoundProvisioner() { try (RuntimeProvisioner provisioner = EmbeddedRuntimeBroker.provisioner( - broker(ExternalWorkspaceProvisioner.KIND), new HttpRuntimeTransport(), EMPTY)) { - assertEquals(ExternalWorkspaceProvisioner.KIND, provisioner.kind()); - assertTrue(provisioner.supportsKind(ExternalWorkspaceProvisioner.KIND)); - assertFalse(provisioner.supportsKind(ExternalRuntimeProvisioner.KIND)); + broker(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE), new HttpRuntimeTransport(), EMPTY)) { + assertEquals(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE, provisioner.kind()); + assertTrue(provisioner.supportsKind(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE)); + assertFalse(provisioner.supportsKind(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH)); assertFalse(provisioner.supportsDrainedStop()); } } @@ -49,16 +47,16 @@ class ExternalWorkspaceFactoryTest { @Test void theWorkspaceKindRefusesToStartWithoutItsRegistry() { var error = assertThrows(IllegalStateException.class, () -> EmbeddedRuntimeBroker - .provisioner(broker(ExternalWorkspaceProvisioner.KIND), new HttpRuntimeTransport(), null)); + .provisioner(broker(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE), new HttpRuntimeTransport(), null)); assertTrue(error.getMessage().contains("Managed Session Store"), error.getMessage()); } @Test void theScratchKindKeepsItsOwnProvisioner() { try (RuntimeProvisioner provisioner = EmbeddedRuntimeBroker.provisioner( - broker(ExternalRuntimeProvisioner.KIND), new HttpRuntimeTransport(), EMPTY)) { - assertEquals(ExternalRuntimeProvisioner.KIND, provisioner.kind()); - assertFalse(provisioner.supportsKind(ExternalWorkspaceProvisioner.KIND)); + broker(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH), new HttpRuntimeTransport(), EMPTY)) { + assertEquals(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH, provisioner.kind()); + assertFalse(provisioner.supportsKind(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE)); } } @@ -66,7 +64,7 @@ class ExternalWorkspaceFactoryTest { void anUnknownKindStillFailsClosedAndNamesBothKinds() { var error = assertThrows(IllegalStateException.class, () -> EmbeddedRuntimeBroker .provisioner(broker("external-workspace-typo"), new HttpRuntimeTransport(), EMPTY)); - assertTrue(error.getMessage().contains(ExternalRuntimeProvisioner.KIND), error.getMessage()); - assertTrue(error.getMessage().contains(ExternalWorkspaceProvisioner.KIND), error.getMessage()); + assertTrue(error.getMessage().contains(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH), error.getMessage()); + assertTrue(error.getMessage().contains(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE), error.getMessage()); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/AgentDefinitionExtensionsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/catalog/AgentDefinitionExtensionsTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/AgentDefinitionExtensionsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/catalog/AgentDefinitionExtensionsTest.java index 131c76cf959..ce00e7808c4 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/catalog/AgentDefinitionExtensionsTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/catalog/AgentDefinitionExtensionsTest.java @@ -1,7 +1,7 @@ -package com.alibaba.qwen.code.managedagent.catalog; +package com.alibaba.qwen.code.managedagent.contracts.catalog; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -174,7 +174,7 @@ class AgentDefinitionExtensionsTest { private static void assertRefused(String field, Runnable operation) { assertThatThrownBy(operation::run).isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus().value()).isEqualTo(409); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()).isEqualTo("agent_definition_unsupported"); assertThat(error.getDetails()).containsEntry("field", field); }); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/ManagedEventEnvelopesTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/event/ManagedEventEnvelopesTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/ManagedEventEnvelopesTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/event/ManagedEventEnvelopesTest.java index 27480f07125..44a11ad85e9 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/ManagedEventEnvelopesTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/event/ManagedEventEnvelopesTest.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.event; +package com.alibaba.qwen.code.managedagent.contracts.event; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentDefinitionCompilerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/AgentDefinitionCompilerTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentDefinitionCompilerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/AgentDefinitionCompilerTest.java index 642ec25bc39..8dd5bb65785 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentDefinitionCompilerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/AgentDefinitionCompilerTest.java @@ -1,7 +1,7 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; import org.junit.jupiter.api.Test; import java.util.List; @@ -131,7 +131,7 @@ class AgentDefinitionCompilerTest { assertThatThrownBy(() -> AgentDefinitionCompiler.compile(json, false, "yolo", FILES_ONLY)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus().value()).isEqualTo(409); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()) .isEqualTo("agent_definition_unsupported"); assertThat(error.getDetails().get("field")) diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/EventIdentityTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/EventIdentityTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/EventIdentityTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/EventIdentityTest.java index c254e1aafa9..8110dc18746 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/EventIdentityTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/EventIdentityTest.java @@ -1,6 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; -import com.alibaba.qwen.code.managedagent.persistence.EventIdentity.Identity; +import com.alibaba.qwen.code.managedagent.contracts.persistence.EventIdentity.Identity; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ExternalStorageRegistrationMySqlIT.java similarity index 61% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ExternalStorageRegistrationMySqlIT.java index 577a4fb341f..2a16f809d74 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ExternalStorageRegistrationMySqlIT.java @@ -1,5 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; - +package com.alibaba.qwen.code.managedagent.contracts.persistence; +import com.alibaba.qwen.code.managedagent.persistence.ExternalStorageRegistrationTest; +import com.alibaba.qwen.code.managedagent.persistence.IsolatedManagedSchemaMySqlTestDatabase; import org.junit.jupiter.api.AfterEach; import javax.sql.DataSource; @@ -8,7 +9,7 @@ class ExternalStorageRegistrationMySqlIT extends ExternalStorageRegistrationTest private IsolatedManagedSchemaMySqlTestDatabase mysql; @Override - DataSource source() { + public DataSource source() { mysql = new IsolatedManagedSchemaMySqlTestDatabase(); return mysql.source(); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHistoryMaintenanceRecordsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHistoryMaintenanceRecordsTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHistoryMaintenanceRecordsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHistoryMaintenanceRecordsTest.java index 870e0916d2d..73f394a0ba5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHistoryMaintenanceRecordsTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHistoryMaintenanceRecordsTest.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHostedRecoveryRecordsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHostedRecoveryRecordsTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHostedRecoveryRecordsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHostedRecoveryRecordsTest.java index c58f505da7e..3730d6aba24 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedHostedRecoveryRecordsTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/persistence/ManagedHostedRecoveryRecordsTest.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.contracts.persistence; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/security/WriterCredentialPolicyTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/security/WriterCredentialPolicyTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/security/WriterCredentialPolicyTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/security/WriterCredentialPolicyTest.java index c70e1539475..74097ca8906 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/security/WriterCredentialPolicyTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/security/WriterCredentialPolicyTest.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.security; +package com.alibaba.qwen.code.managedagent.contracts.security; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; @@ -79,7 +79,7 @@ class WriterCredentialPolicyTest { assertThatThrownBy(() -> policy.require("tenant", "workspace", "session", "self-minted-token-self-minted-token-0")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus().value()).isEqualTo(403); + assertThat(error.getStatus()).isEqualTo(403); assertThat(error.getCode()) .isEqualTo("writer_credential_invalid"); }); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceSelectionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorkspaceSelectionTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceSelectionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorkspaceSelectionTest.java index 743ced2a939..8c5380034a8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/WorkspaceSelectionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/contracts/workspace/WorkspaceSelectionTest.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.workspace; +package com.alibaba.qwen.code.managedagent.contracts.workspace; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.fasterxml.jackson.databind.JsonNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisherTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisherTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisherTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisherTest.java index c3fde712a94..c619ed37439 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisherTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/CompositeCommittedEventPublisherTest.java @@ -2,6 +2,8 @@ package com.alibaba.qwen.code.managedagent.event; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; +import com.alibaba.qwen.code.managedagent.contracts.event.CommittedEventPublisher; +import com.alibaba.qwen.code.managedagent.contracts.event.EventTransport; import com.alibaba.qwen.code.managedagent.model.StoreModels.EventRecord; import com.alibaba.qwen.code.managedagent.session.SessionEventHub; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerSqlTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerSqlTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerSqlTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerSqlTest.java index eed79b197a2..2f59d7b1b76 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerSqlTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerSqlTest.java @@ -1,12 +1,15 @@ package com.alibaba.qwen.code.managedagent.event; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelope; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelopes; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.event.EventTransportConsumer.MaterializeOutcome; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import org.apache.rocketmq.client.apis.message.MessageId; import org.apache.rocketmq.client.apis.message.MessageView; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -64,7 +67,7 @@ public class EventTransportConsumerSqlTest { private EventTransportConsumer consumer; @BeforeEach - void setUp() { + public void setUp() { harness = mock(HarnessConnector.class); when(harness.isAvailable()).thenReturn(true); when(harness.activateForWake(anyString(), anyString())) @@ -80,7 +83,7 @@ public class EventTransportConsumerSqlTest { } @Test - void aRedeliveredOrOlderWakeIsANoOpAndANewerOneActivatesAgain() { + public void aRedeliveredOrOlderWakeIsANoOpAndANewerOneActivatesAgain() { String session = session("ACTIVE"); head(session, "SEALED", 5, -60); @@ -100,7 +103,8 @@ public class EventTransportConsumerSqlTest { @ParameterizedTest @ValueSource(booleans = {false, true}) - void aPrivateWakeCannotInsertOrAdvanceProgressOrActivate(boolean requestPinned) { + public void + aPrivateWakeCannotInsertOrAdvanceProgressOrActivate(boolean requestPinned) { String session = session("ACTIVE"); if (requestPinned) { jdbc.update( @@ -167,7 +171,7 @@ public class EventTransportConsumerSqlTest { } @Test - void aStaleWakeForAFencedSessionIsANoOp() { + public void aStaleWakeForAFencedSessionIsANoOp() { // A live writer: the owner's own wake pump runs the input, and the // notice claims nothing, so the wake stays claimable once the lease // lapses. @@ -206,7 +210,7 @@ public class EventTransportConsumerSqlTest { // Sealing leaves the lease column set, but a SEALED journal is // re-acquirable at once: it holds no writer, so its wake is attempted. @Test - void aSealedJournalsLeftoverLeaseIsNoWriter() { + public void aSealedJournalsLeftoverLeaseIsNoWriter() { String sealed = session("ACTIVE"); head(sealed, "SEALED", 5, 60); @@ -217,7 +221,7 @@ public class EventTransportConsumerSqlTest { } @Test - void aDuplicateClaimIsRefusedOnTheFirstAndLaterUpdate() { + public void aDuplicateClaimIsRefusedOnTheFirstAndLaterUpdate() { String session = session("ACTIVE"); TransportConsumerStore store = new TransportConsumerStore(jdbc); @@ -229,7 +233,7 @@ public class EventTransportConsumerSqlTest { } @Test - void concurrentWakeClaimsHaveOneWinner() throws Exception { + public void concurrentWakeClaimsHaveOneWinner() throws Exception { String session = session("ACTIVE"); TransportConsumerStore store = new TransportConsumerStore(jdbc); var ready = new java.util.concurrent.CountDownLatch(8); @@ -260,7 +264,7 @@ public class EventTransportConsumerSqlTest { } @Test - void wakeAndMessageProgressAdvanceIndependently() { + public void wakeAndMessageProgressAdvanceIndependently() { String session = session("ACTIVE"); event(session, 1, "session.updated"); event(session, 2, "session.updated"); @@ -280,7 +284,7 @@ public class EventTransportConsumerSqlTest { } @Test - void controlAndRetiredSessionsCannotSupplyPublicConsumerState() { + public void controlAndRetiredSessionsCannotSupplyPublicConsumerState() { TransportConsumerStore store = new TransportConsumerStore(jdbc); String control = session("ACTIVE"); head(control, "ACTIVE", 1, 60); @@ -298,7 +302,7 @@ public class EventTransportConsumerSqlTest { } @Test - void anEnvelopeNamesTheBindingOrElseTheJournalsWorkspace() { + public void anEnvelopeNamesTheBindingOrElseTheJournalsWorkspace() { TransportConsumerStore store = new TransportConsumerStore(jdbc); String bound = session("ACTIVE"); head(bound, "ACTIVE", 1, 60, "workspace-journal"); @@ -318,7 +322,7 @@ public class EventTransportConsumerSqlTest { } @Test - void theHeldSetIsTheJournalsThisWriterHoldsUnderALiveLease() { + public void theHeldSetIsTheJournalsThisWriterHoldsUnderALiveLease() { TransportConsumerStore store = new TransportConsumerStore(jdbc); String held = session("ACTIVE"); head(held, "ACTIVE", 1, 60); @@ -340,7 +344,7 @@ public class EventTransportConsumerSqlTest { } @Test - void aPublicEventMaterializesAndItsRedeliveryIsANoOp() { + public void aPublicEventMaterializesAndItsRedeliveryIsANoOp() { String session = session("ACTIVE"); event(session, 1, "session.updated"); event(session, 2, "session.updated"); @@ -363,10 +367,13 @@ public class EventTransportConsumerSqlTest { private void deliver(ManagedEventEnvelope envelope) { MessageView message = mock(MessageView.class); + MessageId messageId = mock(MessageId.class); + when(messageId.toString()).thenReturn("test-message"); + when(message.getMessageId()).thenReturn(messageId); when(message.getBody()).thenReturn(ByteBuffer.wrap( ManagedEventEnvelopes.toJson(envelope) .getBytes(StandardCharsets.UTF_8))); - consumer.consume(message); + com.alibaba.qwen.code.managedagent.adapters.event.RocketMqMessages.consume(consumer, message); } private static ManagedEventEnvelope wake(String session, String workspace, @@ -422,8 +429,8 @@ public class EventTransportConsumerSqlTest { : "(" + ManagedSchemaTime.SQL_NOW + " + " + leaseOffsetSeconds * 1000L + ")") + ", 3, ?, 0, 0, 'READY', " + ManagedSchemaTime.SQL_NOW + ", " + ManagedSchemaTime.SQL_NOW + ", ?, ?, ?, 'MANAGED_SESSION')", TENANT, workspace, session, state, committed, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(TENANT), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(session), session); + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(TENANT), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(session), session); } private void event(String session, long sequence, String type) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerTest.java similarity index 87% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerTest.java index 7995e3f3b65..e19f5cfad3a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportConsumerTest.java @@ -1,9 +1,12 @@ package com.alibaba.qwen.code.managedagent.event; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelope; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelopes; import com.alibaba.qwen.code.managedagent.event.EventTransportConsumer.MaterializeOutcome; import com.alibaba.qwen.code.managedagent.event.TransportWakeActivator.Outcome; import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore; import org.apache.rocketmq.client.apis.consumer.ConsumeResult; +import org.apache.rocketmq.client.apis.message.MessageId; import org.apache.rocketmq.client.apis.message.MessageView; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -43,6 +46,9 @@ class EventTransportConsumerTest { private static MessageView message(String body) { MessageView view = mock(MessageView.class); + MessageId messageId = mock(MessageId.class); + when(messageId.toString()).thenReturn("test-message"); + when(view.getMessageId()).thenReturn(messageId); // A read-only view: the listener must not depend on a fresh buffer. when(view.getBody()).thenReturn(ByteBuffer.wrap( body.getBytes(StandardCharsets.UTF_8)).asReadOnlyBuffer()); @@ -69,7 +75,7 @@ class EventTransportConsumerTest { "wake.requested", 3); when(wakes.onWake(wake)).thenReturn(Outcome.SCHEDULED); - assertEquals(ConsumeResult.SUCCESS, consumer.consume(message(wake))); + assertEquals(ConsumeResult.SUCCESS, com.alibaba.qwen.code.managedagent.adapters.event.RocketMqMessages.consume(consumer, message(wake))); verify(wakes).onWake(wake); verify(store, never()).materializedThrough(anyString(), anyString()); @@ -93,7 +99,7 @@ class EventTransportConsumerTest { when(store.materializedThrough("t1", "s1")) .thenReturn(OptionalLong.of(9)); - assertEquals(ConsumeResult.SUCCESS, consumer.consume(message( + assertEquals(ConsumeResult.SUCCESS, com.alibaba.qwen.code.managedagent.adapters.event.RocketMqMessages.consume(consumer, message( envelope("public_event", "item.output_text.delta", 10)))); verify(materializer, never()).materializeNotified(anyString(), @@ -158,7 +164,7 @@ class EventTransportConsumerTest { ManagedEventEnvelopes.toJson(envelope("public_event", "turn.started", 2)).replace("{\"eventId\"", "{\"pid\":1,\"eventId\""))) { - assertEquals(ConsumeResult.SUCCESS, consumer.consume( + assertEquals(ConsumeResult.SUCCESS, com.alibaba.qwen.code.managedagent.adapters.event.RocketMqMessages.consume(consumer, message(body))); } verify(wakes, never()).onWake(any()); @@ -169,6 +175,6 @@ class EventTransportConsumerTest { "wake.requested", 3); when(wakes.onWake(wake)).thenThrow( new IllegalStateException("database down")); - assertEquals(ConsumeResult.SUCCESS, consumer.consume(message(wake))); + assertEquals(ConsumeResult.SUCCESS, com.alibaba.qwen.code.managedagent.adapters.event.RocketMqMessages.consume(consumer, message(wake))); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportNodeMain.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportNodeMain.java similarity index 82% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportNodeMain.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportNodeMain.java index fff50646e63..6be637b9819 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportNodeMain.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/EventTransportNodeMain.java @@ -1,15 +1,18 @@ package com.alibaba.qwen.code.managedagent.event; import com.alibaba.qwen.code.managedagent.ManagedAgentServerApplication; -import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.adapters.event.LiteTopicEventTransport; +import com.alibaba.qwen.code.managedagent.adapters.event.LiteTopicPublicEventTransport; +import com.alibaba.qwen.code.managedagent.contracts.event.EventTransport; +import com.alibaba.qwen.code.managedagent.contracts.event.JournalEventTransport; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelope; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelopes; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.model.StoreModels.EventRecord; import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore; import com.alibaba.qwen.code.managedagent.session.OwnerSubscriptions; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.rocketmq.client.apis.ClientServiceProvider; -import org.apache.rocketmq.client.apis.producer.Producer; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.builder.SpringApplicationBuilder; @@ -81,32 +84,32 @@ public final class EventTransportNodeMain { * Session by the file {@code shared/faults//}; it fires on * the first node to create {@code shared/fired/-}. */ - static final class Faults { + public static final class Faults { private static final ObjectMapper JSON = new ObjectMapper(); private final String node; private final Path shared; private final Path evidence; - Faults(String node, Path shared, Path evidence) { + public Faults(String node, Path shared, Path evidence) { this.node = node; this.shared = shared; this.evidence = evidence; } - boolean armed(String rule, String sessionId) { + public boolean armed(String rule, String sessionId) { return sessionId != null && Files.exists(shared.resolve("faults").resolve(rule) .resolve(sessionId)); } /** Whether any node has already fired the rule for the Session. */ - boolean fired(String rule, String sessionId) { + public boolean fired(String rule, String sessionId) { return Files.exists(shared.resolve("fired") .resolve(rule + "-" + sessionId)); } /** Blocks until the gate releases the Session's held loads. */ - void awaitRelease(String sessionId) { + public void awaitRelease(String sessionId) { Path release = shared.resolve("release").resolve(sessionId); while (!Files.exists(release)) { try { @@ -119,11 +122,11 @@ public final class EventTransportNodeMain { } /** Halts this JVM if the rule is armed and no node has fired it yet. */ - void haltIfArmed(String rule, String sessionId) { + public void haltIfArmed(String rule, String sessionId) { haltIfArmed(rule, sessionId, Map.of()); } - void haltIfArmed(String rule, String sessionId, Map detail) { + public void haltIfArmed(String rule, String sessionId, Map detail) { if (!armed(rule, sessionId)) { return; } @@ -144,7 +147,7 @@ public final class EventTransportNodeMain { Runtime.getRuntime().halt(HALTED); } - synchronized void record(Map fields) { + public synchronized void record(Map fields) { Map line = new LinkedHashMap<>(); line.put("node", node); line.put("at", System.currentTimeMillis()); @@ -159,12 +162,12 @@ public final class EventTransportNodeMain { } } - /** Swaps the hooked subclasses in for the production transport beans. */ + /** Wraps the production transports and installs the fault-aware wake activator. */ private static final class Hooks implements BeanPostProcessor { private final ConfigurableListableBeanFactory beans; private final Faults faults; - Hooks(ConfigurableListableBeanFactory beans, Faults faults) { + public Hooks(ConfigurableListableBeanFactory beans, Faults faults) { this.beans = beans; this.faults = faults; } @@ -178,23 +181,12 @@ public final class EventTransportNodeMain { return new HookedWakes(beans.getBean(TransportConsumerStore.class), beans.getBean(HarnessConnector.class), owners::subscribe, faults); } - if (bean instanceof LiteTopicEventTransport - && !(bean instanceof HookedJournalWire)) { - var config = beans.getBean(ManagedAgentProperties.class) - .getEventTransport(); - return new HookedJournalWire(beans.getBean(Producer.class), - beans.getBean(ClientServiceProvider.class), config.getTopic(), - config.getWakeBootstrapLiteTopic(), faults); + if (bean instanceof LiteTopicEventTransport original) { + return new HookedJournalWire(original, faults); } - if (bean instanceof LiteTopicPublicEventTransport original - && !(bean instanceof HookedPublicWire)) { - original.close(); - TransportConsumerStore store = beans.getBean(TransportConsumerStore.class); - return new HookedPublicWire(beans.getBean(Producer.class), - beans.getBean(ClientServiceProvider.class), - beans.getBean(ManagedAgentProperties.class).getEventTransport() - .getTopic(), - beans.getBean(ObjectMapper.class), store, faults); + if (bean instanceof LiteTopicPublicEventTransport original) { + // Spring retains the original bean as its destroy-method owner. + return new HookedPublicWire(original, faults); } return bean; } @@ -208,7 +200,7 @@ public final class EventTransportNodeMain { private final java.util.Set halting = java.util.concurrent.ConcurrentHashMap.newKeySet(); - HookedWakes(TransportConsumerStore store, HarnessConnector harness, + public HookedWakes(TransportConsumerStore store, HarnessConnector harness, java.util.function.Consumer activated, Faults faults) { super(store, harness, activated); this.faults = faults; @@ -244,7 +236,7 @@ public final class EventTransportNodeMain { } @Override - LoadOutcome load(ManagedEventEnvelope envelope) { + public LoadOutcome load(ManagedEventEnvelope envelope) { String session = envelope.sessionId(); if (halting.contains(session)) { // This JVM's listener is halting it; the load must not race @@ -268,13 +260,13 @@ public final class EventTransportNodeMain { } } - private static final class HookedJournalWire extends LiteTopicEventTransport { + private static final class HookedJournalWire implements JournalEventTransport { private static final ObjectMapper JSON = new ObjectMapper(); + private final JournalEventTransport delegate; private final Faults faults; - HookedJournalWire(Producer producer, ClientServiceProvider provider, - String topic, String bootstrapLiteTopic, Faults faults) { - super(producer, provider, topic, bootstrapLiteTopic); + public HookedJournalWire(JournalEventTransport delegate, Faults faults) { + this.delegate = delegate; this.faults = faults; } @@ -303,19 +295,19 @@ public final class EventTransportNodeMain { Map.of("wakes", wakes)); } long started = System.nanoTime(); - super.publishCommitted(recordBytes); + delegate.publishCommitted(recordBytes); faults.record(Map.of("event", "journal-handoff", "session", String.valueOf(session), "wakes", wakes, "micros", (System.nanoTime() - started) / 1000)); } } - private static final class HookedPublicWire extends LiteTopicPublicEventTransport { + private static final class HookedPublicWire implements EventTransport { + private final EventTransport delegate; private final Faults faults; - HookedPublicWire(Producer producer, ClientServiceProvider provider, String topic, - ObjectMapper objectMapper, TransportConsumerStore store, Faults faults) { - super(producer, provider, topic, objectMapper, store::workspaceOf); + public HookedPublicWire(EventTransport delegate, Faults faults) { + this.delegate = delegate; this.faults = faults; } @@ -333,7 +325,7 @@ public final class EventTransportNodeMain { } } long started = System.nanoTime(); - super.publish(events); + delegate.publish(events); faults.record(Map.of("event", "public-handoff", "session", String.valueOf(session), "micros", (System.nanoTime() - started) / 1000)); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializerTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializerTest.java index 8f18af9243d..2ee24f94747 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/MessageMaterializerTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent.event; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.MaterializationResult; import com.alibaba.qwen.code.managedagent.model.StoreModels.MaterializationTarget; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import org.junit.jupiter.api.Test; import org.springframework.dao.DataAccessResourceFailureException; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivatorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivatorTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivatorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivatorTest.java index f6dc309c242..1cfe3eff72b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivatorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/event/TransportWakeActivatorTest.java @@ -1,11 +1,12 @@ package com.alibaba.qwen.code.managedagent.event; import com.alibaba.qwen.code.managedagent.AutomationHarnessFake; +import com.alibaba.qwen.code.managedagent.contracts.event.ManagedEventEnvelope; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.event.TransportWakeActivator.LoadOutcome; import com.alibaba.qwen.code.managedagent.event.TransportWakeActivator.Outcome; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore; -import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore.WakeFence; +import com.alibaba.qwen.code.managedagent.repository.TransportConsumerStore.WakeFence; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageServiceTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageServiceTest.java index b5af513497c..4e06e22ec82 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/extensions/ManagedExtensionPackageServiceTest.java @@ -1,16 +1,17 @@ package com.alibaba.qwen.code.managedagent.extensions; +import com.alibaba.qwen.code.managedagent.adapters.extensions.ManagedExtensionArchiveStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Manifest; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Pin; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.SettingsCommand; +import com.alibaba.qwen.code.managedagent.contracts.extensions.ManagedExtensionPackages.Target; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.Manifest; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.Pin; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.SettingsCommand; -import com.alibaba.qwen.code.managedagent.extensions.ManagedExtensionPackages.Target; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceAccess; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.security.SecretProtector; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.digest.DigestUtils; @@ -18,7 +19,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DriverManagerDataSource; @@ -115,19 +115,19 @@ class ManagedExtensionPackageServiceTest { var pin = new Pin(publication.extensionId(), 1, digest, 1L); assertThatThrownBy(() -> service.resolvePin("tenant", "workspace", pin)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()).isEqualTo("extension_settings_unavailable"); }); assertThatThrownBy(() -> service.saveSettings("tenant", "operator", "workspace", publication.extensionId(), "settings", command)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()).isEqualTo("extension_settings_unavailable"); }); assertThatThrownBy(() -> service.resolvePin("tenant", "workspace", new Pin(publication.extensionId(), 1, digest, 2L))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.NOT_FOUND); + assertThat(error.getStatus()).isEqualTo(404); assertThat(error.getCode()).isEqualTo("extension_package_not_found"); }); assertThat(service.resolvePin("tenant", "workspace", @@ -200,7 +200,7 @@ class ManagedExtensionPackageServiceTest { } private ManagedExtensionPackageService service() { - return new ManagedExtensionPackageService(jdbc, manager, workspaces, properties, archives, - json, beans.getBeanProvider(SecretProtector.class)); + return new ManagedExtensionPackageService(new com.alibaba.qwen.code.managedagent.persistence.JdbcExtensionPackageRepository( + jdbc, manager, json, beans.getBeanProvider(SecretProtector.class)), workspaces, properties, archives, json); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnectorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnectorTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnectorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnectorTest.java index 19335da33a1..84e331b81b3 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnectorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/HarnessPoolConnectorTest.java @@ -5,19 +5,18 @@ import com.alibaba.qwen.code.daemon.DaemonTransportException; import com.alibaba.qwen.code.daemon.HostedHarnessCapabilities; import com.alibaba.qwen.code.daemon.HostedHarnessClient; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.persistence.HarnessPlacementStore; -import com.alibaba.qwen.code.managedagent.persistence.HarnessPlacementStore.Placement; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; +import com.alibaba.qwen.code.managedagent.repository.HarnessPlacementStore.Placement; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentCaptor; -import org.springframework.http.HttpStatus; import java.net.URI; import java.util.List; @@ -242,7 +241,7 @@ class HarnessPoolConnectorTest { var owner = new Placement("tenant", "session", "workspace", "a", clientA.capabilities().getBootId(), 1, state); when(placements.get("tenant", "session")).thenReturn(owner); when(placements.hasUnreleasedOwnership(owner.instanceId(), owner.bootId())).thenReturn(true); - var refusal = new ManagedAgentException(HttpStatus.CONFLICT, code, "Writer fence refused takeover"); + var refusal = new ManagedAgentException(409, code, "Writer fence refused takeover"); when(placements.takeover("tenant", "session", owner, "b", clientB.capabilities().getBootId())).thenThrow(refusal); try (var pool = new HarnessPoolConnector(settings(), placements, () -> new HarnessDiscovery.Snapshot(List.of(a, b), true), @@ -316,7 +315,7 @@ class HarnessPoolConnectorTest { for (int retry = 0; retry < 6; retry++) { assertThatThrownBy(() -> pool.createOrLoad("tenant", "session", false)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + assertThat(error.getStatus()).isEqualTo(503); assertThat(error.getCode()).isEqualTo("harness_pool_unavailable"); }); } diff --git a/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/HarnessSdkValuesTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/HarnessSdkValuesTest.java new file mode 100644 index 00000000000..e6d18fc72ee --- /dev/null +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/HarnessSdkValuesTest.java @@ -0,0 +1,157 @@ +package com.alibaba.qwen.code.managedagent.harness; + +import com.alibaba.qwen.code.daemon.DaemonHttpException; +import com.alibaba.qwen.code.daemon.DaemonProtocolException; +import com.alibaba.qwen.code.daemon.DaemonTransportException; +import com.alibaba.qwen.code.daemon.HostedHarnessGenerationException; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessGenerationFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessProtocolFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessTransportFailure; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.CompletionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +class HarnessSdkValuesTest { + @Test + void httpStatusBodyFieldsAndTransportCauseSurviveTheBoundary() throws Exception { + var cause = new IOException("disconnected"); + var http = sdk(DaemonHttpException.class, new Class[] {String.class, int.class, String.class}, + "create", 409, "{\"code\":\"generation_changed\",\"detail\":\"stale\"}"); + http.initCause(cause); + var mapped = (HarnessHttpFailure) HarnessSdkValues.failure(http); + assertThat(mapped.getStatusCode()).isEqualTo(409); + assertThat(mapped.getResponseBody()).isEqualTo(http.getResponseBody()); + assertThat(mapped.getErrorCode()).isEqualTo("generation_changed"); + assertThat(mapped.getBodyField("detail")).isEqualTo("stale"); + assertThat(mapped).hasMessage(http.getMessage()).hasCause(cause); + } + + @Test + void deferredFailuresAndSuppressedSdkFailuresAreConverted() throws Exception { + var protocol = sdk(DaemonProtocolException.class, new Class[] {String.class}, "malformed"); + var transport = sdk(DaemonTransportException.class, new Class[] {String.class}, "disconnected"); + var deferred = new CompletionException(protocol); + deferred.addSuppressed(transport); + Throwable mapped = HarnessSdkValues.failure(deferred); + assertThat(mapped).isInstanceOf(CompletionException.class).hasCauseInstanceOf(HarnessProtocolFailure.class); + assertThat(mapped.getSuppressed()).hasSize(1); + assertThat(mapped.getSuppressed()[0]).isInstanceOf(HarnessTransportFailure.class); + assertThat(HarnessSdkValues.failure(new IllegalStateException("outer", protocol))) + .isInstanceOf(HarnessFailure.class).hasCauseInstanceOf(HarnessProtocolFailure.class); + } + + @Test + void generationIdentityAndUnrelatedFailuresKeepTheirMeaning() throws Exception { + var generation = sdk(HostedHarnessGenerationException.class, + new Class[] {String.class, String.class}, "before", "after"); + var mapped = (HarnessGenerationFailure) HarnessSdkValues.failure(generation); + assertThat(mapped.getExpectedBootId()).isEqualTo("before"); + assertThat(mapped.getActualBootId()).isEqualTo("after"); + assertThat(mapped.getCode()).isEqualTo(generation.getCode()); + var unrelated = new IllegalArgumentException("invalid"); + assertThat(HarnessSdkValues.failure(unrelated)).isSameAs(unrelated); + assertThat(HarnessSdkValues.recovery(null)).isNull(); + } + + @Test + void boundaryPreservesResultsAndMapsImmediateFailures() throws Exception { + var delegate = mock(HarnessConnector.class); + var boundary = new SdkHarnessBoundary(delegate); + Map body = Map.of("message", "hello"); + Map result = Map.of("state", "done"); + when(delegate.runMessageOperation("tenant", "session", body)).thenReturn(result); + assertThat(boundary.runMessageOperation("tenant", "session", body)).isSameAs(result); + verify(delegate).runMessageOperation("tenant", "session", body); + + var http = sdk(DaemonHttpException.class, new Class[] {String.class, int.class, String.class}, + "drain", 503, "{\"code\":\"unavailable\"}"); + when(delegate.drainInstance("instance", "operation")).thenThrow(http); + assertThatThrownBy(() -> boundary.drainInstance("instance", "operation")) + .isInstanceOfSatisfying(HarnessHttpFailure.class, failure -> { + assertThat(failure.getStatusCode()).isEqualTo(503); + assertThat(failure.getErrorCode()).isEqualTo("unavailable"); + }); + var transport = sdk(DaemonTransportException.class, new Class[] {String.class}, "disconnected"); + doThrow(transport).when(delegate).cancel("tenant", "session"); + assertThatThrownBy(() -> boundary.cancel("tenant", "session")) + .isInstanceOf(HarnessTransportFailure.class).hasMessage("disconnected"); + verify(delegate).cancel("tenant", "session"); + } + + @Test + void compressionSubmissionStaysDeferredAndMapsLaterFailures() throws Exception { + var delegate = mock(HarnessConnector.class); + var submission = mock(HarnessConnector.ContextCompressionSubmission.class); + Map body = Map.of("operationId", "operation"); + when(delegate.prepareContextCompression("tenant", "session", body)).thenReturn(submission); + var prepared = new SdkHarnessBoundary(delegate).prepareContextCompression("tenant", "session", body); + verifyNoInteractions(submission); + var transport = sdk(DaemonTransportException.class, new Class[] {String.class}, "disconnected"); + when(submission.submit()).thenThrow(new CompletionException(transport)); + assertThatThrownBy(prepared::submit).isInstanceOf(CompletionException.class) + .hasCauseInstanceOf(HarnessTransportFailure.class); + verify(submission).submit(); + } + + @ParameterizedTest + @ValueSource(booleans = {false, true}) + void streamReadsAndCloseMapFailuresAfterCreation(boolean observe) throws Exception { + var delegate = mock(HarnessConnector.class); + var source = mock(HarnessConnector.SourceStream.class); + if (observe) { + when(delegate.observeStream("tenant", "session", "boot", 7L, "epoch")).thenReturn(source); + } else { + when(delegate.stream("tenant", "session", 7L, "epoch")).thenReturn(source); + } + var boundary = new SdkHarnessBoundary(delegate); + var stream = observe ? boundary.observeStream("tenant", "session", "boot", 7L, "epoch") + : boundary.stream("tenant", "session", 7L, "epoch"); + verifyNoInteractions(source); + var event = new HarnessConnector.SourceEvent(8L, "message", "hello", "prompt", Map.of()); + var transport = sdk(DaemonTransportException.class, new Class[] {String.class}, "disconnected"); + when(source.next()).thenReturn(event).thenThrow(transport); + when(source.eventEpoch()).thenReturn("epoch").thenThrow(transport); + when(source.recoveryBlocked(7L)).thenReturn(true).thenThrow(transport); + doThrow(transport).when(source).close(); + assertThat(stream.next()).isSameAs(event); + assertThat(stream.eventEpoch()).isEqualTo("epoch"); + assertThat(stream.recoveryBlocked(7L)).isTrue(); + assertThatThrownBy(stream::next).isInstanceOf(HarnessTransportFailure.class); + assertThatThrownBy(stream::eventEpoch).isInstanceOf(HarnessTransportFailure.class); + assertThatThrownBy(() -> stream.recoveryBlocked(7L)).isInstanceOf(HarnessTransportFailure.class); + assertThatThrownBy(stream::close).isInstanceOf(HarnessTransportFailure.class); + verify(source).close(); + } + + @Test + void boundaryKeepsUnrelatedFailuresAndErrorsUnchanged() { + var delegate = mock(HarnessConnector.class); + var boundary = new SdkHarnessBoundary(delegate); + var invalid = new IllegalArgumentException("invalid"); + var fatal = new AssertionError("fatal"); + when(delegate.isAvailable()).thenThrow(invalid).thenThrow(fatal); + assertThatThrownBy(boundary::isAvailable).isSameAs(invalid); + assertThatThrownBy(boundary::isAvailable).isSameAs(fatal); + } + + private static T sdk(Class type, Class[] parameters, Object... arguments) throws Exception { + var constructor = type.getDeclaredConstructor(parameters); + constructor.setAccessible(true); + return constructor.newInstance(arguments); + } +} diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessCreateOrLoadPinningTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessCreateOrLoadPinningTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessCreateOrLoadPinningTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessCreateOrLoadPinningTest.java index e662a907994..ac636b8ea9b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessCreateOrLoadPinningTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessCreateOrLoadPinningTest.java @@ -4,9 +4,9 @@ import com.alibaba.qwen.code.daemon.HarnessSessionRef; import com.alibaba.qwen.code.daemon.HostedHarnessCapabilities; import com.alibaba.qwen.code.daemon.HostedHarnessClient; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.net.httpserver.HttpServer; import org.junit.jupiter.api.AfterEach; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessPoolMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessPoolMySqlIT.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessPoolMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessPoolMySqlIT.java index 4732c64ed44..4cf3ca6097d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessPoolMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/HostedHarnessPoolMySqlIT.java @@ -3,10 +3,9 @@ package com.alibaba.qwen.code.managedagent.harness; import com.alibaba.qwen.code.daemon.HostedHarnessClient; import com.alibaba.qwen.code.managedagent.ManagedAgentServerApplication; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; import com.alibaba.qwen.code.managedagent.persistence.HarnessPlacementStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; @@ -142,7 +141,7 @@ class HostedHarnessPoolMySqlIT { jdbc.update("INSERT INTO ma_session (tenant_id, session_id, agent_id, tenant_key, session_key, session_scope_key," + " gmt_create, gmt_modified) VALUES (?, ?, 'qwen-code', ?, ?, ?, 1, 1)", tenant, id, JdbcRuntimeBindingRepository.harnessDrainKey(tenant), JdbcRuntimeBindingRepository.harnessDrainKey(id), - ManagedSessionStore.sessionScopeKey(tenant, id)); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, id)); jdbc.update("INSERT INTO ma_event_stream (tenant_id, session_id, stream_kind, stream_id, gmt_modified)" + " VALUES (?, ?, 'SESSION', X'', 1)", tenant, id); return id; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/KubernetesHarnessDiscoveryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/KubernetesHarnessDiscoveryTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/KubernetesHarnessDiscoveryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/KubernetesHarnessDiscoveryTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessColdCancelRegressionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessColdCancelRegressionTest.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessColdCancelRegressionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessColdCancelRegressionTest.java index 2e0d0ae0164..94a3512bd28 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessColdCancelRegressionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessColdCancelRegressionTest.java @@ -6,6 +6,7 @@ import com.alibaba.qwen.code.daemon.HostedHarnessCapabilities; import com.alibaba.qwen.code.daemon.HostedHarnessClient; import com.alibaba.qwen.code.daemon.LoadHarnessSession; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore; @@ -15,7 +16,6 @@ import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; import com.alibaba.qwen.code.managedagent.session.HarnessCoordinator; import com.alibaba.qwen.code.managedagent.session.HarnessEventProjector; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.ObjectMapper; @@ -91,8 +91,8 @@ class QwenHostedHarnessColdCancelRegressionTest { properties.getHarness().setWorkspaceFilesEnabled(true); properties.getHarness().setToken("test-token"); properties.getHarness().setCapabilityDigest("sha256:" + "a".repeat(64)); - ManagedAgentStore store = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), - ignored -> {}, registry, properties); + ManagedAgentStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), + ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var admission = store.insertWorkspaceSessionCommand(tenant, "actor-a", "create", "digest", "qwen-code", null, null, List.of(), null, new WorkspaceSelection("ws-a", ".")); String session = admission.sessionId(); @@ -113,10 +113,12 @@ class QwenHostedHarnessColdCancelRegressionTest { HarnessRuntimeRecovery recovery = mock(HarnessRuntimeRecovery.class); when(recovery.getCheckpointId()).thenReturn("checkpoint-of-T1"); when(recovery.getActivationId()).thenReturn("activation-of-T1"); + var expectedRecovery = new com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRuntimeRecovery( + null, "checkpoint-of-T1", "activation-of-T1", List.of(), false, false, false); when(attached.getRuntimeRecovery()).thenReturn(recovery); ManagedActionStore actions = mock(ManagedActionStore.class); when(actions.approvalMode(tenant, session)).thenReturn("yolo"); - WorkspaceExecutionStore execution = new WorkspaceExecutionStore(jdbc, transactionManager); + WorkspaceExecutionStore execution = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, transactionManager)); QwenHostedHarnessConnector connector = new QwenHostedHarnessConnector(properties, store, execution, actions); ReflectionTestUtils.setField(connector, "client", client); connector.createOrLoad(tenant, session, true); @@ -155,13 +157,13 @@ class QwenHostedHarnessColdCancelRegressionTest { assertThatThrownBy(() -> connector.cancelOriginalTurn(tenant, session, prompt)) .isInstanceOf(IllegalStateException.class); assertThat(connector.recoverManagedRuntime(tenant, session, true, prompt).runtimeRecovery()) - .isSameAs(recovery); + .isEqualTo(expectedRecovery); clearInvocations(client); doNothing().when(client).cancelTurn(attached, prompt); coordinator.cancel(tenant, session, turn); jdbc.update("UPDATE ma_workspace_access SET role = 'OPERATOR' WHERE tenant_id = ?", tenant); var retained = connector.recoverManagedRuntime(tenant, session, false).runtimeRecovery(); - assertThat(retained).isSameAs(recovery); + assertThat(retained).isEqualTo(expectedRecovery); assertThat(retained.getCheckpointId()).isEqualTo("checkpoint-of-T1"); assertThat(retained.getActivationId()).isEqualTo("activation-of-T1"); jdbc.update("UPDATE ma_workspace_access SET role = 'READER' WHERE tenant_id = ?", tenant); @@ -186,7 +188,7 @@ class QwenHostedHarnessColdCancelRegressionTest { } ManagedAgentService service = new ManagedAgentService(store, new RequestDigests(), - coordinator, connector, registry); + coordinator, connector, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); clearInvocations(client); assertThatThrownBy(() -> service.cancelTurn(tenant, "actor-a", "cancel-fresh", session, turn)) .isInstanceOfSatisfying(ManagedAgentException.class, @@ -217,8 +219,8 @@ class QwenHostedHarnessColdCancelRegressionTest { properties.getHarness().setWorkspaceFilesEnabled(true); properties.getHarness().setToken("test-token"); properties.getHarness().setCapabilityDigest("sha256:" + "a".repeat(64)); - ManagedAgentStore store = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), - ignored -> {}, registry, properties); + ManagedAgentStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), + ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String session = store.insertWorkspaceSessionCommand(tenant, "actor-a", "create", "digest", "qwen-code", null, null, List.of(), null, new WorkspaceSelection("ws-a", ".")) .sessionId(); @@ -236,7 +238,7 @@ class QwenHostedHarnessColdCancelRegressionTest { when(attached.getApprovalMode()).thenReturn("yolo"); ManagedActionStore actions = mock(ManagedActionStore.class); when(actions.approvalMode(tenant, session)).thenReturn("yolo"); - WorkspaceExecutionStore execution = new WorkspaceExecutionStore(jdbc, transactionManager); + WorkspaceExecutionStore execution = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, transactionManager)); QwenHostedHarnessConnector connector = new QwenHostedHarnessConnector(properties, store, execution, actions); ReflectionTestUtils.setField(connector, "client", client); try { @@ -273,7 +275,9 @@ class QwenHostedHarnessColdCancelRegressionTest { verifyNoInteractions(fixture.client()); var recovered = fixture.connector().recoverManagedFailureCancellation( fixture.tenant(), fixture.session(), fixture.turn(), fixture.owner(), prompt); - assertThat(recovered.runtimeRecovery()).isSameAs(fixture.recovery()); + assertThat(recovered.runtimeRecovery()).isEqualTo( + new com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRuntimeRecovery( + null, "failure-checkpoint", "failure-activation", List.of(), false, false, false)); clearInvocations(fixture.client()); fixture.connector().recoverManagedFailureCancellation( fixture.tenant(), fixture.session(), fixture.turn(), fixture.owner(), prompt); @@ -397,8 +401,8 @@ class QwenHostedHarnessColdCancelRegressionTest { properties.getHarness().setWorkspaceFilesEnabled(true); properties.getHarness().setToken("test-token"); properties.getHarness().setCapabilityDigest("sha256:" + "a".repeat(64)); - ManagedAgentStore store = new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), - ignored -> {}, registry, properties); + ManagedAgentStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, mapper, Clock.systemUTC(), + ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String session = store.insertWorkspaceSessionCommand(tenant, "actor-a", "create", "digest", "qwen-code", null, null, List.of(), null, new WorkspaceSelection("ws-a", ".")).sessionId(); String turn = store.insertTurnCommand(tenant, "SUBMIT", "submit", "digest", session, @@ -413,10 +417,12 @@ class QwenHostedHarnessColdCancelRegressionTest { when(attached.getHarnessBootId()).thenReturn(boot); when(attached.getApprovalMode()).thenReturn("default"); HarnessRuntimeRecovery recovery = mock(HarnessRuntimeRecovery.class); + when(recovery.getCheckpointId()).thenReturn("failure-checkpoint"); + when(recovery.getActivationId()).thenReturn("failure-activation"); when(attached.getRuntimeRecovery()).thenReturn(recovery); ManagedActionStore actions = mock(ManagedActionStore.class); when(actions.approvalMode(tenant, session)).thenReturn("default"); - WorkspaceExecutionStore execution = new WorkspaceExecutionStore(jdbc, transactionManager); + WorkspaceExecutionStore execution = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, transactionManager)); QwenHostedHarnessConnector connector = new QwenHostedHarnessConnector(properties, store, execution, actions); ReflectionTestUtils.setField(connector, "client", client); String owner = "owner-a"; @@ -429,12 +435,11 @@ class QwenHostedHarnessColdCancelRegressionTest { store.recordAdmission(tenant, session, turn, owner, epoch, 1); } clearInvocations(client); - return new RecoveryFixture(tenant, session, turn, owner, store, connector, client, recovery); + return new RecoveryFixture(tenant, session, turn, owner, store, connector, client); } private record RecoveryFixture(String tenant, String session, String turn, String owner, - ManagedAgentStore store, QwenHostedHarnessConnector connector, HostedHarnessClient client, - HarnessRuntimeRecovery recovery) implements AutoCloseable { + ManagedAgentStore store, QwenHostedHarnessConnector connector, HostedHarnessClient client) implements AutoCloseable { @Override public void close() { connector.close(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnectorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnectorTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnectorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnectorTest.java index eecd4b78a30..a1e20e35c89 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnectorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessConnectorTest.java @@ -4,6 +4,7 @@ import com.alibaba.qwen.code.daemon.CreateHarnessSession; import com.alibaba.qwen.code.daemon.DaemonHttpException; import com.alibaba.qwen.code.daemon.HarnessEventStream; import com.alibaba.qwen.code.daemon.HarnessRecoveryStatus; +import com.alibaba.qwen.code.daemon.HarnessRuntimeExecutionRecovery; import com.alibaba.qwen.code.daemon.HarnessRuntimeRecovery; import com.alibaba.qwen.code.daemon.HarnessSessionRef; import com.alibaba.qwen.code.daemon.HarnessSessionStatus; @@ -16,6 +17,11 @@ import com.alibaba.qwen.code.daemon.PromptReceipt; import com.alibaba.qwen.code.daemon.SessionCreationOutcomeUnknownException; import com.alibaba.qwen.code.daemon.SubmitHarnessTurn; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HostedHarnessRecoveryDeclinedException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.ToolResultResourceRef; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellApprovalSelection; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; @@ -25,15 +31,12 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnSummary; import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.ToolResultResourceRef; import com.alibaba.qwen.code.managedagent.persistence.SshWorkspaceStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeStorage; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeStorage; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -1128,7 +1131,7 @@ class QwenHostedHarnessConnectorTest { .containsEntry("passiveManagedRuntimeRecovery", true) .doesNotContainKeys("driveRuntimeRecovery", "captureBytes"); clearInvocations(execution); - RuntimeBrokerException refusal = WorkspaceExecutionStore.unavailable(); + RuntimeBrokerException refusal = com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); doThrow(refusal).when(execution).authorize(session); assertThatThrownBy(() -> connector.createOrLoad("tenant-a", SESSION_ID, true)) .isInstanceOfSatisfying(RuntimeBrokerException.class, error -> { @@ -1442,7 +1445,7 @@ class QwenHostedHarnessConnectorTest { QwenHostedHarnessConnector cold = new QwenHostedHarnessConnector(properties, sessions, execution, mock(ManagedActionStore.class)); ReflectionTestUtils.setField(cold, "client", client); - RuntimeBrokerException refusal = WorkspaceExecutionStore.unavailable(); + RuntimeBrokerException refusal = com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); doThrow(refusal).when(execution).authorize(session); assertThatThrownBy(() -> cold.createOrLoad("tenant-a", SESSION_ID, true)) @@ -1633,7 +1636,7 @@ class QwenHostedHarnessConnectorTest { connector.createOrLoad("tenant-a", SESSION_ID, true); verify(execution).authorize(session); - doThrow(WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); assertThatThrownBy(() -> connector.createOrLoad("tenant-a", SESSION_ID, true)) .hasMessageContaining("Workspace execution authority is unavailable"); assertThatThrownBy(() -> connector.submit("tenant-a", SESSION_ID, @@ -1702,7 +1705,7 @@ class QwenHostedHarnessConnectorTest { // The grant revoked while the Session stays attached: the relay // runs the same Workspace admission as submit and forwards nothing. - doThrow(WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); assertThatThrownBy(() -> connector.runAutomationOperation("tenant-a", SESSION_ID, fire)) .hasMessageContaining("Workspace execution authority is unavailable"); @@ -1924,7 +1927,7 @@ class QwenHostedHarnessConnectorTest { // a consume reconciliation start work in the Session, so they run // the Workspace admission and forward nothing; the sender's own // journal steps still land. - doThrow(WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); for (String kind : java.util.List.of("receive", "consume")) { assertThatThrownBy(() -> connector.runMessageOperation("tenant-a", SESSION_ID, Map.of("operationId", @@ -2007,7 +2010,7 @@ class QwenHostedHarnessConnectorTest { // loads nothing. QwenHostedHarnessConnector fresh = new QwenHostedHarnessConnector(properties, sessions, execution, actions); ReflectionTestUtils.setField(fresh, "client", client); - doThrow(WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); assertThatThrownBy(() -> fresh.activateForWake("tenant-a", SESSION_ID)) .hasMessageContaining("Workspace execution authority is unavailable"); assertThatThrownBy(() -> connector.recoverForWake("tenant-a", SESSION_ID)) @@ -2272,8 +2275,8 @@ class QwenHostedHarnessConnectorTest { // The child's task Turn already ended, so there is no CANCELLING // Turn for a cancellation grant: the stop must not need one, nor // the mount a new-work grant verifies. - doThrow(WorkspaceExecutionStore.unavailable()).when(execution).authorizeCancellation(session); - doThrow(WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(execution).authorizeCancellation(session); + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); QwenHostedHarnessConnector connector = new QwenHostedHarnessConnector(properties, sessions, execution, actions); ReflectionTestUtils.setField(connector, "client", client); Map stop = Map.of( @@ -2326,8 +2329,8 @@ class QwenHostedHarnessConnectorTest { properties.getHarness().setWorkspaceFilesEnabled(true); ManagedActionStore actions = mock(ManagedActionStore.class); when(actions.approvalMode("tenant-a", SESSION_ID)).thenReturn("default"); - doThrow(WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); - doThrow(WorkspaceExecutionStore.unavailable()).when(execution).authorizeCancellation(session); + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(execution).authorizeCancellation(session); QwenHostedHarnessConnector connector = new QwenHostedHarnessConnector(properties, sessions, execution, actions); ReflectionTestUtils.setField(connector, "client", client); Map cancel = Map.of("kind", "cancel_run", @@ -2424,7 +2427,7 @@ class QwenHostedHarnessConnectorTest { // Even with the attachment cached, a channel operation is new work: // it re-runs the Workspace authority like submit and continue do. - doThrow(WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(execution).authorize(session); assertThatThrownBy(() -> connector.runChannelOperation("tenant-a", SESSION_ID, Map.of("kind", "submit_input"))) .hasMessageContaining("Workspace execution authority is unavailable"); @@ -2532,7 +2535,7 @@ class QwenHostedHarnessConnectorTest { var response = new ObjectMapper().createObjectNode().put("optionId", "allow") .put("inputRevision", 7L).put("policyRevision", "hosted-tool-approval/1"); - doThrow(WorkspaceExecutionStore.unavailable()).doNothing() + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).doNothing() .when(execution).authorizeActionResponse(session); assertThatThrownBy(() -> connector.resolveAction("tenant-a", SESSION_ID, actionId, response)) .hasMessageContaining("Workspace execution authority is unavailable"); @@ -2571,7 +2574,7 @@ class QwenHostedHarnessConnectorTest { verify(execution, times(6)).authorizeActionResponse(session); verify(execution, times(3)).verifyMountForProbe(session.workspace()); - doThrow(WorkspaceExecutionStore.unavailable()).when(execution).authorizeActionResponse(session); + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(execution).authorizeActionResponse(session); assertThatThrownBy(() -> connector.resolveAction("tenant-a", SESSION_ID, actionId, response)) .hasMessageContaining("Workspace execution authority is unavailable"); verify(client, times(2)).resolveAction(any(), any(), any(), anyLong(), any()); @@ -2585,6 +2588,10 @@ class QwenHostedHarnessConnectorTest { mock(HostedHarnessCapabilities.class); HarnessSessionRef session = mock(HarnessSessionRef.class); HarnessRuntimeRecovery recovery = mock(HarnessRuntimeRecovery.class); + when(recovery.getCheckpointId()).thenReturn("takeover-checkpoint"); + when(recovery.getActivationId()).thenReturn("takeover-activation"); + var expectedRecovery = new com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRuntimeRecovery( + null, "takeover-checkpoint", "takeover-activation", List.of(), false, false, false); PromptReceipt receipt = mock(PromptReceipt.class); when(capabilities.getBootId()).thenReturn(BOOT_ID); when(client.capabilities()).thenReturn(capabilities); @@ -2597,10 +2604,10 @@ class QwenHostedHarnessConnectorTest { QwenHostedHarnessConnector connector = connector(client); assertThat(connector.recoverManagedRuntime("tenant-a", SESSION_ID, - false).runtimeRecovery()).isSameAs(recovery); + false).runtimeRecovery()).isEqualTo(expectedRecovery); // Re-entered before the continuation was admitted: still pending. assertThat(connector.recoverManagedRuntime("tenant-a", SESSION_ID, - false).runtimeRecovery()).isSameAs(recovery); + false).runtimeRecovery()).isEqualTo(expectedRecovery); connector.continueManagedRuntime("tenant-a", SESSION_ID, "44444444-4444-4444-8444-444444444444", "checkpoint", "activation"); @@ -3895,6 +3902,10 @@ class QwenHostedHarnessConnectorTest { when(client.capabilities()).thenReturn(capabilities); HarnessSessionRef attached = mock(HarnessSessionRef.class); HarnessRuntimeRecovery recovery = mock(HarnessRuntimeRecovery.class); + when(recovery.getCheckpointId()).thenReturn("unknown-create-checkpoint"); + when(recovery.getActivationId()).thenReturn("unknown-create-activation"); + var expectedRecovery = new com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRuntimeRecovery( + null, "unknown-create-checkpoint", "unknown-create-activation", List.of(), false, false, false); when(attached.getHarnessBootId()).thenReturn(BOOT_ID); when(attached.getRuntimeRecovery()).thenReturn(recovery); when(attached.getHarnessLastEventId()).thenReturn(41L); @@ -3917,7 +3928,7 @@ class QwenHostedHarnessConnectorTest { // The unknown-outcome create must recover by loading, and every // Attachment component the coordinator resumes on must travel. assertThat(admission.bootId()).isEqualTo(BOOT_ID); - assertThat(admission.runtimeRecovery()).isSameAs(recovery); + assertThat(admission.runtimeRecovery()).isEqualTo(expectedRecovery); assertThat(admission.lastEventId()).isEqualTo(41L); assertThat(admission.eventEpoch()).isEqualTo("epoch-7"); ArgumentCaptor load = @@ -3937,6 +3948,25 @@ class QwenHostedHarnessConnectorTest { when(client.capabilities()).thenReturn(capabilities); HarnessSessionRef attached = mock(HarnessSessionRef.class); HarnessRuntimeRecovery recovery = mock(HarnessRuntimeRecovery.class); + HarnessRuntimeExecutionRecovery execution = mock(HarnessRuntimeExecutionRecovery.class); + when(execution.getFunctionCallId()).thenReturn("function-call"); + when(execution.getToolName()).thenReturn("shell"); + when(execution.getExecutionCallId()).thenReturn("execution-call"); + when(execution.getRuntimeSessionId()).thenReturn("runtime-session"); + when(execution.getProgressCursor()).thenReturn("cursor"); + when(execution.getOutcome()).thenReturn("known"); + when(execution.getStatus()).thenReturn(Map.of("state", "settled")); + when(recovery.getPhase()).thenReturn("results_ready"); + when(recovery.getCheckpointId()).thenReturn("checkpoint"); + when(recovery.getActivationId()).thenReturn("activation"); + when(recovery.getExecutions()).thenReturn(List.of(execution)); + when(recovery.isContinuationReady()).thenReturn(true); + when(recovery.isCancellationReady()).thenReturn(true); + var expectedRecovery = new com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRuntimeRecovery( + "results_ready", "checkpoint", "activation", + List.of(new com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRuntimeRecovery.Execution( + "function-call", "shell", "execution-call", "runtime-session", "cursor", "known", + Map.of("state", "settled"))), false, true, true); when(attached.getHarnessBootId()).thenReturn(BOOT_ID); when(attached.getRuntimeRecovery()).thenReturn(recovery); when(attached.getHarnessLastEventId()).thenReturn(23L); @@ -3948,7 +3978,7 @@ class QwenHostedHarnessConnectorTest { connector.createOrLoad("tenant-a", SESSION_ID, true); assertThat(admission.bootId()).isEqualTo(BOOT_ID); - assertThat(admission.runtimeRecovery()).isSameAs(recovery); + assertThat(admission.runtimeRecovery()).isEqualTo(expectedRecovery); assertThat(admission.lastEventId()).isEqualTo(23L); assertThat(admission.eventEpoch()).isEqualTo("epoch-3"); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessNewSessionRegressionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessNewSessionRegressionTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessNewSessionRegressionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessNewSessionRegressionTest.java index a32827e6c1c..19edb57ae57 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessNewSessionRegressionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedHarnessNewSessionRegressionTest.java @@ -6,9 +6,10 @@ import com.alibaba.qwen.code.daemon.HostedHarnessCapabilities; import com.alibaba.qwen.code.daemon.HostedHarnessClient; import com.alibaba.qwen.code.daemon.LoadHarnessSession; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import org.junit.jupiter.api.Test; import org.springframework.test.util.ReflectionTestUtils; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedMcpManagementAttachmentTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedMcpManagementAttachmentTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedMcpManagementAttachmentTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedMcpManagementAttachmentTest.java index 212b58f4dac..270008b265c 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedMcpManagementAttachmentTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/harness/QwenHostedMcpManagementAttachmentTest.java @@ -8,12 +8,12 @@ import com.alibaba.qwen.code.daemon.HostedHarnessClient; import com.alibaba.qwen.code.daemon.LoadHarnessSession; import com.alibaba.qwen.code.daemon.SessionCreationOutcomeUnknownException; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentDefinitionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMainMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/ResidentWorkspaceRetirementMainMySqlIT.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMainMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/ResidentWorkspaceRetirementMainMySqlIT.java index df6ffef9d2e..b4f8a2e4bee 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMainMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/ResidentWorkspaceRetirementMainMySqlIT.java @@ -1,5 +1,7 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; +import com.alibaba.qwen.code.managedagent.persistence.IsolatedManagedSchemaMySqlTestDatabase; +import com.alibaba.qwen.code.managedagent.persistence.ResidentRetirementFixture; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMainTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/ResidentWorkspaceRetirementMainTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMainTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/ResidentWorkspaceRetirementMainTest.java index 981b689788a..55baf0e7f3b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMainTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/ResidentWorkspaceRetirementMainTest.java @@ -1,5 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; +import com.alibaba.qwen.code.managedagent.persistence.ResidentWorkspaceRetirementStore; import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiHostedMainTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiHostedMainTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiHostedMainTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiHostedMainTest.java index 5ea3d9dcacc..d53ac3886bf 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiHostedMainTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiHostedMainTest.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.databind.DeserializationFeature; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeMainTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiRuntimeMainTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeMainTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiRuntimeMainTest.java index 33763ad6f66..36b5d314748 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeMainTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiRuntimeMainTest.java @@ -1,6 +1,6 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; -import com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceCsiResourceGuard; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; @@ -30,7 +30,7 @@ class WorkspaceCsiRuntimeMainTest { String uid = UUID.randomUUID().toString(); var request = new WorkspaceCsiRuntimeMain.Request(registration, UUID.randomUUID().toString(), "a".repeat(64), "registry.example/worker@sha256:" + "b".repeat(64), List.of("node", "/opt/worker.js"), List.of(), - new WorkspaceCsiResourceGuard.ProtectionIdentity(uid, "protect", uid, 1, "protect-binding", uid, 1)); + new com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity(uid, "protect", uid, 1, "protect-binding", uid, 1)); Path path = directory.resolve("request.json"); String body = json.writeValueAsString(request); Files.writeString(path, body); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionMainTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiSessionMainTest.java similarity index 90% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionMainTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiSessionMainTest.java index 8ef704e9d03..dda0a986d35 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionMainTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiSessionMainTest.java @@ -1,15 +1,22 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; +import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; +import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; +import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationRetentionStore; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; import com.alibaba.qwen.code.managedagent.session.HarnessCoordinator; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; @@ -87,7 +94,7 @@ class WorkspaceCsiSessionMainTest { CsiFilesRetirementProfile.CONFIG_REF, CsiFilesRetirementProfile.POLICY_REF); jdbc.update( "INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" - + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); } @Test @@ -99,7 +106,7 @@ class WorkspaceCsiSessionMainTest { "SELECT " + ManagedSchemaProjection.TENANT_GUARD.columns() + " FROM ma_tenant_guard WHERE guard_domain = 'output_publication'"); assertThat(retention.get("tenant_id")).isEqualTo("tenant"); assertThat(retention.get("tenant_key")).isEqualTo( - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("tenant")); + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("tenant")); assertThat(first.replayed()).isFalse(); assertThat(UUID.fromString(first.sessionId()).toString()).isEqualTo(first.sessionId()); var row = jdbc.queryForMap("SELECT " + ManagedSchemaProjection.SESSION.columns() + " FROM ma_session"); @@ -107,12 +114,12 @@ class WorkspaceCsiSessionMainTest { assertThat(row.get("runtime_request_key")).isEqualTo(first.runtimeRequestKey()); assertThat(row.get("workspace_generation")).isEqualTo(3L); assertThat(row.get("workspace_storage_id")).isEqualTo("storage"); - assertThat(row.get("context_config_ref")).isEqualTo(ManagedWorkspaceRegistry.descriptorRef( + assertThat(row.get("context_config_ref")).isEqualTo(com.alibaba.qwen.code.managedagent.persistence.PersistenceTestAccess.descriptorRef( CsiFilesRetirementProfile.CONFIG_REF, CsiFilesRetirementProfile.POLICY_REF)); - assertThat(row.get("creator_actor_key")).isEqualTo(ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + assertThat(row.get("creator_actor_key")).isEqualTo(com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); assertThat(row.get("owner_actor_key")).isEqualTo(row.get("creator_actor_key")); var sessions = store(); - var original = new TransactionTemplate(manager).execute(status -> sessions.requireCsiRequest(registration, + var original = new TransactionTemplate(manager).execute(status -> com.alibaba.qwen.code.managedagent.persistence.PersistenceTestAccess.requireCsiRequest(sessions, registration, first.sessionId())); assertThat(original.getIsolationKey()).isEqualTo(first.sessionId()); assertThat(original.requestKey()).isEqualTo(first.runtimeRequestKey()); @@ -124,7 +131,7 @@ class WorkspaceCsiSessionMainTest { var replay = create(request("create")); assertThat(jdbc.queryForMap( "SELECT " + ManagedSchemaProjection.TENANT_GUARD.columns() + " FROM ma_tenant_guard WHERE guard_domain = 'output_publication'")).isEqualTo(retention); - assertThat(replay).isEqualTo(new WorkspaceCsiSessionMain.Created(first.sessionId(), first.runtimeRequestKey(), true)); + assertThat(replay).isEqualTo(new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Created(first.sessionId(), first.runtimeRequestKey(), true)); assertThat(json.valueToTree(jdbc.queryForMap("SELECT " + ManagedSchemaProjection.SESSION.columns() + " FROM ma_session"))).isEqualTo(json.valueToTree(row)); assertThat(jdbc.queryForMap("SELECT " + ManagedSchemaProjection.STORAGE_OWNER.columns() + " FROM ma_storage_owner")).isEqualTo(physicalBefore); assertEmpty( @@ -184,7 +191,7 @@ class WorkspaceCsiSessionMainTest { var service = new ManagedAgentService(childStore, new RequestDigests(), mock(HarnessCoordinator.class), - harness, new ManagedWorkspaceRegistry(jdbc)); + harness, new ManagedWorkspaceRegistry(jdbc), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); assertThatThrownBy(() -> service.createChildSession("tenant", original.sessionId(), "child-run", "audit", "inspect")) .isInstanceOf(ManagedAgentException.class) @@ -259,10 +266,10 @@ class WorkspaceCsiSessionMainTest { + " ?, state = 'ACTIVE'", CsiFilesRetirementProfile.CONFIG_REF, CsiFilesRetirementProfile.POLICY_REF); jdbc.update( "UPDATE ma_workspace_access SET role = 'OPERATOR', actor_id = ?", - ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); jdbc.update("UPDATE ma_storage SET registration_revision = 7"); } - var wrongRevision = new WorkspaceCsiSessionMain.Request(registration, "actor", "rejected", "unsupported", null, + var wrongRevision = new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request(registration, "actor", "rejected", "unsupported", null, new WorkspaceSelection("workspace", ".")); assertThatThrownBy(() -> create(wrongRevision)).isInstanceOf(ManagedAgentException.class) .satisfies(error -> assertThat(((ManagedAgentException) error).getCode()).isEqualTo("unsupported_feature")); @@ -273,7 +280,7 @@ class WorkspaceCsiSessionMainTest { void refusesChangedRequestAndCorruptedPinOnReplay() { var first = create(request("replay")); var before = jdbc.queryForMap("SELECT " + ManagedSchemaProjection.SESSION.columns() + " FROM ma_session"); - var changed = new WorkspaceCsiSessionMain.Request(registration, "actor", "replay", null, "different", + var changed = new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request(registration, "actor", "replay", null, "different", new WorkspaceSelection("workspace", ".")); assertThatThrownBy(() -> create(changed)).isInstanceOf(ManagedAgentException.class) .satisfies(error -> assertThat(((ManagedAgentException) error).getCode()).isEqualTo("idempotency_conflict")); @@ -291,7 +298,7 @@ class WorkspaceCsiSessionMainTest { jdbc.execute( "ALTER TABLE ma_session ADD CONSTRAINT refuse_test_create CHECK (title <>" + " 'reject')"); - var refused = new WorkspaceCsiSessionMain.Request(registration, "actor", "reject", null, "reject", + var refused = new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request(registration, "actor", "reject", null, "reject", new WorkspaceSelection("workspace", ".")); assertThatThrownBy(() -> create(refused)).isInstanceOf(org.springframework.dao.DataIntegrityViolationException.class); assertEmpty( @@ -338,7 +345,7 @@ class WorkspaceCsiSessionMainTest { WorkspaceExecutionProfile.POLICY_REF); jdbc.update("INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" + " VALUES ('tenant', 'local-workspace', ?, 'OPERATOR')", - ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); var legacy = new TransactionTemplate(manager).execute(status -> store().insertWorkspaceSessionCommand("tenant", "actor", "old", "sha256:" + "b".repeat(64), "qwen-code", null, null, List.of(), null, new WorkspaceSelection("local-workspace", "."))); @@ -434,7 +441,7 @@ class WorkspaceCsiSessionMainTest { var original = create(request("agent-instructions")); var before = allTables(); assertPrivateRefusal(() -> new TransactionTemplate(manager).executeWithoutResult(status -> - new ManagedSessionStore(jdbc).publishAgentInstructions("tenant", "workspace", original.sessionId(), "instructions"))); + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).publishAgentInstructions("tenant", "workspace", original.sessionId(), "instructions"))); assertThat(allTables()).isEqualTo(before); } @@ -459,7 +466,7 @@ class WorkspaceCsiSessionMainTest { var original = create(request("direct-retained-pin")); jdbc.update("UPDATE ma_session SET tool_profile = 'workspace-files/1'"); var before = allTables(); - assertThatThrownBy(() -> new ManagedSessionStore(jdbc).acquireWriter("tenant", original.sessionId(), + assertThatThrownBy(() -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).acquireWriter("tenant", original.sessionId(), "a".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "writer", 60000L))) .isInstanceOf(IllegalStateException.class) .hasMessage("CSI mutation requires its original transaction connection"); @@ -591,12 +598,12 @@ class WorkspaceCsiSessionMainTest { private void insertLegacyOperation(String session) { String id = count("ma_operation") == 0 ? "unused" : UUID.randomUUID().toString(); - String scope = ManagedSessionStore.sessionScopeKey("tenant", session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session); String target = - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(session); + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(session); jdbc.update( "INSERT INTO ma_operation (operation_key, scope_key, session_scope_key, target_kind, target_key, request_json, phase, tenant_id, session_id, operation_id, operation_kind, actor_digest, idempotency_key, request_digest, state, admission_stage, delivery_state, session_status_before, available_at, gmt_create, gmt_modified) VALUES (?, ?, ?, 'SESSION', ?, '{}', 'ADMITTED', 'tenant', ?, ?, 'CLOSE', '', 'unused', 'digest', 'PENDING', 'JAVA_DURABLE', 'PENDING', 'ACTIVE', 0, 0, 0)", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( "CLOSE", scope, target, id), scope, scope, @@ -634,21 +641,21 @@ class WorkspaceCsiSessionMainTest { assertThatThrownBy(operation).isInstanceOf(ManagedAgentException.class).satisfies(error -> { var refusal = (ManagedAgentException) error; assertThat(refusal.getCode()).isEqualTo("csi_managed_mutation_unavailable"); - assertThat(refusal.getStatus()).isEqualTo(org.springframework.http.HttpStatus.CONFLICT); + assertThat(refusal.getStatus()).isEqualTo(org.springframework.http.HttpStatus.CONFLICT.value()); }); } - private WorkspaceCsiSessionMain.Request request(String idempotencyKey) { - return new WorkspaceCsiSessionMain.Request(registration, "actor", idempotencyKey, null, null, + private com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request request(String idempotencyKey) { + return new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request(registration, "actor", idempotencyKey, null, null, new WorkspaceSelection("workspace", ".")); } - private WorkspaceCsiSessionMain.Created create(WorkspaceCsiSessionMain.Request request) { + private com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Created create(com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request request) { return WorkspaceCsiSessionMain.create(jdbc, manager, json, properties, request); } private ManagedAgentStore store() { - return new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } private void assertEmpty(String... tables) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckMainTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiWorkerAckMainTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckMainTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiWorkerAckMainTest.java index 9c8a4b6a84c..984b9f8a971 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckMainTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceCsiWorkerAckMainTest.java @@ -1,5 +1,7 @@ -package com.alibaba.qwen.code.managedagent.persistence; +package com.alibaba.qwen.code.managedagent.maintenance; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiWorkerAckStore; +import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiWorkerAckStoreTest; import com.fasterxml.jackson.databind.JsonNode; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRecoveryCommandTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceRecoveryCommandTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRecoveryCommandTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceRecoveryCommandTest.java index c432bd4022a..588842a7526 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRecoveryCommandTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/maintenance/WorkspaceRecoveryCommandTest.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.managedagent.workspace.runtime; +package com.alibaba.qwen.code.managedagent.maintenance; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/model/RequestDigestsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/model/RequestDigestsTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/model/RequestDigestsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/model/RequestDigestsTest.java diff --git a/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentStateStoreProxyTransactionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentStateStoreProxyTransactionTest.java new file mode 100644 index 00000000000..6b86e4cc514 --- /dev/null +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentStateStoreProxyTransactionTest.java @@ -0,0 +1,96 @@ +package com.alibaba.qwen.code.managedagent.persistence; + +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import org.junit.jupiter.api.Test; +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource; +import org.springframework.transaction.interceptor.TransactionInterceptor; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.mock; + +class AgentStateStoreProxyTransactionTest { + @Test + void allSevenFormerDefaultBoundariesCommitAndRollbackOnTheDedicatedConnection() throws Exception { + var source = new DriverManagerDataSource("jdbc:h2:mem:transaction-boundaries-" + UUID.randomUUID() + ";DB_CLOSE_DELAY=-1", "sa", ""); + var business = new DriverManagerDataSource("jdbc:h2:mem:host-business-" + UUID.randomUUID(), "sa", ""); + var jdbc = new JdbcTemplate(source); + jdbc.execute("CREATE TABLE probe (step INTEGER NOT NULL)"); + var fail = new AtomicBoolean(); + var target = mock(ManagedAgentStore.class, invocation -> { + var method = invocation.getMethod(); + if (method.getName().equals("insertSessionCommand") && method.getParameterCount() == 10 + || method.getName().equals("insertWorkspaceSessionCommand") && method.getParameterCount() == 20) { + assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isTrue(); + assertThat(TransactionSynchronizationManager.hasResource(source)).isTrue(); + assertThat(TransactionSynchronizationManager.hasResource(business)).isFalse(); + jdbc.update("INSERT INTO probe VALUES (1)"); + jdbc.update("INSERT INTO probe VALUES (2)"); + if (fail.get()) { + throw new IllegalStateException("after both writes"); + } + return null; + } + return CALLS_REAL_METHODS.answer(invocation); + }); + var beans = new DefaultListableBeanFactory(); + beans.registerSingleton("managedAgentTransactionManager", new DataSourceTransactionManager(source)); + beans.registerSingleton("businessTransactionManager", new DataSourceTransactionManager(business)); + var interceptor = new TransactionInterceptor(); + interceptor.setBeanFactory(beans); + interceptor.setTransactionAttributeSource(new AnnotationTransactionAttributeSource()); + interceptor.setTransactionManagerBeanName("businessTransactionManager"); + interceptor.afterPropertiesSet(); + var factory = new ProxyFactory(target); + factory.setProxyTargetClass(true); + factory.addAdvice(interceptor); + var proxy = (AgentStateStore) factory.getProxy(); + List entries = Arrays.stream(AgentStateStore.class.getMethods()).filter(method -> + method.getName().equals("insertSessionCommand") && method.getParameterCount() == 9 + || method.getName().equals("insertWorkspaceSessionCommand") && Set.of(11, 12, 14, 15, 17, 19).contains(method.getParameterCount())).toList(); + assertThat(entries).hasSize(7); + for (var entry : entries) { + assertThat(ManagedAgentStore.class.getDeclaredMethod(entry.getName(), entry.getParameterTypes()) + .getAnnotation(org.springframework.transaction.annotation.Transactional.class)).isNotNull(); + Object[] arguments = + Arrays.stream(entry.getParameterTypes()) + .map( + type -> { + if (type == boolean.class) { + return false; + } + if (type == List.class) { + return List.of(); + } + if (type == Set.class) { + return Set.of(); + } + return null; + }) + .toArray(); + fail.set(false); + entry.invoke(proxy, arguments); + assertThat(jdbc.queryForList("SELECT step FROM probe ORDER BY step", Integer.class)).containsExactly(1, 2); + jdbc.update("DELETE FROM probe"); + fail.set(true); + assertThatThrownBy(() -> entry.invoke(proxy, arguments)).isInstanceOf(InvocationTargetException.class) + .hasCauseInstanceOf(IllegalStateException.class); + assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM probe", Integer.class)).isZero(); + } + } +} diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentStateStoreTransactionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentStateStoreTransactionTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentStateStoreTransactionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentStateStoreTransactionTest.java index aa6469c4e1a..6b6e15e5e28 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentStateStoreTransactionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AgentStateStoreTransactionTest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import org.junit.jupiter.api.Test; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AliyunToolPublicationReadRetryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AliyunToolPublicationReadRetryTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AliyunToolPublicationReadRetryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AliyunToolPublicationReadRetryTest.java index 2d2f81d8cc5..b8b563a30e7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AliyunToolPublicationReadRetryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AliyunToolPublicationReadRetryTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.AliyunToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationStorageException; +import com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationStorageException; import com.aliyun.oss.ClientBuilderConfiguration; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AutomationCommandTimestampTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AutomationCommandTimestampTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AutomationCommandTimestampTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AutomationCommandTimestampTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStoreTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStoreTest.java index 22af75a9635..b6a35ec8c11 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/AutomationLedgerStoreTest.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.jdbc.core.JdbcTemplate; @@ -168,7 +169,7 @@ class AutomationLedgerStoreTest { jdbc.update("INSERT INTO ma_session_record" + " (session_scope_key, tenant_id, workspace_id, session_id, domain, record_id, record_key, revision, task_state, record_resource_id, gmt_create) VALUES (?, ?, 'w', ?, 'automation_run'," + " 'arun_budget', ?, 1, 'completed', 'res', 42)", - ManagedSessionStore.sessionScopeKey(TENANT, SESSION), TENANT, + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, SESSION), TENANT, SESSION, ManagedExtensionProjection.recordKey(SESSION, "automation_run", "arun_budget")); assertThat(store.countActive(TENANT, SCHEDULE)).isZero(); @@ -181,7 +182,7 @@ class AutomationLedgerStoreTest { jdbc.update("INSERT INTO ma_session_record" + " (session_scope_key, tenant_id, workspace_id, session_id, domain, record_id, record_key, revision, task_state, record_resource_id, gmt_create) VALUES (?, ?, 'w', ?, 'automation_run'," + " 'arun_older', ?, 1, 'completed', 'res', 42)", - ManagedSessionStore.sessionScopeKey(TENANT, SESSION), TENANT, + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, SESSION), TENANT, SESSION, ManagedExtensionProjection.recordKey(SESSION, "automation_run", "arun_older")); jdbc.update("INSERT INTO ma_schedule_occurrence (tenant_id,schedule_id,occurrence_key,run_id,session_id,slot,trigger_kind,outcome,reason,definition_revision,fence,attempts,next_retry_at,last_error,gmt_create,gmt_modified) VALUES" diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStoreTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStoreTest.java index d31a63d4a0d..805eae153e3 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ChildWorkspaceStoreTest.java @@ -1,11 +1,11 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedActionStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedActionStoreTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedActionStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedActionStoreTest.java index 05aeafbe9bd..9ae5451dcea 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedActionStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedActionStoreTest.java @@ -1,15 +1,15 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredResource; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -46,7 +46,7 @@ class ConsolidatedActionStoreTest { ManagedSchema.initialize(source); jdbc = new JdbcTemplate(source); transactions = new TransactionTemplate(new DataSourceTransactionManager(source)); - scope = ManagedSessionStore.sessionScopeKey("tenant", "session"); + scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "session"); jdbc.update("INSERT INTO ma_session (tenant_id,session_id,agent_id,status,approval_mode,gmt_create,gmt_modified)" + " VALUES (?,?,?,'ACTIVE','manual',0,0)", "tenant", "session", "agent"); AgentStateStore state = mock(AgentStateStore.class); @@ -76,7 +76,7 @@ class ConsolidatedActionStoreTest { assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ma_resource_ref WHERE owner_kind='SESSION_RECORD'", Long.class)).isEqualTo(1); project("decided", response("allow"), 2); assertThat(actions.find("tenant", "session", ACTION).orElseThrow().decisionDigest()) - .isEqualTo(ManagedActionStore.decisionDigest(response("allow"))); + .isEqualTo(com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.decisionDigest(response("allow"))); assertThat(actions.list("tenant", "session", null, null, 10)).isEmpty(); assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ma_resource_ref WHERE owner_kind='SESSION_RECORD'", Long.class)).isEqualTo(2); } @@ -115,10 +115,10 @@ class ConsolidatedActionStoreTest { jdbc.update("UPDATE ma_session SET workspace_id='workspace',workspace_generation=1," + "workspace_storage_id='storage',cwd_relative='.',context_config_ref='config'," + "context_revision=1,workspace_config_ref='config',workspace_policy_ref='policy'"); - byte[] actor = ManagedWorkspaceRegistry.actorKey("tenant", "actor"); + byte[] actor = com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor"); ManagedReceipts.insert(jdbc, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_ACTOR, - "tenant", com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope.ACTOR, + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_ACTOR, + "tenant", com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope.ACTOR, ManagedReceipts.actorScope(actor), "create", "create-digest", "CREATE_SESSION", actor, "session", null, "COMPLETED", null, true, 0); assertThatThrownBy(() -> transactions.execute(status -> actions.admit( @@ -133,7 +133,7 @@ class ConsolidatedActionStoreTest { JsonNode response = JSON.createObjectNode() .put("policyRevision", "p1").put("inputRevision", 7).put("optionId", "allow"); - assertThat(ManagedActionStore.decisionDigest(response)).isEqualTo( + assertThat(com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.decisionDigest(response)).isEqualTo( "d61ff840d95b8aaa9b24999e2910fc687098d73b225c0837b6ec6718fb038ae5"); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedChannelStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedChannelStoreTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedChannelStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedChannelStoreTest.java index 9fc3d96526a..57adf278fc8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedChannelStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedChannelStoreTest.java @@ -1,9 +1,10 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.persistence.ChannelDeliveryRepository.ChannelDelivery; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelDeliveryRepository.ChannelDelivery; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelStoreSupport; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -93,7 +94,7 @@ class ConsolidatedChannelStoreTest { private void resource(String session, String body) { byte[] bytes = body.getBytes(StandardCharsets.UTF_8); - String scope = ManagedSessionStore.sessionScopeKey("tenant", session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session); jdbc.update("INSERT INTO ma_resource (resource_key,session_scope_key,tenant_id,session_id,resource_id,kind,schema_version,byte_length,sha256,storage_kind,inline_bytes,state,gmt_create) VALUES (?,?,'tenant',?,'resource','domain',1,?,?, 'MYSQL_INLINE',?,'REFERENCED'," + ManagedSchemaTime.SQL_NOW + ")", ManagedSchemaKeys.key(scope, "resource"), scope, session, bytes.length, ChannelStoreSupport.sha256(body), bytes); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedStoreFixture.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedStoreFixture.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedStoreFixture.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedStoreFixture.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedTaskEventStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedTaskEventStoreTest.java similarity index 83% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedTaskEventStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedTaskEventStoreTest.java index 61eaaa1abde..6dc6c134557 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedTaskEventStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ConsolidatedTaskEventStoreTest.java @@ -36,14 +36,14 @@ class ConsolidatedTaskEventStoreTest { @Test void opaqueCursorBindsTenantSessionAndTaskWithoutDelimiterAmbiguity() { String task = "任务:child:1"; - String cursor = ManagedTaskEventStore.encodeCursor("tenant", "session", task, 14); - assertThat(ManagedTaskEventStore.decodeCursor("tenant", "session", task, cursor)).isEqualTo(14); - assertThatThrownBy(() -> ManagedTaskEventStore.decodeCursor("another", "session", task, cursor)).isInstanceOf(ManagedAgentException.class); - assertThatThrownBy(() -> ManagedTaskEventStore.decodeCursor("tenant", "another", task, cursor)).isInstanceOf(ManagedAgentException.class); - assertThatThrownBy(() -> ManagedTaskEventStore.decodeCursor("tenant", "session", task + " ", cursor)).isInstanceOf(ManagedAgentException.class); - assertThatThrownBy(() -> ManagedTaskEventStore.decodeCursor("tenant", "session", task, "x".repeat(129))).isInstanceOf(ManagedAgentException.class); + String cursor = com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.encodeCursor("tenant", "session", task, 14); + assertThat(com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.decodeCursor("tenant", "session", task, cursor)).isEqualTo(14); + assertThatThrownBy(() -> com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.decodeCursor("another", "session", task, cursor)).isInstanceOf(ManagedAgentException.class); + assertThatThrownBy(() -> com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.decodeCursor("tenant", "another", task, cursor)).isInstanceOf(ManagedAgentException.class); + assertThatThrownBy(() -> com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.decodeCursor("tenant", "session", task + " ", cursor)).isInstanceOf(ManagedAgentException.class); + assertThatThrownBy(() -> com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.decodeCursor("tenant", "session", task, "x".repeat(129))).isInstanceOf(ManagedAgentException.class); String old = java.util.Base64.getUrlEncoder().encodeToString("tev1:task:14".getBytes(java.nio.charset.StandardCharsets.US_ASCII)); - assertThatThrownBy(() -> ManagedTaskEventStore.decodeCursor("tenant", "session", "task", old)).isInstanceOf(ManagedAgentException.class); + assertThatThrownBy(() -> com.alibaba.qwen.code.managedagent.repository.ManagedTaskEventStore.decodeCursor("tenant", "session", "task", old)).isInstanceOf(ManagedAgentException.class); } @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/CsiRuntimeMarkerProbeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/CsiRuntimeMarkerProbeTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/CsiRuntimeMarkerProbeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/CsiRuntimeMarkerProbeTest.java index ed159f17618..c366b0c9866 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/CsiRuntimeMarkerProbeTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/CsiRuntimeMarkerProbeTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiFilesRetirementGuard; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiFilesRetirementGuard; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExecutedQueryPlan.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExecutedQueryPlan.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExecutedQueryPlan.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExecutedQueryPlan.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExecutedQueryPlanTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExecutedQueryPlanTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExecutedQueryPlanTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExecutedQueryPlanTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationTest.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationTest.java index ca0842b4d63..f289e221f57 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ExternalStorageRegistrationTest.java @@ -5,11 +5,12 @@ import com.alibaba.qwen.code.managedagent.api.ExternalStorageRegistrationControl import com.alibaba.qwen.code.managedagent.api.RequestIdFilter; import com.alibaba.qwen.code.managedagent.api.TenantContextFilter; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.persistence.ExternalStorageRegistration.ExternalStorageIdentity; -import com.alibaba.qwen.code.managedagent.persistence.ExternalStorageRegistration.StorageAlias; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ExternalStorageRegistration; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ExternalStorageRegistration.ExternalStorageIdentity; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ExternalStorageRegistration.StorageAlias; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,7 +37,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -class ExternalStorageRegistrationTest { +public class ExternalStorageRegistrationTest { private static final String ROUTE = "/internal/managed-workspaces/v1/storage:register-external"; private final ObjectMapper json = new ObjectMapper(); private ManagedSchemaJdbcTemplate jdbc; @@ -46,13 +47,13 @@ class ExternalStorageRegistrationTest { private ServerProperties server; private MockMvc mvc; - DataSource source() { + public DataSource source() { return new DriverManagerDataSource("jdbc:h2:mem:external-storage-" + UUID.randomUUID() + ";MODE=MySQL;DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE", "sa", ""); } @BeforeEach - void setUp() throws Exception { + public void setUp() throws Exception { var source = source(); ManagedSchema.initialize(source); jdbc = new ManagedSchemaJdbcTemplate(source); @@ -68,7 +69,8 @@ class ExternalStorageRegistrationTest { } @Test - void registrationSurvivesRestartAndCorrelationChangesWithoutMutatingOwnership() throws Exception { + public void registrationSurvivesRestartAndCorrelationChangesWithoutMutatingOwnership() + throws Exception { var registration = registration("provider", "volume", "workspace", "/aliases/one"); String body = body(registration, "key", "request-one"); var first = mvc.perform(operator().content(body)).andExpect(status().isOk()) @@ -95,7 +97,7 @@ class ExternalStorageRegistrationTest { } @Test - void conflictsNeverRebindAliasesPhysicalOwnersOrIdempotencyKeys() { + public void conflictsNeverRebindAliasesPhysicalOwnersOrIdempotencyKeys() { var original = registration("provider", "volume", "workspace", "/aliases/one"); store.registerExternalStorage("tenant", "key", original); var owner = jdbc.queryForMap("SELECT * FROM ma_storage_owner"); @@ -121,7 +123,7 @@ class ExternalStorageRegistrationTest { } @Test - void concurrentCrossTenantAliasRegistrationHasExactlyOneWinner() throws Exception { + public void concurrentCrossTenantAliasRegistrationHasExactlyOneWinner() throws Exception { var start = new CountDownLatch(1); try (var pool = Executors.newFixedThreadPool(2)) { var attempts = List.of("one", "two").stream().map(tenant -> pool.submit(() -> { @@ -143,7 +145,7 @@ class ExternalStorageRegistrationTest { } @Test - void externalRegistrationCannotEnterLocalExecutionAndCannotAdoptLegacyAliases() { + public void externalRegistrationCannotEnterLocalExecutionAndCannotAdoptLegacyAliases() { for (String kind : List.of("LOCAL", "UNREGISTERED")) { String alias = JdbcRuntimeBindingRepository.storageAliasKey("tenant", kind); jdbc.update("INSERT INTO ma_storage (alias_key, tenant_key, storage_key, tenant_id, storage_id, storage_kind) VALUES (?, ?, ?, ?, ?, ?)", @@ -165,7 +167,8 @@ class ExternalStorageRegistrationTest { } @Test - void onlyLoopbackOperatorCanRegisterAndForwardedHeadersGrantNoAccess() throws Exception { + public void onlyLoopbackOperatorCanRegisterAndForwardedHeadersGrantNoAccess() + throws Exception { String body = body(registration("provider", "volume", "workspace", "/aliases/one"), "key", "request"); mvc.perform(post(ROUTE).header("X-Qwen-Tenant-Id", "tenant").contentType("application/json").content(body)) .andExpect(status().isForbidden()).andExpect(header().string("Cache-Control", "no-store")); @@ -187,7 +190,8 @@ class ExternalStorageRegistrationTest { } @Test - void unsafePathsAndNonStorageIdentitiesAreRejectedWithoutLeakingInput() throws Exception { + public void unsafePathsAndNonStorageIdentitiesAreRejectedWithoutLeakingInput() + throws Exception { String valid = body(registration("provider", "volume", "workspace", "/aliases/one"), "key", "request"); for (String invalid : List.of("/", "/a/../b", "/a//b", "/a/", "relative", "C:\\private")) { var document = json.readTree(valid); @@ -206,7 +210,7 @@ class ExternalStorageRegistrationTest { } @Test - void theProvisionerReadPathResolvesOnlyTheTenantBoundAlias() { + public void theProvisionerReadPathResolvesOnlyTheTenantBoundAlias() { var registration = registration("provider", "volume", "workspace", "/aliases/one"); store.registerExternalStorage("tenant", "key", registration); int storage = jdbc.queryForObject("SELECT COUNT(*) FROM ma_storage", Integer.class); @@ -237,7 +241,7 @@ class ExternalStorageRegistrationTest { } @Test - void theProvisionerReadPathRefusesADurableRowThatDisagreesWithItself() { + public void theProvisionerReadPathRefusesADurableRowThatDisagreesWithItself() { store.registerExternalStorage("tenant", "key", registration("provider", "volume", "workspace", "/aliases/one")); // A revision or mount root that drifted away from the persisted JSON is @@ -250,7 +254,7 @@ class ExternalStorageRegistrationTest { } @Test - void aRetiredOrRetiringRegistrationResolvesEmpty() { + public void aRetiredOrRetiringRegistrationResolvesEmpty() { store.registerExternalStorage("tenant", "key", registration("provider", "volume", "workspace", "/aliases/one")); assertThat(store.find("tenant", "workspace")).isPresent(); @@ -264,7 +268,7 @@ class ExternalStorageRegistrationTest { } @Test - void aRegistrationRevisionMustStayInsideTheExactJsonRange() { + public void aRegistrationRevisionMustStayInsideTheExactJsonRange() { long max = ExternalStorageRegistration.ExternalStorageIdentity .MAX_REGISTRATION_REVISION; assertThat(max).isEqualTo(9_007_199_254_740_991L); @@ -275,16 +279,15 @@ class ExternalStorageRegistrationTest { .isInstanceOf(IllegalArgumentException.class) .hasMessage("Invalid external storage registration."); // The registry record mirrors the same bound without importing the DTO. - assertThat(com.alibaba.qwen.code.runtimebroker.provisioning.ExternalStorageRegistry + assertThat(com.alibaba.qwen.code.runtimebroker.spi.ExternalStorageRegistry .RegisteredExternalStorage.MAX_REGISTRATION_REVISION).isEqualTo(max); - assertThatThrownBy(() -> new com.alibaba.qwen.code.runtimebroker.provisioning - .ExternalStorageRegistry.RegisteredExternalStorage("provider", "volume", + assertThatThrownBy(() -> new com.alibaba.qwen.code.runtimebroker.spi.ExternalStorageRegistry.RegisteredExternalStorage("provider", "volume", max + 1, "workspace", "/aliases/one", "/worker/workspace")) .isInstanceOf(IllegalArgumentException.class); } @Test - void anUnreadableRegistrationIsRetryableInsteadOfPermanent() { + public void anUnreadableRegistrationIsRetryableInsteadOfPermanent() { store.registerExternalStorage("tenant", "key", registration("provider", "volume", "workspace", "/aliases/one")); jdbc.update("UPDATE ma_storage SET registration_json = 'not-json'"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/FaultingExtensionRecordStore.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/FaultingExtensionRecordStore.java similarity index 79% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/FaultingExtensionRecordStore.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/FaultingExtensionRecordStore.java index 02c1f74664f..1e8b90874e8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/FaultingExtensionRecordStore.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/FaultingExtensionRecordStore.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredResource; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -32,9 +32,15 @@ public class FaultingExtensionRecordStore } @Override - ApplyResult apply(String tenantId, String workspaceId, String sessionId, - long firstSequence, int eventCount, byte[] recordBytes, - Function resources, long committedAt) { + public ApplyResult apply( + String tenantId, + String workspaceId, + String sessionId, + long firstSequence, + int eventCount, + byte[] recordBytes, + Function resources, + long committedAt) { gate.inspect(recordBytes, resources); ApplyResult result = super.apply(tenantId, workspaceId, sessionId, firstSequence, eventCount, recordBytes, resources, @@ -44,9 +50,14 @@ public class FaultingExtensionRecordStore } @Override - ApplyResult applyNative(String tenantId, String workspaceId, - String sessionId, long firstSequence, int eventCount, - byte[] recordBytes, Function resources, + public ApplyResult applyNative( + String tenantId, + String workspaceId, + String sessionId, + long firstSequence, + int eventCount, + byte[] recordBytes, + Function resources, long committedAt) { gate.inspect(recordBytes, resources); ApplyResult result = super.applyNative(tenantId, workspaceId, diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStoreTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStoreTest.java index 898416ba0ea..a84327a1773 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HarnessPlacementStoreTest.java @@ -1,10 +1,11 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import org.apache.commons.codec.digest.DigestUtils; import org.h2.jdbcx.JdbcDataSource; @@ -56,7 +57,7 @@ public class HarnessPlacementStoreTest { jdbc = new ReleaseFaultJdbc(source); tx = new TransactionTemplate(new DataSourceTransactionManager(source)); placements = new HarnessPlacementStore(jdbc); - store = new ManagedSessionStore(jdbc); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var properties = new ManagedAgentProperties(); properties.getSessionStore().setBindingKey("0123456789abcdef0123456789abcdef"); credentials = new WriterCredentialPolicy(properties); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStoreTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStoreTest.java index 50eeef2ec19..c78d48c545e 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedCleanupDebtStoreTest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -151,13 +151,13 @@ class HostedCleanupDebtStoreTest { + " byte_length, sha256, storage_kind, inline_bytes, state, gmt_create)" + " VALUES (?, ?, ?, ?, ?, ?, 'hosted-turn-cleanup', 1, ?, ?," + " ?, ?, 'REFERENCED', ?)", - resourceKey, ManagedSessionStore.sessionScopeKey(tenant, session), tenant, + resourceKey, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), tenant, workspace, session, resourceId, body.length, "d", storageKind, body, gmtCreate); jdbc.update("INSERT INTO ma_resource_ref (session_scope_key, owner_kind, owner_key," + " owner_revision, resource_key, tenant_id, workspace_id, session_id," + " journal_revision, resource_id, gmt_create, runtime_binding_key)" + " VALUES (?, 'HOSTED_CLEANUP', ?, 1, ?, ?, ?, ?, 1, ?, ?, ?)", - ManagedSessionStore.sessionScopeKey(tenant, session), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), ManagedSchemaKeys.key("hosted-cleanup", prompt), resourceKey, tenant, workspace, session, resourceId, gmtCreate, "binding-" + UUID.randomUUID()); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceConcurrencyIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceConcurrencyIT.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceConcurrencyIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceConcurrencyIT.java index 825e17d4524..ee12b7c028e 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceConcurrencyIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceConcurrencyIT.java @@ -2,22 +2,23 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.EmbeddedRuntimeBroker; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedRuntimeBroker; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.http.RuntimeBrokerHttpServer; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; -import com.alibaba.qwen.code.runtimebroker.transport.RuntimeBrokerHttpServer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.tools.Server; @@ -424,8 +425,8 @@ class HostedWorkspaceConcurrencyIT { private static ManagedAgentStore store(DriverManagerDataSource source, ManagedAgentProperties properties) { var jdbc = new ManagedSchemaJdbcTemplate(source); - return new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } private static ManagedAgentProperties properties(String tenant, Path root, Path state) { @@ -454,14 +455,14 @@ class HostedWorkspaceConcurrencyIT { var jdbc = new ManagedSchemaJdbcTemplate(source); var manager = new DataSourceTransactionManager(source); if (LINUX) { - return new WorkspaceStorageGuard(jdbc, manager, properties); + return new WorkspaceStorageGuard(jdbc, manager, properties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()); } return new WorkspaceStorageGuard(jdbc, manager, properties, root -> { var attributes = Files.readAttributes(root, BasicFileAttributes.class); if (!attributes.isDirectory() || !root.equals(root.toRealPath())) { throw new java.io.IOException("Invalid root"); } - return new WorkspaceStorageGuard.Identity(root.toString(), "synthetic-host", "synthetic-device", + return new StorageIdentityReader.Identity(root.toString(), "synthetic-host", "synthetic-device", attributes.fileKey().toString(), attributes.creationTime().toInstant().toString()); }); } @@ -643,7 +644,7 @@ class HostedWorkspaceConcurrencyIT { var bindings = new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("test", new byte[32])); var sessions = new JdbcRuntimeSessionRepository(source); var executions = new JdbcToolExecutionRepository(source); - var authority = new WorkspaceExecutionStore(new ManagedSchemaJdbcTemplate(source), new DataSourceTransactionManager(source), guard) { + var authority = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(new ManagedSchemaJdbcTemplate(source), new DataSourceTransactionManager(source), guard) { private void pause(String stage) { try { Path armed = directory.resolve("armed"); @@ -682,7 +683,7 @@ class HostedWorkspaceConcurrencyIT { pause("after-lost-release"); super.releaseLost(binding); } - }; + }); try (var broker = new EmbeddedRuntimeBroker(store(source, properties), properties, bindings, sessions, executions, authority); var input = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8))) { publish(directory.resolve("ready.json"), Map.of("uri", broker.getBaseUri().toString())); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceStorageGuardMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceStorageGuardMySqlIT.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceStorageGuardMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceStorageGuardMySqlIT.java index 0d471ef4712..9835df8a0d6 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceStorageGuardMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/HostedWorkspaceStorageGuardMySqlIT.java @@ -2,6 +2,7 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount; +import com.alibaba.qwen.code.managedagent.maintenance.WorkspaceStorageRegistrationMain; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; @@ -52,7 +53,7 @@ class HostedWorkspaceStorageGuardMySqlIT { properties.getRuntimeBroker().setVerifiedWorkspaceRecoveryEnabled(true); properties.getRuntimeBroker().setWorkspaceMounts(List.of(new WorkspaceMount(tenant, storage, root.toString()))); var binding = new ContextBinding(tenant, "workspace", 1, storage, "child", "config", 1); - var guard = new WorkspaceStorageGuard(jdbc, new DataSourceTransactionManager(dataSource), properties); + var guard = new WorkspaceStorageGuard(jdbc, new DataSourceTransactionManager(dataSource), properties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()); String registration = UUID.randomUUID().toString(); try { assertUnavailable(() -> guard.verify(binding)); @@ -71,7 +72,7 @@ class HostedWorkspaceStorageGuardMySqlIT { var first = executor.submit(() -> guard.fence(tenant, storage, 1, operation)); var otherSource = new DriverManagerDataSource(url, user, password); var other = new WorkspaceStorageGuard(new ManagedSchemaJdbcTemplate(otherSource), - new DataSourceTransactionManager(otherSource), properties); + new DataSourceTransactionManager(otherSource), properties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()); var second = executor.submit(() -> other.fence(tenant, storage, 1, operation)); first.get(10, TimeUnit.SECONDS); second.get(10, TimeUnit.SECONDS); @@ -87,7 +88,7 @@ class HostedWorkspaceStorageGuardMySqlIT { Files.copy(temporary.resolve("previous/.qwen-managed-storage.json"), root.resolve(".qwen-managed-storage.json")); Files.setLastModifiedTime(root, FileTime.fromMillis(2)); - var restarted = new WorkspaceStorageGuard(jdbc, new DataSourceTransactionManager(dataSource), properties); + var restarted = new WorkspaceStorageGuard(jdbc, new DataSourceTransactionManager(dataSource), properties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()); assertUnavailable(() -> restarted.verify(binding)); assertUnavailable(() -> restarted.restoreOriginal(tenant, storage, 1, operation)); assertThat(restarted.inspect(tenant, storage)).contains("revision=2", "identity=mismatch"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/IsolatedManagedSchemaMySqlTestDatabase.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/IsolatedManagedSchemaMySqlTestDatabase.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/IsolatedManagedSchemaMySqlTestDatabase.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/IsolatedManagedSchemaMySqlTestDatabase.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageAliasMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageAliasMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageAliasMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageAliasMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageAliasTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageAliasTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageAliasTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageAliasTest.java index 207ca31e276..6bf3e836ff9 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageAliasTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/LocalStorageAliasTest.java @@ -2,12 +2,14 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -86,7 +88,7 @@ class LocalStorageAliasTest { String aliasKey = JdbcRuntimeBindingRepository.storageAliasKey("tenant", "storage"); assertThat(jdbc.queryForObject("SELECT gmt_create FROM ma_storage WHERE alias_key = ?", Long.class, aliasKey)).isPositive(); - String registrationOperation = WorkspaceRecoveryStore.parse(jdbc.queryForObject( + String registrationOperation = com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.parse(jdbc.queryForObject( "SELECT registration_json FROM ma_storage WHERE alias_key = ?", String.class, aliasKey)) .path("operationId").asText(); jdbc.update("UPDATE ma_storage SET storage_kind = 'UNREGISTERED', gmt_modified = 1 WHERE alias_key = ?", @@ -139,7 +141,7 @@ class LocalStorageAliasTest { var secondRow = children.admit("other-tenant", "parent", "run", second, System.currentTimeMillis()); var firstClaim = children.claim(firstRow, "first-worker", System.currentTimeMillis(), 60_000); var secondClaim = children.claim(secondRow, "second-worker", System.currentTimeMillis(), 60_000); - var authority = new WorkspaceExecutionStore(jdbc, manager); + var authority = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, manager)); String step = authority.holdForMaintenance(first, firstRow.childWorkspaceId(), firstClaim.claimGeneration()); assertThatThrownBy(() -> authority.holdForMaintenance(second, secondRow.childWorkspaceId(), secondClaim.claimGeneration())) .isInstanceOfSatisfying(RuntimeBrokerException.class, error -> assertThat(error.getCode()).isEqualTo("workspace_busy")); @@ -201,7 +203,7 @@ class LocalStorageAliasTest { Path different = Files.createDirectory(temporary.resolve("different")).toRealPath(); properties.getRuntimeBroker().setWorkspaceMounts(List.of(new WorkspaceMount("third", "different", different.toString()))); var attributes = Files.readAttributes(root, java.nio.file.attribute.BasicFileAttributes.class); - var collision = new WorkspaceStorageGuard(jdbc, manager, properties, path -> new WorkspaceStorageGuard.Identity( + var collision = new WorkspaceStorageGuard(jdbc, manager, properties, path -> new StorageIdentityReader.Identity( path.toString(), "host", "device", attributes.fileKey().toString(), attributes.creationTime().toInstant().toString())); assertThatThrownBy(() -> collision.register("third", "different", UUID.randomUUID().toString())).isInstanceOf(RuntimeBrokerException.class); assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ma_storage_owner", Integer.class)).isEqualTo(1); @@ -213,7 +215,7 @@ class LocalStorageAliasTest { void aSeededFailedBindingStillProtectsTheOtherAlias() { var source = java.util.Objects.requireNonNull(jdbc.getDataSource()); var repository = new JdbcRuntimeBindingRepository(source, - new com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector("test", new byte[32])); + new com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector("test", new byte[32])); var request = new com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest( new com.alibaba.qwen.code.runtimebroker.RuntimeScope("other-tenant", "workspace", "1", root.toString(), "sha256:" + "a".repeat(64), "workspace"), null, "local-process", "other-storage"); @@ -269,14 +271,14 @@ class LocalStorageAliasTest { String session = "retired-private"; session(tenant, "other-storage", session); var transactions = new TransactionTemplate(manager); - var journal = new ManagedSessionStore(jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String token = "a".repeat(64); byte[] bytes = ("{\"subtype\":\"session_execution_engine\"}\n{\"subtype\":\"managed_session_header_v1\"}\n").getBytes(java.nio.charset.StandardCharsets.UTF_8); transactions.execute(status -> journal.acquireWriter(tenant, session, token, new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "original", 30_000L))); transactions.execute(status -> journal.commit(tenant, session, token, new ManagedSessionStoreModels.CommitTransactionRequest( - "workspace", "original", 1, 0, 0, "genesis", "session.create", "genesis-command", WorkspaceRecoveryStore.hash(bytes), - 0, 0, 0, null, null, null, 0, null, 2, java.util.Base64.getEncoder().encodeToString(bytes), WorkspaceRecoveryStore.hash(bytes), List.of()))); + "workspace", "original", 1, 0, 0, "genesis", "session.create", "genesis-command", com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(bytes), + 0, 0, 0, null, null, null, 0, null, 2, java.util.Base64.getEncoder().encodeToString(bytes), com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(bytes), List.of()))); transactions.execute(status -> journal.sealWriter(tenant, session, token, new ManagedSessionStoreModels.SealWriterRequest("workspace", "original", 1))); transactions.executeWithoutResult(status -> { @@ -308,7 +310,7 @@ class LocalStorageAliasTest { void anUnknownBindingStateCannotCertifyAnotherAliasesPhysicalStop() { var source = java.util.Objects.requireNonNull(jdbc.getDataSource()); var repository = new JdbcRuntimeBindingRepository(source, - new com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector("test", new byte[32])); + new com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector("test", new byte[32])); var request = new com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest( new com.alibaba.qwen.code.runtimebroker.RuntimeScope("other-tenant", "workspace", "1", root.toString(), "sha256:" + "a".repeat(64), "workspace"), null, "local-process", "other-storage"); @@ -347,7 +349,7 @@ class LocalStorageAliasTest { private WorkspaceStorageGuard guard() { return new WorkspaceStorageGuard(jdbc, manager, properties, path -> { var attributes = Files.readAttributes(path, java.nio.file.attribute.BasicFileAttributes.class); - return new WorkspaceStorageGuard.Identity(path.toString(), "host", "device", attributes.fileKey().toString(), + return new StorageIdentityReader.Identity(path.toString(), "host", "device", attributes.fileKey().toString(), attributes.creationTime().toInstant().toString()); }); } @@ -359,7 +361,7 @@ class LocalStorageAliasTest { private void session(String tenant, String storage, String session) { jdbc.update("INSERT INTO ma_session (tenant_id, session_id, session_kind, workspace_storage_id, tenant_key, session_key, session_scope_key, gmt_create, gmt_modified)" + " VALUES (?, ?, 'CONTROL', ?, ?, ?, ?, 1, 1)", tenant, session, storage, - JdbcRuntimeBindingRepository.harnessDrainKey(tenant), JdbcRuntimeBindingRepository.harnessDrainKey(session), ManagedSessionStore.sessionScopeKey(tenant, session)); + JdbcRuntimeBindingRepository.harnessDrainKey(tenant), JdbcRuntimeBindingRepository.harnessDrainKey(session), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session)); } private static void assertUncovered(Runnable action) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStoreTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStoreTest.java index 78ab2aac6d6..da7db17eb1a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedActionStoreTest.java @@ -1,14 +1,14 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredResource; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -17,7 +17,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.support.TransactionTemplate; @@ -61,7 +60,7 @@ class ManagedActionStoreTest { jdbc = new ManagedSchemaJdbcTemplate(source); transactions = new TransactionTemplate( new DataSourceTransactionManager(source)); - scope = ManagedSessionStore.sessionScopeKey(TENANT, SESSION); + scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, SESSION); jdbc.update("INSERT INTO ma_session (tenant_id,session_id,agent_id,status,approval_mode,gmt_create,gmt_modified)" + " VALUES (?,?,'agent','ACTIVE','manual',0,0)", TENANT, SESSION); agents = mock(AgentStateStore.class); @@ -175,9 +174,9 @@ class ManagedActionStoreTest { // The resurrection arm's evidence that a new delivery must wait. jdbc.update("INSERT INTO ma_storage (alias_key, tenant_key, storage_key," + " tenant_id, storage_id, fence_operation_id) VALUES (?, ?, ?, ?, ?, ?)", - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey(TENANT, "storage"), - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageFenceKey(TENANT), - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageFenceKey("storage"), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey(TENANT, "storage"), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageFenceKey(TENANT), + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageFenceKey("storage"), TENANT, "storage", "op-fence"); var replayed = admitAs("owner", "idem", "digest", response("allow"), 20); @@ -347,7 +346,7 @@ class ManagedActionStoreTest { .isInstanceOf(ManagedAgentException.class) .satisfies(error -> { var refusal = (ManagedAgentException) error; - assertThat(refusal.getStatus()).isEqualTo(HttpStatus.FORBIDDEN); + assertThat(refusal.getStatus()).isEqualTo(403); assertThat(refusal.getCode()).isEqualTo("action_forbidden"); }); assertThat(stringColumn("state", operationId)).isEqualTo("FAILED"); @@ -380,7 +379,7 @@ class ManagedActionStoreTest { .isInstanceOf(ManagedAgentException.class) .satisfies(error -> { var refusal = (ManagedAgentException) error; - assertThat(refusal.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(refusal.getStatus()).isEqualTo(409); assertThat(refusal.getCode()).isEqualTo("workspace_unavailable"); }); assertThat(stringColumn("state", operationId)).isEqualTo("FAILED"); @@ -414,7 +413,7 @@ class ManagedActionStoreTest { .isInstanceOf(ManagedAgentException.class) .satisfies(error -> { var refusal = (ManagedAgentException) error; - assertThat(refusal.getStatus()).isEqualTo(HttpStatus.FORBIDDEN); + assertThat(refusal.getStatus()).isEqualTo(403); assertThat(refusal.getCode()).isEqualTo("action_forbidden"); }); assertThat(stringColumn("state", operationId)).isEqualTo("FAILED"); @@ -542,13 +541,13 @@ class ManagedActionStoreTest { + " context_revision = 1, workspace_config_ref = 'config'," + " workspace_policy_ref = 'policy', owner_actor_key = ?" + " WHERE tenant_id = ? AND session_id = ?", - ManagedWorkspaceRegistry.actorKey(TENANT, "owner"), TENANT, SESSION); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(TENANT, "owner"), TENANT, SESSION); } private void grantRole(String actor, String role) { jdbc.update("INSERT INTO ma_workspace_access (tenant_id, workspace_id," + " actor_id, role) VALUES (?, 'workspace', ?, ?)", - TENANT, ManagedWorkspaceRegistry.actorKey(TENANT, actor), role); + TENANT, com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(TENANT, actor), role); } private String stringColumn(String name, String operationId) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentCsiMutationMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentCsiMutationMySqlIT.java similarity index 90% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentCsiMutationMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentCsiMutationMySqlIT.java index 2ffd4b96129..d8cdfab0bf8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentCsiMutationMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentCsiMutationMySqlIT.java @@ -1,8 +1,10 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -10,7 +12,6 @@ import org.flywaydb.core.Flyway; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DriverManagerDataSource; @@ -68,9 +69,9 @@ class ManagedAgentCsiMutationMySqlIT { CsiFilesRetirementProfile.CONFIG_REF, CsiFilesRetirementProfile.POLICY_REF); jdbc.update( "INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" - + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); String ordinary = "!ordinary-retired"; - String scope = ManagedSessionStore.sessionScopeKey("tenant", ordinary); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", ordinary); jdbc.update( "INSERT INTO ma_session (tenant_id, session_id, agent_id, status, gmt_create, gmt_modified) VALUES ('tenant', ?, 'qwen-code', 'DELETED', 1, 1)", ordinary); jdbc.update( @@ -85,10 +86,9 @@ class ManagedAgentCsiMutationMySqlIT { long now = ToolPublicationRetentionStore.now(jdbc); jdbc.update( "INSERT INTO ma_operation (operation_key, session_scope_key, scope_key, operation_id, operation_kind, tenant_id, session_id, target_kind, target_key, request_digest, request_json, phase, state, gmt_create, gmt_modified) VALUES (?, ?, ?, ?, 'SESSION_RESOURCE_GC', 'tenant', ?, 'SESSION', ?, ?, '{}', 'COLLECTING', 'PENDING', ?, ?)", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys .operationKey( - com.alibaba.qwen.code.runtimebroker.managedschema - .ManagedSchemaContract.OperationKind + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind .SESSION_RESOURCE_GC, scope, scope, @@ -98,7 +98,7 @@ class ManagedAgentCsiMutationMySqlIT { operationId, ordinary, scope, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( operationId), now, now); @@ -126,8 +126,8 @@ class ManagedAgentCsiMutationMySqlIT { }; try (var pool = Executors.newFixedThreadPool(2)) { try { - var creation = pool.submit(() -> WorkspaceCsiSessionMain.create(creationJdbc, manager, json, properties, - new WorkspaceCsiSessionMain.Request(registration, "actor", "create", null, null, + var creation = pool.submit(() -> com.alibaba.qwen.code.managedagent.persistence.PrivateCsiFixture.create(creationJdbc, manager, json, properties, + new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request(registration, "actor", "create", null, null, new WorkspaceSelection("workspace", ".")))); assertThat(inserting.await(5, TimeUnit.SECONDS)).isTrue(); var collection = pool.submit(() -> page ? collector.collect(claim) : collector.claim()); @@ -194,9 +194,9 @@ class ManagedAgentCsiMutationMySqlIT { CsiFilesRetirementProfile.CONFIG_REF, CsiFilesRetirementProfile.POLICY_REF); jdbc.update( "INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" - + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", ManagedWorkspaceRegistry.actorKey("tenant", "actor")); - var store = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var transaction = new TransactionTemplate(manager); transaction.setIsolationLevel(isolation); transaction.setTimeout(15); @@ -220,8 +220,8 @@ class ManagedAgentCsiMutationMySqlIT { })); try { assertThat(warmed.await(5, TimeUnit.SECONDS)).isTrue(); - var original = WorkspaceCsiSessionMain.create(jdbc, manager, json, properties, - new WorkspaceCsiSessionMain.Request(registration, "actor", "original", null, null, + var original = com.alibaba.qwen.code.managedagent.persistence.PrivateCsiFixture.create(jdbc, manager, json, properties, + new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request(registration, "actor", "original", null, null, new WorkspaceSelection("workspace", "."))); session.set(original.sessionId()); var before = allTables(jdbc, json); @@ -250,7 +250,7 @@ class ManagedAgentCsiMutationMySqlIT { .satisfies(error -> { assertThat(error.getCause()).isInstanceOf(ManagedAgentException.class); var refusal = (ManagedAgentException) error.getCause(); - assertThat(refusal.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(refusal.getStatus()).isEqualTo(409); assertThat(refusal.getCode()).isEqualTo("csi_managed_mutation_unavailable"); }); assertThat(allTables(jdbc, json)).isEqualTo(before); @@ -296,8 +296,8 @@ class ManagedAgentCsiMutationMySqlIT { CsiFilesRetirementProfile.CONFIG_REF, CsiFilesRetirementProfile.POLICY_REF); jdbc.update( "INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" - + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", ManagedWorkspaceRegistry.actorKey("tenant", "actor")); - var journal = new ManagedSessionStore(jdbc); + + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var transaction = new TransactionTemplate(manager); transaction.setIsolationLevel(isolation); transaction.setTimeout(15); @@ -321,8 +321,8 @@ class ManagedAgentCsiMutationMySqlIT { })); try { assertThat(warmed.await(5, TimeUnit.SECONDS)).isTrue(); - session.set(WorkspaceCsiSessionMain.create(jdbc, manager, json, properties, - new WorkspaceCsiSessionMain.Request(registration, "actor", "original", null, null, + session.set(com.alibaba.qwen.code.managedagent.persistence.PrivateCsiFixture.create(jdbc, manager, json, properties, + new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request(registration, "actor", "original", null, null, new WorkspaceSelection("workspace", "."))).sessionId()); var before = allTables(jdbc, json); created.countDown(); @@ -381,11 +381,11 @@ class ManagedAgentCsiMutationMySqlIT { CsiFilesRetirementProfile.CONFIG_REF, CsiFilesRetirementProfile.POLICY_REF); jdbc.update( "INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" - + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", ManagedWorkspaceRegistry.actorKey("tenant", "actor")); - String session = WorkspaceCsiSessionMain.create(jdbc, manager, json, properties, - new WorkspaceCsiSessionMain.Request(registration, "actor", "original", null, null, + + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + String session = com.alibaba.qwen.code.managedagent.persistence.PrivateCsiFixture.create(jdbc, manager, json, properties, + new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request(registration, "actor", "original", null, null, new WorkspaceSelection("workspace", "."))).sessionId(); - var journal = new ManagedSessionStore(jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var transaction = new TransactionTemplate(manager); var hold = manager.getTransaction(new DefaultTransactionDefinition()); try { @@ -411,7 +411,7 @@ class ManagedAgentCsiMutationMySqlIT { .satisfies(error -> { assertThat(error.getCause()).isInstanceOf(ManagedAgentException.class); var refusal = (ManagedAgentException) error.getCause(); - assertThat(refusal.getStatus()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + assertThat(refusal.getStatus()).isEqualTo(503); assertThat(refusal.getCode()).isEqualTo("tenant_guard_contended"); }); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentEventSequenceConcurrencyMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentEventSequenceConcurrencyMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentEventSequenceConcurrencyMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentEventSequenceConcurrencyMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentEventSequenceConcurrencyTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentEventSequenceConcurrencyTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentEventSequenceConcurrencyTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentEventSequenceConcurrencyTest.java index e776021d16b..6901d273f0d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentEventSequenceConcurrencyTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentEventSequenceConcurrencyTest.java @@ -54,9 +54,9 @@ class ManagedAgentEventSequenceConcurrencyTest { jdbc = new ManagedSchemaJdbcTemplate(source); transactions = new TransactionTemplate( new DataSourceTransactionManager(source)); - store = new ManagedAgentStore(jdbc, new ObjectMapper(), + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } @ParameterizedTest diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStoreChildCreationFenceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStoreChildCreationFenceTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStoreChildCreationFenceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStoreChildCreationFenceTest.java index f3c34718de2..85d3c1aa50f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStoreChildCreationFenceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentStoreChildCreationFenceTest.java @@ -1,9 +1,10 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.fasterxml.jackson.databind.ObjectMapper; @@ -45,8 +46,8 @@ class ManagedAgentStoreChildCreationFenceTest { jdbc = new ManagedSchemaJdbcTemplate(source); var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - store = new ManagedAgentStore(jdbc, new ObjectMapper(), - Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), + Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); jdbc.update("INSERT INTO ma_workspace (tenant_id," + " workspace_id, workspace_generation, storage_id," + " display_name, config_ref, policy_ref, state)" @@ -82,7 +83,7 @@ class ManagedAgentStoreChildCreationFenceTest { + " VALUES ('DOMAIN', ?, ?, 'tenant', 'workspace', ?," + " 'child_run', ?, 'h', 1, ?, 'child_agent', ?," + " 'session', ?, 1)", - ManagedSessionStore.sessionScopeKey("tenant", parent), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", parent), ManagedExtensionProjection.recordKey(parent, "child_run", childRunId), parent, childRunId, "resource-" + childRunId, taskState, diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentTurnDeferralTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentTurnDeferralTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentTurnDeferralTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentTurnDeferralTest.java index a5e2ca494df..8bfb2990160 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentTurnDeferralTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedAgentTurnDeferralTest.java @@ -30,10 +30,10 @@ class ManagedAgentTurnDeferralTest { + ";LOCK_TIMEOUT=10000"); ManagedSchema.initialize(source); JdbcTemplate jdbc = new ManagedSchemaJdbcTemplate(source); - ManagedAgentStore store = new ManagedAgentStore(jdbc, + ManagedAgentStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), events -> {}, org.mockito.Mockito.mock(ManagedWorkspaceRegistry.class), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String tenant = "tenant-defer"; Admission session = store.insertSessionCommand(tenant, diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunchTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunchTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunchTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunchTest.java index 09ad516427f..74ae86433b5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunchTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionAgentLaunchTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.runtimebroker.ManagedExtensionExecutionProfile; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordLifecycleGateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordLifecycleGateTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordLifecycleGateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordLifecycleGateTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileMySqlIT.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileMySqlIT.java index 90d6968a705..600bc28d431 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileMySqlIT.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.fasterxml.jackson.databind.JsonNode; @@ -52,7 +53,7 @@ class ManagedExtensionRecordVerdictReconcileMySqlIT { + " VALUES (?, ?, ?, 'workspace', ?, 'child_run'," + " ?, 'h', 1, 'res-x', 'child_agent', 'running'," + " 'session', 'planned', 1)", - ManagedSessionStore.sessionScopeKey(TENANT, parent), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, parent), ManagedExtensionProjection.recordKey(parent, "child_run", runId), TENANT, parent, diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileTest.java index b651abad823..308341ed854 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedExtensionRecordVerdictReconcileTest.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -53,7 +54,7 @@ class ManagedExtensionRecordVerdictReconcileTest { + " VALUES (?, ?, ?, 'workspace', ?, 'DOMAIN', 'child_run'," + " 'run-1', 'h', 1, 'res-x', 'child_agent'," + " 'running', 'session', 'planned', 1)", - ManagedSessionStore.sessionScopeKey(TENANT, sessionId), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, sessionId), ManagedExtensionProjection.recordKey(sessionId, "child_run", "run-1"), TENANT, sessionId); return sessionId; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessagePagingMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessagePagingMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessagePagingMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessagePagingMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessagePagingTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessagePagingTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessagePagingTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessagePagingTest.java index eb297d7673b..d2962d36a36 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessagePagingTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessagePagingTest.java @@ -2,14 +2,15 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; +import com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules; import com.alibaba.qwen.code.managedagent.model.StoreModels; +import com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionDefinition; @@ -43,7 +44,7 @@ class ManagedMessagePagingTest { ManagedSchema.initialize(source); jdbc = new CountingJdbc(source); store = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new SdkHarnessRules()); view = new TransactionTemplate(new DataSourceTransactionManager(source)); view.setReadOnly(true); view.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ); @@ -116,7 +117,7 @@ class ManagedMessagePagingTest { assertThat(read(null, 100, true).coveredSequence()).isEqualTo(2); assertThatThrownBy(() -> read(null, 100, false)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + assertThat(error.getStatus()).isEqualTo(503); assertThat(error.getCode()).isEqualTo("message_projection_pending"); }).hasMessageContaining("pending messages"); } @@ -128,7 +129,7 @@ class ManagedMessagePagingTest { + " WHERE tenant_id=? AND session_id=?", "tenant", "session"); assertThatThrownBy(() -> read(null, 100, true)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + assertThat(error.getStatus()).isEqualTo(503); assertThat(error.getCode()).isEqualTo("message_projection_pending"); }).hasMessageContaining("pending messages"); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessageSnapshotRemovalTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessageSnapshotRemovalTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessageSnapshotRemovalTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessageSnapshotRemovalTest.java index 82633391ec6..f021251f063 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessageSnapshotRemovalTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedMessageSnapshotRemovalTest.java @@ -1,8 +1,11 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.AfterEach; @@ -41,8 +44,8 @@ class ManagedMessageSnapshotRemovalTest { jdbc = new ManagedSchemaJdbcTemplate(source); manager = new DataSourceTransactionManager(source); transaction = new TransactionTemplate(manager); - store = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + store = VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new SdkHarnessRules())); session = transaction.execute(status -> store.insertSessionCommand(TENANT, "actor", "CREATE_SESSION", "create", "digest", "qwen-code", null, "title", List.of(), null)) .sessionId(); @@ -109,9 +112,9 @@ class ManagedMessageSnapshotRemovalTest { assertThat(jdbc.queryForObject("SELECT retention_until FROM ma_resource WHERE resource_key = ?", Long.class, oldBody)).isNotNull().isLessThanOrEqualTo(ToolPublicationRetentionStore.now(jdbc)); - String scope = ManagedSessionStore.sessionScopeKey(TENANT, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, session); String nativeId = "native-snapshot-manifest"; - String nativeKey = ManagedSessionStore.resourceKey(scope, nativeId); + String nativeKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, nativeId); byte[] nativeBytes = {123, 125}; jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, session_id," + " resource_id, kind, schema_version, byte_length, sha256, storage_kind," diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedModelContextRetentionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedModelContextRetentionTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedModelContextRetentionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedModelContextRetentionTest.java index abaae858433..aa29b0c7c29 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedModelContextRetentionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedModelContextRetentionTest.java @@ -1,8 +1,9 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -21,7 +22,7 @@ class ManagedModelContextRetentionTest { + ";MODE=MySQL;DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE"); ManagedSchema.initialize(source); var jdbc = new ManagedSchemaJdbcTemplate(source); - String scope = ManagedSessionStore.sessionScopeKey("tenant", "session"); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "session"); String record = ManagedExtensionProjection.recordKey("session", "hosted_context_usage", "latest"); String resource = ManagedSchemaKeys.key(scope, "resource"); byte[] body = "{}".getBytes(StandardCharsets.UTF_8); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeSnapshotRetirementTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeSnapshotRetirementTest.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeSnapshotRetirementTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeSnapshotRetirementTest.java index c4dcef317f8..5be0a63d80f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeSnapshotRetirementTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedNativeSnapshotRetirementTest.java @@ -21,7 +21,7 @@ class ManagedNativeSnapshotRetirementTest { ManagedSchema.initialize(source); var jdbc = new ManagedSchemaJdbcTemplate(source); var tx = new TransactionTemplate(new DataSourceTransactionManager(source)); - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var snapshots = new ManagedNativeJournalSnapshots(jdbc, sessions, new ObjectMapper()); for (String session : java.util.List.of("retiring", "protected")) { tx.executeWithoutResult(status -> new ExtensionRecordJournal(sessions, "tenant", "workspace", session).open()); @@ -31,7 +31,7 @@ class ManagedNativeSnapshotRetirementTest { } } jdbc.update("UPDATE ma_session SET retired_at = 1 WHERE tenant_id = ? AND session_id = ?", "tenant", "retiring"); - String scope = ManagedSessionStore.sessionScopeKey("tenant", "retiring"); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "retiring"); for (int remaining = 4; remaining > 0; remaining--) { assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ma_resource_ref WHERE session_scope_key = ?" + " AND owner_kind = 'NATIVE_SNAPSHOT'", Long.class, scope)).isEqualTo(remaining); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceiptScopeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceiptScopeTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceiptScopeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceiptScopeTest.java index b40c5f3db36..6d4a0227b63 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceiptScopeTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceiptScopeTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceActor; import com.fasterxml.jackson.databind.ObjectMapper; @@ -22,8 +22,8 @@ class ManagedReceiptScopeTest { ManagedSchema.initialize(fixture.source()); var jdbc = new ManagedSchemaJdbcTemplate(fixture.source()); var tx = new TransactionTemplate(new DataSourceTransactionManager(fixture.source())); - var store = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); for (int size : List.of(384, 385, 512)) { String tenant = "tenant-" + size; String actor = new WorkspaceActor(tenant, "😀".repeat(size)).getActorId(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceiptsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceiptsTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceiptsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedReceiptsTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuardMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuardMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuardMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuardMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuardTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuardTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuardTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuardTest.java index 5dbe9e628ac..685d156d9da 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuardTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedRuntimeRetentionGuardTest.java @@ -1,9 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeRetention; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeDrainReceipt; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; @@ -13,8 +11,12 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeRetention; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import org.flywaydb.core.Flyway; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.BeforeEach; @@ -511,7 +513,7 @@ class ManagedRuntimeRetentionGuardTest { jdbc.update( "UPDATE ma_session_record SET task_state = 'pending', settled_at = NULL" + " WHERE session_scope_key = ?", - ManagedSessionStore.sessionScopeKey("tenant", unsettled)); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", unsettled)); assertThat(guard.bindingReferenced(connection, binding("binding", 4))).isTrue(); jdbc.update("UPDATE ma_session_record SET task_state = 'succeeded', settled_at = 2"); assertThat(guard.bindingReferenced(connection, binding("binding", 4))).isFalse(); @@ -556,7 +558,7 @@ class ManagedRuntimeRetentionGuardTest { row.set(columns.indexOf("session_id"), owner); row.set( columns.indexOf("session_scope_key"), - ManagedSessionStore.sessionScopeKey("tenant", owner)); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", owner)); row.set(columns.indexOf("record_id"), id); row.set( columns.indexOf("record_key"), @@ -714,7 +716,7 @@ class ManagedRuntimeRetentionGuardTest { private void projection(String tenant, String session, String kind) { jdbc.update( "INSERT INTO ma_session_record (session_scope_key, record_key, tenant_id, workspace_id, session_id, domain, record_id, operation_hash, revision, record_resource_id, task_kind, task_state, runtime_state, delivery_target, delivery_state, gmt_create) VALUES (?, ?, ?, 'workspace', ?, 'child_run', 'run', ?, 1, 'resource', ?, 'pending', 'unbound', 'session', 'planned', 1)", - ManagedSessionStore.sessionScopeKey(tenant, session), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), ManagedExtensionProjection.recordKey(session, "child_run", "run"), tenant, session, diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaBusinessMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaBusinessMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaBusinessMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaBusinessMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaBusinessStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaBusinessStoreTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaBusinessStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaBusinessStoreTest.java index 8cf4122bcd4..a97db6cbc5f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaBusinessStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaBusinessStoreTest.java @@ -1,10 +1,13 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ChannelRouteRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; @@ -71,7 +74,7 @@ public class ManagedSchemaBusinessStoreTest { var references = new java.util.HashMap(); for (String ownerSession : List.of(session, session + " ")) { byte[] bytes = ownerSession.getBytes(java.nio.charset.StandardCharsets.UTF_8); - String scope = ManagedSessionStore.sessionScopeKey(tenant, ownerSession); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, ownerSession); references.put(ownerSession, new ObjectMapper().createObjectNode() .put("resourceId", resourceId).put("kind", "automation-definition") .put("schemaVersion", 1).put("byteLength", bytes.length) @@ -118,7 +121,7 @@ public class ManagedSchemaBusinessStoreTest { void anotherTenantDeliveryCannotHideAnIdenticallyNamedPendingMessage() { String foreignTenant = tenant + "-foreign"; for (String ownerTenant : List.of(tenant, foreignTenant)) { - String scope = ManagedSessionStore.sessionScopeKey(ownerTenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(ownerTenant, session); jdbc.update( "INSERT INTO ma_session_record (session_scope_key, record_key, tenant_id, workspace_id, session_id, domain, record_id, revision, record_resource_id, delivery_target, delivery_state, gmt_create) VALUES (?, ?, ?, 'workspace', ?, 'session_message', 'message', 1, 'body', 'session', 'planned', 0)", scope, ManagedExtensionProjection.recordKey(session, "session_message", "message"), ownerTenant, session); @@ -150,7 +153,7 @@ public class ManagedSchemaBusinessStoreTest { @Test void childResultReadsTheImmutableBodyOfALargeLatestMessage() throws Exception { new TransactionTemplate(manager).executeWithoutResult(status -> ToolPublicationRetentionStore.lockSession(jdbc, tenant, session)); - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); String text = "large-child-result-".repeat(4096); var json = new ObjectMapper(); var parts = json.createArrayNode(); @@ -267,7 +270,7 @@ public class ManagedSchemaBusinessStoreTest { var collector = new SessionResourceCollectionCollector(jdbc, manager, properties, () -> Duration.ofMinutes(2).toNanos()); var retention = new ToolPublicationRetentionStore(jdbc, manager); - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); byte[] bytes = "hello".getBytes(java.nio.charset.StandardCharsets.UTF_8); String resource = ManagedSchemaKeys.key(scope, "resource"); jdbc.update( @@ -307,7 +310,7 @@ public class ManagedSchemaBusinessStoreTest { var collector = new SessionResourceCollectionCollector(jdbc, manager, properties, () -> Duration.ofMinutes(2).toNanos()); new TransactionTemplate(manager).executeWithoutResult(status -> ToolPublicationRetentionStore.lockSession(jdbc, tenant, session)); - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); byte[] bytes = "hello".getBytes(java.nio.charset.StandardCharsets.UTF_8); String digest = ToolPublicationContract.sha256(bytes); String resource = ManagedSchemaKeys.key(scope, "projection-body"); @@ -346,7 +349,7 @@ public class ManagedSchemaBusinessStoreTest { @Test void schedulePagesUseCurrentRootPermissionsThroughoutTheChildWorkspaceChain() { - byte[] actor = ManagedWorkspaceRegistry.actorKey(tenant, "operator"); + byte[] actor = com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(tenant, "operator"); String parent = null; for (String workspace : List.of("root", "child", "grandchild", "foreign")) { boolean child = workspace.equals("child") || workspace.equals("grandchild"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaJdbcTimeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaJdbcTimeTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaJdbcTimeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaJdbcTimeTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationLoadMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationLoadMySqlIT.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationLoadMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationLoadMySqlIT.java index 171d61b9278..81113fcfcff 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationLoadMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationLoadMySqlIT.java @@ -1,11 +1,12 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.junit.jupiter.api.Test; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -62,7 +63,7 @@ class ManagedSchemaPublicationLoadMySqlIT { var properties = new ManagedAgentProperties(); properties.getToolPublication().setGcEnabled(true); properties.getToolPublication().setDeletionGrace(java.time.Duration.ZERO); - String sessionScope = ManagedSessionStore.sessionScopeKey(TENANT, HEALTHY); + String sessionScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, HEALTHY); String scope = ManagedSchemaKeys.key("capture-scope"); String publication = "capture"; String operationId = "capture-gc-" + ManagedSchemaKeys.key(scope, publication); @@ -158,7 +159,7 @@ class ManagedSchemaPublicationLoadMySqlIT { jdbc.update("INSERT INTO ma_read_lease (lease_id, tenant_key, session_key, retirement_generation, expires_at)" + " VALUES ('held-reader', ?, ?, 0, ?)", ManagedSchemaKeys.key(TENANT), readerKey, ToolPublicationRetentionStore.now(jdbc) + 600_000); - String unknownScope = ManagedSessionStore.sessionScopeKey(TENANT, "unknown-put"); + String unknownScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, "unknown-put"); String unknownTarget = ManagedSchemaKeys.key("original-object"); String unknownKey = ManagedSchemaKeys.operationKey(OperationKind.OBJECT_PUT, unknownScope, unknownTarget, "put"); insertOperation(jdbc, unknownKey, unknownScope, "put", "OBJECT_PUT", "unknown-put", unknownTarget, "UNKNOWN", 0); @@ -170,7 +171,7 @@ class ManagedSchemaPublicationLoadMySqlIT { assertThat(jdbc.queryForObject("SELECT failure_code FROM ma_operation WHERE session_id = ?" + " AND operation_kind = 'SESSION_RESOURCE_GC'", String.class, "unknown-put")).isEqualTo("put_unresolved"); - String scope = ManagedSessionStore.sessionScopeKey(TENANT, HEALTHY); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(TENANT, HEALTHY); String proofKey = ManagedSchemaKeys.operationKey(OperationKind.WORKSPACE_OPERATOR_RECOVERY, scope, scope, "proof"); insertOperation(jdbc, proofKey, scope, "proof", "WORKSPACE_OPERATOR_RECOVERY", HEALTHY, scope, "SUCCEEDED", ManagedSchemaTime.NEVER_DUE_MILLIS); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationStoreTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationStoreTest.java index 6315085464a..6b5a4446bff 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaPublicationStoreTest.java @@ -1,9 +1,11 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.PublicationJournalFixture; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; @@ -83,7 +85,7 @@ public class ManagedSchemaPublicationStoreTest { outcome.set("history", history); JsonNode admission = data.prepareAdmission(key, "pub-1", "writer-1", 1, WRITER_TOKEN, outcome); String resourceId = admission.path("resourceId").asText(); - String resourceKey = ManagedSchemaKeys.key(ManagedSessionStore.sessionScopeKey("tenant-1", "session-1"), resourceId); + String resourceKey = ManagedSchemaKeys.key(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant-1", "session-1"), resourceId); assertThat(fixture.jdbc.queryForObject("SELECT state FROM ma_resource WHERE resource_key = ?", String.class, resourceKey)).isEqualTo("PUBLISHED"); var payload = json.createObjectNode().put("executionCallId", "execution-1") diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaRelayLoadMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaRelayLoadMySqlIT.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaRelayLoadMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaRelayLoadMySqlIT.java index 7d08f625907..9b58b6a8d01 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaRelayLoadMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaRelayLoadMySqlIT.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.junit.jupiter.api.Test; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; @@ -88,7 +88,7 @@ class ManagedSchemaRelayLoadMySqlIT { } private static Object[] row(String domain, String id, String state, long createdAt) { - return new Object[] {ManagedSessionStore.sessionScopeKey("tenant", "sender"), + return new Object[] {com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "sender"), ManagedSchemaKeys.key(domain, id), domain, id, state, createdAt}; } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionMySqlIT.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionMySqlIT.java index 54bd879b943..1532a14ab38 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionMySqlIT.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -93,7 +93,7 @@ public class ManagedSchemaSessionMySqlIT extends ManagedSchemaSessionStoreTest { } private void seedHistory(String tenant, String session, int first, int last) { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); List operations = new ArrayList<>(); List resources = new ArrayList<>(); byte[] bytes = "history".getBytes(StandardCharsets.UTF_8); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionStoreTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionStoreTest.java index a1173d2fc81..6c0ff2a32c7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSchemaSessionStoreTest.java @@ -1,6 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; @@ -46,13 +47,13 @@ public class ManagedSchemaSessionStoreTest { tx = new TransactionTemplate(new DataSourceTransactionManager(source)); tx.setIsolationLevel(org.springframework.transaction.TransactionDefinition.ISOLATION_REPEATABLE_READ); store = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), clock, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); tenant = "tenant-" + UUID.randomUUID(); session = tx.execute( @@ -169,7 +170,7 @@ public class ManagedSchemaSessionStoreTest { "SELECT COUNT(*) FROM ma_resource_ref WHERE owner_kind = 'MESSAGE'" + " AND session_scope_key = ?", Integer.class, - ManagedSessionStore.sessionScopeKey(tenant, session))) + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session))) .isEqualTo(1); } @@ -242,12 +243,12 @@ public class ManagedSchemaSessionStoreTest { @Test void projectionCleanupPreservesJournalReferencesAndRecoveryProtection() { alignProjectionClock(); - var privateBody = tx.execute(status -> new ManagedSessionStore(jdbc) + var privateBody = tx.execute(status -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)) .publishAgentInstructions(tenant, "workspace", session, "private instructions")); event("big", "a".repeat(70_000)); materialize(); String original = messageBody("big"); - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); jdbc.update("INSERT INTO ma_resource_ref (session_scope_key, owner_kind, owner_key, owner_revision, resource_key, tenant_id, session_id, gmt_create) VALUES (?, 'JOURNAL', ?, 1, ?, ?, ?, " + ManagedSchemaTime.SQL_NOW + ")", scope, scope, original, tenant, session); event("big", "b"); @@ -266,7 +267,7 @@ public class ManagedSchemaSessionStoreTest { makeProjectionCandidatesDue(); projectionCollector().runOnce(); assertThat(resourceState(original)).isEqualTo("COLLECTED"); - assertThat(resourceState(ManagedSessionStore.resourceKey(scope, privateBody.resourceId()))).isEqualTo("PUBLISHED"); + assertThat(resourceState(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, privateBody.resourceId()))).isEqualTo("PUBLISHED"); } private void alignProjectionClock() { @@ -373,7 +374,7 @@ public class ManagedSchemaSessionStoreTest { jdbc.update( "UPDATE ma_resource SET session_scope_key = ? WHERE resource_key = (SELECT" + " body_resource_key FROM ma_message WHERE tenant_id = ? AND item_id = ?)", - ManagedSessionStore.sessionScopeKey(tenant, "another-session"), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, "another-session"), tenant, "big"); assertThatThrownBy(() -> messages()) @@ -458,7 +459,7 @@ public class ManagedSchemaSessionStoreTest { @Test void writerHeartbeatCompletesWhileMessagePageReadIsPaused() throws Exception { - var privateStore = new ManagedSessionStore(jdbc); + var privateStore = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String token = "a".repeat(32); var grant = tx.execute(status -> privateStore.acquireWriter(tenant, session, token, new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "writer", 60_000L))); @@ -484,14 +485,14 @@ public class ManagedSchemaSessionStoreTest { String token = "a".repeat(32); jdbc.update( "INSERT INTO ma_session_head (tenant_key, harness_key, tenant_id, harness_session_id, session_id, head_kind, gmt_create, gmt_modified) VALUES (?, ?, ?, ?, ?, 'HARNESS', " + ManagedSchemaTime.SQL_NOW + ", " + ManagedSchemaTime.SQL_NOW + ")", - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.harnessDrainKey( + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.harnessDrainKey( tenant), - com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.harnessDrainKey( + com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.harnessDrainKey( session), tenant, session, session); - var privateStore = new ManagedSessionStore(jdbc); + var privateStore = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); assertThatThrownBy(() -> privateStore.restore(tenant, "workspace", session, token)) .isInstanceOfSatisfying( ManagedAgentException.class, @@ -696,13 +697,13 @@ public class ManagedSchemaSessionStoreTest { void duplicateMessageBodyIdentityIsReusedOnlyWhenItsImmutableRowMatches(String corruption) { var duplicate = new DuplicateBodyJdbc(jdbc.getDataSource(), corruption); var subject = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( duplicate, new ObjectMapper(), clock, ignored -> {}, new ManagedWorkspaceRegistry(duplicate), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); event("big", "x".repeat(70_000)); if ("none".equals(corruption)) { tx.executeWithoutResult(status -> subject.materializeNextBatch(tenant, session, 100)); @@ -804,7 +805,8 @@ public class ManagedSchemaSessionStoreTest { } }; subject = new ManagedAgentStore(paused, new ObjectMapper(), clock, ignored -> {}, - new ManagedWorkspaceRegistry(paused), new ManagedAgentProperties()); + new ManagedWorkspaceRegistry(paused), new ManagedAgentProperties(), + new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); } StoreModels.MessagePage read() { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardConvoyIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardConvoyIT.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardConvoyIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardConvoyIT.java index 77d32a4c979..77923814323 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardConvoyIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardConvoyIT.java @@ -5,20 +5,22 @@ import com.alibaba.qwen.code.managedagent.ExtensionRecordJournal; import com.alibaba.qwen.code.managedagent.ManagedHookRecordContractTest; import com.alibaba.qwen.code.managedagent.MySqlLockProbe; import com.alibaba.qwen.code.managedagent.PublicationJournalFixture; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AuthorizeLifecycleRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.BlockRecoveryRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.RenewWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AuthorizeLifecycleRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.BlockRecoveryRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.RenewWriterRequest; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.AfterEach; @@ -123,7 +125,7 @@ class ManagedSessionGuardConvoyIT { var jdbc = new ManagedSchemaJdbcTemplate(pool); var manager = new DataSourceTransactionManager(pool); var transactions = new TransactionTemplate(manager); - var store = new ManagedSessionStore(jdbc); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); int callers = 4; var ready = new CountDownLatch(callers); var release = new CountDownLatch(1); @@ -154,7 +156,7 @@ class ManagedSessionGuardConvoyIT { var jdbc = new ManagedSchemaJdbcTemplate(source); var manager = new DataSourceTransactionManager(source); var transactions = new TransactionTemplate(manager); - var store = new ManagedSessionStore(jdbc); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String tenant = "tenant-a"; String workspace = "workspace-a"; String token = "w".repeat(32); @@ -206,7 +208,7 @@ class ManagedSessionGuardConvoyIT { var cause = error.getCause(); assertThat(cause).isInstanceOf(ManagedAgentException.class); assertThat(((ManagedAgentException) cause).getStatus()) - .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value()); assertThat(((ManagedAgentException) cause).getCode()) .isEqualTo("tenant_guard_contended"); }); @@ -228,7 +230,7 @@ class ManagedSessionGuardConvoyIT { var cause = error.getCause(); assertThat(cause).isInstanceOf(ManagedAgentException.class); assertThat(((ManagedAgentException) cause).getStatus()) - .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value()); assertThat(((ManagedAgentException) cause).getCode()) .isEqualTo("tenant_guard_contended"); }); @@ -251,7 +253,7 @@ class ManagedSessionGuardConvoyIT { var cause = error.getCause(); assertThat(cause).isInstanceOf(ManagedAgentException.class); assertThat(((ManagedAgentException) cause).getStatus()) - .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value()); assertThat(((ManagedAgentException) cause).getCode()) .isEqualTo("tenant_guard_contended"); }); @@ -261,8 +263,8 @@ class ManagedSessionGuardConvoyIT { // A Turn submit admits through the same placement row on its // per-turn path, so it is bounded and named the same way. - var agents = new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + var agents = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); assertFastGuardFailure(() -> transactions.execute(ignored -> agents.insertTurnCommand(tenant, "turn.submit", "submit-witness", "a".repeat(64), "victim", List.of(), "b".repeat(64)))); } finally { @@ -298,7 +300,7 @@ class ManagedSessionGuardConvoyIT { var jdbc = new ManagedSchemaJdbcTemplate(source); var manager = new DataSourceTransactionManager(source); var transactions = new TransactionTemplate(manager); - var store = new ManagedSessionStore(jdbc); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String tenant = "tenant-a"; String workspace = "workspace-a"; String token = "w".repeat(32); @@ -525,7 +527,7 @@ class ManagedSessionGuardConvoyIT { var jdbc = new ManagedSchemaJdbcTemplate(source); var manager = new DataSourceTransactionManager(source); var transactions = new TransactionTemplate(manager); - var store = new ManagedSessionStore(jdbc); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String tenant = "tenant-a"; String workspace = "workspace-a"; String token = "w".repeat(32); @@ -582,7 +584,7 @@ class ManagedSessionGuardConvoyIT { new AcquireWriterRequest(workspace, "writer-victim", 60_000L)))) .isInstanceOf(ManagedAgentException.class) .satisfies(error -> { - assertThat(((ManagedAgentException) error).getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(((ManagedAgentException) error).getStatus()).isEqualTo(409); assertThat(((ManagedAgentException) error).getCode()) .isEqualTo("managed_session_lifecycle_active"); }); @@ -667,7 +669,7 @@ class ManagedSessionGuardConvoyIT { var pausing = new PauseAfterDataSource(source, trigger); var jdbc = new ManagedSchemaJdbcTemplate(pausing); var transactions = new TransactionTemplate(new DataSourceTransactionManager(pausing)); - var store = new ManagedSessionStore(jdbc); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var grant = transactions.execute(ignored -> store.acquireWriter(TENANT, "victim", TOKEN, new AcquireWriterRequest(WORKSPACE, "writer-victim", 60_000L))); long deadlocksBefore = MySqlLockProbe.innodbDeadlocks(jdbc); @@ -771,7 +773,7 @@ class ManagedSessionGuardConvoyIT { var cause = error.getCause(); assertThat(cause).isInstanceOf(ManagedAgentException.class); assertThat(((ManagedAgentException) cause).getStatus()) - .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value()); assertThat(((ManagedAgentException) cause).getCode()) .isEqualTo("tenant_guard_contended"); }); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardMappingTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardMappingTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardMappingTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardMappingTest.java index 3c86a9e1fb8..c225219d489 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardMappingTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionGuardMappingTest.java @@ -6,7 +6,6 @@ import org.springframework.dao.CannotAcquireLockException; import org.springframework.dao.DataAccessException; import org.springframework.dao.DeadlockLoserDataAccessException; import org.springframework.dao.QueryTimeoutException; -import org.springframework.http.HttpStatus; import java.sql.SQLException; import java.util.List; @@ -70,7 +69,7 @@ class ManagedSessionGuardMappingTest { } private void requireGuardContended(ManagedAgentException error) { - assertThat(error.getStatus()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + assertThat(error.getStatus()).isEqualTo(503); assertThat(error.getCode()).isEqualTo("tenant_guard_contended"); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionHistoryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionHistoryTest.java similarity index 89% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionHistoryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionHistoryTest.java index 2bce8412c43..03502b13af5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionHistoryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionHistoryTest.java @@ -3,7 +3,7 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.springframework.jdbc.core.JdbcTemplate; @@ -19,15 +19,15 @@ class ManagedSessionHistoryTest { try (var fixture = ConsolidatedStoreFixture.open("session_history")) { ManagedSchema.initialize(fixture.source()); JdbcTemplate jdbc = new JdbcTemplate(fixture.source()); - ManagedAgentStore store = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), - ignored -> {}, new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + ManagedAgentStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), + ignored -> {}, new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); for (int i = 1; i <= 5; i++) { String id = "session-" + i; jdbc.update("INSERT INTO ma_session (tenant_id,session_id,agent_id,status,gmt_create,gmt_modified," + "tenant_key,session_key,session_scope_key) VALUES (?,?,?,?,?,0,?,?,?)", "tenant", id, "qwen-code", i % 2 == 0 ? "ARCHIVED" : "ACTIVE", i, ManagedSchemaKeys.key("tenant"), ManagedSchemaKeys.key(id), - ManagedSessionStore.sessionScopeKey("tenant", id)); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", id)); jdbc.update("INSERT INTO ma_event_stream (tenant_id, session_id, stream_kind, stream_id, gmt_modified)" + " VALUES (?, ?, 'SESSION', X'', 0)", "tenant", id); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionNativeReadTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionNativeReadTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionNativeReadTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionNativeReadTest.java index d8e19e61152..135e43bd295 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionNativeReadTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionNativeReadTest.java @@ -1,14 +1,17 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.flywaydb.core.Flyway; import org.junit.jupiter.api.AfterEach; @@ -81,14 +84,14 @@ class ManagedSessionNativeReadTest { binding = bindings.compareAndSet(binding, binding.withAttestation(lease, new RuntimeResourceHandle("kubernetes-workspace", 3, Map.of("podUid", "fixture-original")), Instant.now(), Instant.now())); - store = new ManagedSessionStore(jdbc); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); transactions.execute(status -> store.acquireWriter("tenant", session, TOKEN, new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "writer", 300_000L))); jdbc.update( "INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create, last_verified_at) VALUES (?, ?, 'tenant', 'workspace', ?, 'resource', 'managed-session-context', 1, ?, ?, 'MYSQL_INLINE', ?, 'fixture', 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ", 946684800000)", - ManagedSessionStore.resourceKey( - ManagedSessionStore.sessionScopeKey("tenant", session), "resource"), - ManagedSessionStore.sessionScopeKey("tenant", session), session, BYTES.length, + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey( + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), "resource"), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), session, BYTES.length, ToolPublicationContract.sha256(BYTES), BYTES); } @@ -235,10 +238,10 @@ class ManagedSessionNativeReadTest { new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "ordinary-writer", 60_000L))); jdbc.update( "INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create) VALUES (?, ?, 'tenant', 'workspace', ?, 'ordinary-resource', 'managed-session-context', 1, ?, ?, 'MYSQL_INLINE', ?, 'ordinary-fixture', 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ")", - ManagedSessionStore.resourceKey( - ManagedSessionStore.sessionScopeKey("tenant", ordinary), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey( + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", ordinary), "ordinary-resource"), - ManagedSessionStore.sessionScopeKey("tenant", ordinary), ordinary, BYTES.length, + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", ordinary), ordinary, BYTES.length, ToolPublicationContract.sha256(BYTES), BYTES); assertThat(store.readOwnerResource("tenant", "workspace", ordinary, "ordinary-resource", TOKEN).bytes()) .isEqualTo(BYTES); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceMySqlIT.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceMySqlIT.java index 09c680fc597..eeb602b0c39 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceMySqlIT.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceTest.java index e6b9e68fd50..6754af5e7ea 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionPhysicalFenceTest.java @@ -1,9 +1,10 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -48,7 +49,7 @@ class ManagedSessionPhysicalFenceTest { ManagedSchema.initialize(source); jdbc = new ManagedSchemaJdbcTemplate(source); transactions = new TransactionTemplate(new DataSourceTransactionManager(source)); - sessions = new ManagedSessionStore(jdbc); + sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); generation = transactions .execute( @@ -316,17 +317,17 @@ class ManagedSessionPhysicalFenceTest { "INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role) VALUES" + " (?, 'workspace', ?, 'OPERATOR')", TENANT, - ManagedWorkspaceRegistry.actorKey(TENANT, "actor")); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(TENANT, "actor")); var publicStore = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new com.fasterxml.jackson.databind.ObjectMapper(), java.time.Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties()); + new com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var selection = - new com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection("workspace", "."); + new com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection("workspace", "."); var parent = transactions .execute( diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStoreCredentialTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStoreCredentialTest.java similarity index 88% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStoreCredentialTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStoreCredentialTest.java index 6508e0a5402..c2a1aa0df00 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStoreCredentialTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStoreCredentialTest.java @@ -1,8 +1,9 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; import org.flywaydb.core.Flyway; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; @@ -30,7 +31,7 @@ class ManagedSessionStoreCredentialTest { void lifecycleWriterPathsRefuseForeignCredentialsBeforeStateLookup(String route) { JdbcTemplate jdbc = mock(JdbcTemplate.class); when(jdbc.getDataSource()).thenReturn(new JdbcDataSource()); - ManagedSessionStore store = new ManagedSessionStore(jdbc); + ManagedSessionStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getSessionStore().setBindingKey(KEY); WriterCredentialPolicy policy = new WriterCredentialPolicy(properties); @@ -43,7 +44,7 @@ class ManagedSessionStoreCredentialTest { policy.issue("tenant", "workspace", "foreign-session")}) { assertThatThrownBy(() -> invokeWriterPath(store, route, token)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus().value()).isEqualTo(403); + assertThat(error.getStatus()).isEqualTo(403); assertThat(error.getCode()).isEqualTo("writer_credential_invalid"); }); verifyNoInteractions(jdbc); @@ -75,8 +76,8 @@ class ManagedSessionStoreCredentialTest { void publicationWriterLockRequiresTheBoundCredential() { JdbcDataSource dataSource = new JdbcDataSource(); dataSource.setURL("jdbc:h2:mem:credential-check;MODE=MySQL"); - ManagedSessionStore store = new ManagedSessionStore( - new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource)); + ManagedSessionStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore( + new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource))); ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getSessionStore().setBindingKey(KEY); store.setCredentials(new WriterCredentialPolicy(properties)); @@ -85,7 +86,7 @@ class ManagedSessionStoreCredentialTest { "workspace", "session", "writer", 1, "self-minted-token-self-minted-token-0")) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus().value()).isEqualTo(403); + assertThat(error.getStatus()).isEqualTo(403); assertThat(error.getCode()) .isEqualTo("writer_credential_invalid"); }); @@ -98,8 +99,8 @@ class ManagedSessionStoreCredentialTest { + "DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE"); Flyway.configure().dataSource(dataSource) .locations("classpath:db/managed-schema").load().migrate(); - ManagedSessionStore store = new ManagedSessionStore( - new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource)); + ManagedSessionStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore( + new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource))); ManagedAgentProperties properties = new ManagedAgentProperties(); properties.getSessionStore().setBindingKey(KEY); WriterCredentialPolicy policy = new WriterCredentialPolicy(properties); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotNumericPagingTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotNumericPagingTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotNumericPagingTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotNumericPagingTest.java index 030bb130787..b6aee7cd7c7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotNumericPagingTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotNumericPagingTest.java @@ -3,7 +3,7 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.model.StoreModels; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.springframework.jdbc.core.RowMapper; @@ -197,13 +197,13 @@ class ManagedSnapshotNumericPagingTest { new TransactionTemplate(new DataSourceTransactionManager(database.source())); transaction.setIsolationLevel(org.springframework.transaction.TransactionDefinition.ISOLATION_REPEATABLE_READ); store = - new ManagedAgentStore( + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); jdbc.update( "INSERT INTO" + " ma_session(tenant_id,session_id,agent_id,gmt_create,gmt_modified,tenant_key,session_key,session_scope_key)" @@ -213,7 +213,7 @@ class ManagedSnapshotNumericPagingTest { "qwen-code", ManagedSchemaKeys.key("tenant"), ManagedSchemaKeys.key("session"), - ManagedSessionStore.sessionScopeKey("tenant", "session")); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "session")); int sequence = ties ? count - 5 : count; jdbc.update( "INSERT INTO" @@ -235,11 +235,11 @@ class ManagedSnapshotNumericPagingTest { + "first_sequence,last_sequence,gmt_create,gmt_modified,revision,parts_json)" + " VALUES(?,?,?,?,'message','assistant','completed','{}',?,?,0,0,1,'[]')", rows); - String scope = ManagedSessionStore.sessionScopeKey("tenant", "session"); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "session"); byte[] bytes = {91, 93}; for (int index = Math.max(1, count - 3); index <= count; index++) { String resourceId = "numeric-parts-" + index; - String resourceKey = ManagedSessionStore.resourceKey(scope, resourceId); + String resourceKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, resourceId); jdbc.update("INSERT INTO ma_resource(resource_key,session_scope_key,tenant_id,session_id," + "resource_id,kind,schema_version,byte_length,sha256,storage_kind,inline_bytes,state,gmt_create)" + " VALUES(?,?,'tenant','session',?,'managed-message-parts',1,2,?,'MYSQL_INLINE',?,'REFERENCED',0)", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotPoolTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotPoolTest.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotPoolTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotPoolTest.java index 5cbad7f457a..1873ba8ff01 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotPoolTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSnapshotPoolTest.java @@ -4,9 +4,9 @@ import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -40,7 +40,7 @@ class ManagedSnapshotPoolTest { jdbc = new ManagedSchemaJdbcTemplate(source); jdbc.update("INSERT INTO ma_session (tenant_id, session_id, agent_id, gmt_create, gmt_modified, tenant_key, session_key, session_scope_key) VALUES ('tenant', 'session', 'qwen-code', 0, 0, ?, ?, ?)", ManagedSchemaKeys.key("tenant"), ManagedSchemaKeys.key("session"), - ManagedSessionStore.sessionScopeKey("tenant", "session")); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "session")); jdbc.update("INSERT INTO ma_event_stream (tenant_id, session_id, stream_kind, stream_id, last_sequence, covered_sequence, gmt_modified) VALUES ('tenant', 'session', 'SESSION', X'', 1, 1, 0)"); } @@ -55,7 +55,8 @@ class ManagedSnapshotPoolTest { + "first_sequence,last_sequence,gmt_create,gmt_modified,parts_json)" + " VALUES ('tenant','session','item','turn','message','assistant','completed','{}',1,1,0,0,'[]')"); var store = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), + new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); var view = new TransactionTemplate(new DataSourceTransactionManager(source)); view.setReadOnly(true); view.setIsolationLevel(org.springframework.transaction.TransactionDefinition.ISOLATION_REPEATABLE_READ); @@ -67,7 +68,7 @@ class ManagedSnapshotPoolTest { @Test void retainedPrivatePinPreventsReleasedBodyCollection() { var manager = new DataSourceTransactionManager(source); - String scope = ManagedSessionStore.sessionScopeKey("tenant", "session"); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "session"); jdbc.update("INSERT INTO ma_resource (resource_key,session_scope_key,tenant_id,session_id,resource_id,kind,schema_version," + "byte_length,sha256,storage_kind,inline_bytes,state,gmt_create,retention_until)" + " VALUES (?,?,'tenant','session','released','managed-message-parts',1,2,?,'MYSQL_INLINE',?,'REFERENCED',0,0)", @@ -86,8 +87,8 @@ class ManagedSnapshotPoolTest { @Test void readsSessionAndStreamCountersWithOneConnection() { - var store = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); assertThat(store.findSession("tenant", "session").orElseThrow().lastSequence()).isEqualTo(1); assertThat(store.findSessionById("session")).isPresent(); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultCaptureTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultCaptureTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultCaptureTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultCaptureTest.java index 9abeef0e959..741921fcfb5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultCaptureTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultCaptureTest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjectorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjectorTest.java similarity index 89% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjectorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjectorTest.java index e1bd3372704..eff091978ba 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjectorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedToolResultProjectorTest.java @@ -22,7 +22,7 @@ class ManagedToolResultProjectorTest { factory.getBeanProvider(ToolPublicationDataStore.class), org.mockito.Mockito.mock(ManagedArtifactReader.class), org.mockito.Mockito.mock( - com.alibaba.qwen.code.managedagent.artifact.ManagedArtifactPolicy.class), + com.alibaba.qwen.code.managedagent.contracts.artifact.ManagedArtifactPolicy.class), properties)); projector.tick(); org.mockito.Mockito.verifyNoInteractions(store); @@ -49,11 +49,11 @@ class ManagedToolResultProjectorTest { factory.getBeanProvider(ToolPublicationDataStore.class), org.mockito.Mockito.mock(ManagedArtifactReader.class), org.mockito.Mockito.mock( - com.alibaba.qwen.code.managedagent.artifact.ManagedArtifactPolicy.class), + com.alibaba.qwen.code.managedagent.contracts.artifact.ManagedArtifactPolicy.class), properties); org.mockito.Mockito.when(store.claim()) .thenThrow(new com.alibaba.qwen.code.managedagent.exception.ManagedAgentException( - org.springframework.http.HttpStatus.SERVICE_UNAVAILABLE, + 503, "tenant_guard_contended", "Tenant guard is held")); org.assertj.core.api.Assertions.assertThatCode(projector::tick) @@ -96,9 +96,9 @@ class ManagedToolResultProjectorTest { @Test void scopedIdentityEncodingIsStableAndUnambiguous() { - assertThat(ManagedToolResultStore.identity("result", "1", "tenant-1", "workspace-1", "session-1", "execution-1")) + assertThat(com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.identity("result", "1", "tenant-1", "workspace-1", "session-1", "execution-1")) .isEqualTo("result_2cfafc7d888110d51e955a9816b826fe3fd617141bbf38748bf6116b0efc5d69"); - assertThat(ManagedToolResultStore.identity("result", "1", "a", "bc")) - .isNotEqualTo(ManagedToolResultStore.identity("result", "1", "ab", "c")); + assertThat(com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.identity("result", "1", "a", "bc")) + .isNotEqualTo(com.alibaba.qwen.code.managedagent.repository.ManagedToolResultStore.identity("result", "1", "ab", "c")); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedWorkspaceRolesMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedWorkspaceRolesMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedWorkspaceRolesMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ManagedWorkspaceRolesMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4FaultProcess.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4FaultProcess.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4FaultProcess.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4FaultProcess.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4FileObjects.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4FileObjects.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4FileObjects.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4FileObjects.java index aa2919929c2..80a400305fd 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4FileObjects.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4FileObjects.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; import java.io.IOException; import java.io.InputStream; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlDatabase.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlDatabase.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlDatabase.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlDatabase.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlDatabaseTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlDatabaseTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlDatabaseTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlDatabaseTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlGate.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlGate.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlGate.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlGate.java index 76a5790d426..467a878bed3 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlGate.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4MySqlGate.java @@ -1,6 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -57,7 +58,7 @@ public class O4MySqlGate extends ToolPublicationCollectorTest { var writer = executor.submit(() -> tx.execute(status -> { connection.set(jdbc.queryForObject("SELECT CONNECTION_ID()", Long.class)); entered.countDown(); - return new ManagedSessionStore(jdbc).acquireWriter(tenant, session, + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).acquireWriter(tenant, session, "a".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace-1", "late", 60000L)); })); assertThat(entered.await(10, TimeUnit.SECONDS)).isTrue(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4OssGate.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4OssGate.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4OssGate.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4OssGate.java index fdbde380be9..92ba630e727 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4OssGate.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/O4OssGate.java @@ -1,10 +1,9 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.AliyunToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationStorageException; -import com.alibaba.qwen.code.managedagent.config.AliyunToolPublicationStorageAutoConfiguration; +import com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationStorageException; import com.aliyun.oss.OSS; import com.aliyun.oss.common.auth.DefaultCredentialProvider; import com.aliyun.oss.common.auth.DefaultCredentials; @@ -64,7 +63,7 @@ public class O4OssGate extends O4MySqlGate { private static OSS client() throws Exception { secret("OSS_ACCESS_KEY_ID"); secret("OSS_ACCESS_KEY_SECRET"); - return new AliyunToolPublicationStorageAutoConfiguration().toolPublicationOss(properties()); + return com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient.buildOss(properties(), com.aliyun.oss.common.auth.CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider()); } private static ManagedAgentProperties properties() { var props = new ManagedAgentProperties(); @@ -77,7 +76,7 @@ public class O4OssGate extends O4MySqlGate { private static OSS deniedClient() { var credentials = new DefaultCredentials(secret("OSS_DELETE_DENIED_ACCESS_KEY_ID"), secret("OSS_DELETE_DENIED_ACCESS_KEY_SECRET"), System.getenv("OSS_DELETE_DENIED_SESSION_TOKEN")); - return AliyunToolPublicationStorageAutoConfiguration.buildOss(properties(), new DefaultCredentialProvider(credentials)); + return com.alibaba.qwen.code.managedagent.adapters.artifact.AliyunObjectStoreClient.buildOss(properties(), new DefaultCredentialProvider(credentials)); } @Test diff --git a/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/PersistenceTestAccess.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/PersistenceTestAccess.java new file mode 100644 index 00000000000..d9e55907253 --- /dev/null +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/PersistenceTestAccess.java @@ -0,0 +1,7 @@ +package com.alibaba.qwen.code.managedagent.persistence; +public final class PersistenceTestAccess { + private PersistenceTestAccess() { } + public static String descriptorRef(String tenant, String workspace) { return ManagedWorkspaceRegistry.descriptorRef(tenant, workspace); } + public static com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest requireCsiRequest(ManagedAgentStore store, com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration registration, String key) { return store.requireCsiRequest(registration, key); } + public static boolean blocked(org.springframework.jdbc.core.JdbcTemplate jdbc, String tenant, String session) { return ManagedConfigStore.blocked(jdbc, tenant, session); } +} diff --git a/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/PrivateCsiFixture.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/PrivateCsiFixture.java new file mode 100644 index 00000000000..02c7a8dcc43 --- /dev/null +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/PrivateCsiFixture.java @@ -0,0 +1,10 @@ +package com.alibaba.qwen.code.managedagent.persistence; +public final class PrivateCsiFixture { + private PrivateCsiFixture() { } + public static com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Created create( + org.springframework.jdbc.core.JdbcTemplate jdbc, org.springframework.transaction.PlatformTransactionManager manager, + com.fasterxml.jackson.databind.ObjectMapper json, com.alibaba.qwen.code.managedagent.settings.ManagedAgentSettings settings, + com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request request) { + return WorkspaceCsiSessionCommands.create(jdbc, manager, json, settings, request, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); + } +} diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/RecoverySettlementStoreMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/RecoverySettlementStoreMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/RecoverySettlementStoreMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/RecoverySettlementStoreMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/RecoverySettlementStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/RecoverySettlementStoreTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/RecoverySettlementStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/RecoverySettlementStoreTest.java index 4e1c474ddda..46ba7ba036c 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/RecoverySettlementStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/RecoverySettlementStoreTest.java @@ -1,11 +1,11 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.Admission; import com.alibaba.qwen.code.managedagent.model.StoreModels.HarnessEvent; import com.alibaba.qwen.code.managedagent.model.StoreModels.ProjectedEvent; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnRecord; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; @@ -206,7 +206,7 @@ class RecoverySettlementStoreTest { } private ManagedAgentStore newStore() { - return new ManagedAgentStore(jdbc, new ObjectMapper(), clock, events -> {}, - mock(ManagedWorkspaceRegistry.class), new ManagedAgentProperties()); + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), clock, events -> {}, + mock(ManagedWorkspaceRegistry.class), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentRetirementFixture.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentRetirementFixture.java similarity index 84% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentRetirementFixture.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentRetirementFixture.java index 92bfc8eddfe..ac6e067060b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentRetirementFixture.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentRetirementFixture.java @@ -1,20 +1,21 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.adapters.runtime.WorkspaceCsiResourceGuard; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.ResidentWorkspace; -import com.alibaba.qwen.code.managedagent.event.CommittedEventPublisher; +import com.alibaba.qwen.code.managedagent.contracts.event.CommittedEventPublisher; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceCsiResourceGuard; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesHttpRuntimeClient; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -38,27 +39,27 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -final class ResidentRetirementFixture { +public final class ResidentRetirementFixture { private static final String UID = "11111111-1111-4111-8111-111111111111"; private static final ObjectMapper JSON = new ObjectMapper(); - final DataSource source; - final ManagedSchemaJdbcTemplate jdbc; - final DataSourceTransactionManager manager; - final JdbcRuntimeBindingRepository bindings; - final WorkspaceCsiReservationStore storage; - final ResidentWorkspace mapping; - final ResidentWorkspaceRetirementStore.Request request; - final RuntimeProvisionRequest runtimeRequest; - final Map> objects = new LinkedHashMap<>(); - final List> attachments = new java.util.ArrayList<>(); - RuntimeBindingRecord original; + public final DataSource source; + public final ManagedSchemaJdbcTemplate jdbc; + public final DataSourceTransactionManager manager; + public final JdbcRuntimeBindingRepository bindings; + public final WorkspaceCsiReservationStore storage; + public final ResidentWorkspace mapping; + public final ResidentWorkspaceRetirementStore.Request request; + public final RuntimeProvisionRequest runtimeRequest; + public final Map> objects = new LinkedHashMap<>(); + public final List> attachments = new java.util.ArrayList<>(); + public RuntimeBindingRecord original; private final ManagedAgentProperties properties = new ManagedAgentProperties(); - ResidentRetirementFixture(DataSource source) { + public ResidentRetirementFixture(DataSource source) { this(source, "workspace"); } - ResidentRetirementFixture(DataSource source, String workspaceId) { + public ResidentRetirementFixture(DataSource source, String workspaceId) { this.source = source; ManagedSchema.initialize(source); jdbc = new ManagedSchemaJdbcTemplate(source); @@ -67,7 +68,7 @@ final class ResidentRetirementFixture { mapping = new ResidentWorkspace(workspaceId, 1, new WorkspaceCsiRegistration("tenant", "storage", "cluster", "runtime", "claim", "pvc-uid", "volume", "pv-uid", "diskplugin.csi.alibabacloud.com", "handle", "backend", "serial", "/workspace", 1), - new WorkspaceCsiResourceGuard.ProtectionIdentity(UID, "protect", UID, 1, "protect-binding", UID, 1), + new com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity(UID, "protect", UID, 1, "protect-binding", UID, 1), "worker@sha256:" + "a".repeat(64), List.of("node", "/worker/cli.js"), List.of()); properties.getHarness().setWorkspaceFilesEnabled(true); properties.getRuntimeBroker().setResidentWorkspaces(List.of(mapping)); @@ -79,21 +80,21 @@ final class ResidentRetirementFixture { + " config_ref, policy_ref, state) VALUES ('tenant', ?, 1, 'storage', 'resident', ?, ?, 'ACTIVE')", workspaceId, WorkspaceExecutionProfile.CONFIG_REF, WorkspaceExecutionProfile.POLICY_REF); jdbc.update("INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" - + " VALUES ('tenant', ?, ?, 'OWNER')", workspaceId, ManagedWorkspaceRegistry.actorKey("tenant", "owner")); + + " VALUES ('tenant', ?, ?, 'OWNER')", workspaceId, com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "owner")); } - ResidentWorkspaceRetirementStore store() { + public ResidentWorkspaceRetirementStore store() { return new ResidentWorkspaceRetirementStore(jdbc, manager, bindings, List.of(mapping), JSON); } - StoreModels.Admission create(String key) { - var store = new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), mock(CommittedEventPublisher.class), - new ManagedWorkspaceRegistry(jdbc), properties); + public StoreModels.Admission create(String key) { + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), mock(CommittedEventPublisher.class), + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); return new TransactionTemplate(manager).execute(status -> store.insertWorkspaceSessionCommand("tenant", "owner", key, "digest-" + key, "qwen-code", null, "Session", List.of(), null, new WorkspaceSelection(mapping.workspaceId(), "."))); } - RuntimeBindingRecord ready() { + public RuntimeBindingRecord ready() { if (original != null) { return original; } @@ -145,8 +146,15 @@ final class ResidentRetirementFixture { return original; } - KubernetesHttpRuntimeClient api() { - var api = mock(KubernetesHttpRuntimeClient.class); + public static KubernetesHttpRuntimeClient maintenanceMethods(KubernetesHttpRuntimeClient api) { + org.mockito.Mockito.doCallRealMethod().when(api).verifyProtection(any(), any()); + org.mockito.Mockito.doCallRealMethod().when(api).reclamationBaseline(any(), any(), anyString(), any()); + org.mockito.Mockito.doCallRealMethod().when(api).reclamationReceipt(any(), any(), any(), any(), any()); + return api; + } + + public KubernetesHttpRuntimeClient api() { + var api = ResidentRetirementFixture.maintenanceMethods(mock(KubernetesHttpRuntimeClient.class)); when(api.get(anyString(), anyString(), anyString())).thenAnswer(call -> CompletableFuture.completedFuture(objects.get(call.getArgument(0)))); when(api.getCluster(anyString(), anyString())).thenAnswer(call -> @@ -177,7 +185,7 @@ final class ResidentRetirementFixture { return api; } - void preparePlatform() { + public void preparePlatform() { var now = Instant.now(); String volume = "kubernetes.io/csi/" + mapping.registration().driver() + "^handle"; objects.get("nodes").put("status", copy(Map.of("nodeInfo", Map.of("bootID", "boot-a"), @@ -192,7 +200,7 @@ final class ResidentRetirementFixture { attachments.add(attachment); } - void completePlatform() { + public void completePlatform() { var now = Instant.now(); var pod = objects.get("pods"); map(pod.get("metadata")).put("deletionTimestamp", now.minusSeconds(3).toString()); @@ -212,11 +220,11 @@ final class ResidentRetirementFixture { attachments.clear(); } - void resourceVersion(String version) { + public void resourceVersion(String version) { map(objects.get("pods").get("metadata")).put("resourceVersion", version); } - List> attempts() { + public List> attempts() { return jdbc.query("SELECT * FROM ma_operation_work WHERE work_kind = 'POD_DELETE_ATTEMPT' ORDER BY target_key", (row, index) -> WorkspaceOperations.work(row)); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceGraceDeadlineTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceGraceDeadlineTest.java similarity index 85% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceGraceDeadlineTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceGraceDeadlineTest.java index 2d142285068..491f68f0730 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceGraceDeadlineTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceGraceDeadlineTest.java @@ -35,7 +35,7 @@ class ResidentWorkspaceGraceDeadlineTest { metadata.put("deletionGracePeriodSeconds", 30); RuntimeException refusal = null; try { - fixture.store().reclaim(fixture.request, api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, api); } catch (RuntimeException failure) { refusal = failure; } @@ -65,7 +65,7 @@ class ResidentWorkspaceGraceDeadlineTest { }); RuntimeException refusal = null; try { - fixture.store().reclaim(fixture.request, api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, api); } catch (RuntimeException failure) { refusal = failure; } @@ -82,8 +82,8 @@ class ResidentWorkspaceGraceDeadlineTest { fixture.preparePlatform(); fixture.store().begin(fixture.request); var api = fixture.api(); - fixture.store().prepareReclamation(fixture.request, "10", api); - fixture.store().deletePod(fixture.request, "11", api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).prepareReclamation(fixture.request, "10", api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "11", api); return api; } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReclamationProcessMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReclamationProcessMySqlIT.java similarity index 84% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReclamationProcessMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReclamationProcessMySqlIT.java index 958d5064c62..aa2322c8346 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReclamationProcessMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReclamationProcessMySqlIT.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.ResidentWorkspace; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesHttpRuntimeClient; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.io.TempDir; @@ -44,13 +44,13 @@ class ResidentWorkspaceReclamationProcessMySqlIT { fixture.preparePlatform(); fixture.store().begin(fixture.request); var api = fixture.api(); - fixture.store().prepareReclamation(fixture.request, "10", api); - fixture.store().deletePod(fixture.request, "11", api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).prepareReclamation(fixture.request, "10", api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "11", api); fixture.completePlatform(); doReturn(CompletableFuture.completedFuture(new KubernetesHttpRuntimeClient.DeleteResponse(503, new byte[0], true))) .when(api).patchPodFinalizers(anyString(), anyString(), any(byte[].class)); if (!cut.endsWith("receipt")) { - assertThat(fixture.store().reclaim(fixture.request, api).state()).isEqualTo("PLATFORM_CLEANED"); + assertThat(new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, api).state()).isEqualTo("PLATFORM_CLEANED"); } Path input = directory.resolve("request.json"); JSON.writeValue(input.toFile(), Map.of("mapping", fixture.mapping, "request", fixture.request, "cut", cut, @@ -79,12 +79,12 @@ class ResidentWorkspaceReclamationProcessMySqlIT { assertThat(fixture.bindings.findById(fixture.original.getBindingId()).getState()) .isEqualTo(cut.equals("after") ? RuntimeBindingRecord.State.RELEASED : RuntimeBindingRecord.State.DRAINING); if (!cut.equals("after")) { - assertThat(fixture.store().reclaim(fixture.request, fixture.api()).state()).isEqualTo("RELEASED"); + assertThat(new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, fixture.api()).state()).isEqualTo("RELEASED"); } assertThat(fixture.jdbc.queryForObject("SELECT active_binding_id FROM ma_runtime_slot", String.class)).isNull(); assertThat(fixture.storage.inspect(fixture.mapping.registration()).revision()).isEqualTo(2); - var unused = mock(KubernetesHttpRuntimeClient.class); - assertThat(fixture.store().reclaim(fixture.request, unused).state()).isEqualTo("RELEASED"); + var unused = ResidentRetirementFixture.maintenanceMethods(mock(KubernetesHttpRuntimeClient.class)); + assertThat(new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, unused).state()).isEqualTo("RELEASED"); verifyNoInteractions(unused); } finally { if (child.isAlive()) { @@ -119,7 +119,7 @@ class ResidentWorkspaceReclamationProcessMySqlIT { } }; var bindings = new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("test", new byte[32])); - var api = mock(KubernetesHttpRuntimeClient.class); + var api = ResidentRetirementFixture.maintenanceMethods(mock(KubernetesHttpRuntimeClient.class)); var objects = JSON.convertValue(document.get("objects"), new com.fasterxml.jackson.core.type.TypeReference>>() { }); when(api.get(anyString(), anyString(), anyString())).thenAnswer(call -> CompletableFuture.completedFuture( @@ -129,7 +129,7 @@ class ResidentWorkspaceReclamationProcessMySqlIT { when(api.listPersistentVolumes()).thenReturn(CompletableFuture.completedFuture(List.of(objects.get("persistentvolumes")))); when(api.listPods(anyString())).thenReturn(CompletableFuture.completedFuture(List.of(objects.get("pods")))); when(api.listVolumeAttachments()).thenReturn(CompletableFuture.completedFuture(List.of())); - new ResidentWorkspaceRetirementStore(jdbc, manager, bindings, List.of(selected), JSON).reclaim(request, api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(new ResidentWorkspaceRetirementStore(jdbc, manager, bindings, List.of(selected), JSON)).reclaim(request, api); } private static void awaitKill(Path input) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementProcessMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementProcessMySqlIT.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementProcessMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementProcessMySqlIT.java index 7a1883b2b66..79efc39f271 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementProcessMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementProcessMySqlIT.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.ResidentWorkspace; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesHttpRuntimeClient; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.net.httpserver.HttpServer; import org.junit.jupiter.api.Timeout; @@ -160,8 +160,8 @@ class ResidentWorkspaceRetirementProcessMySqlIT { assertThat(received.get()).isEqualTo(persisted); assertThat(target.get()).isEqualTo("/api/v1/namespaces/runtime/pods/" + placement.get("podName")); } - var unusedApi = mock(KubernetesHttpRuntimeClient.class); - assertThat(restarted.deletePod(fixture.request, "10", unusedApi)).isEqualTo(status); + var unusedApi = ResidentRetirementFixture.maintenanceMethods(mock(KubernetesHttpRuntimeClient.class)); + assertThat(new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(restarted).deletePod(fixture.request, "10", unusedApi)).isEqualTo(status); assertThat(restarted.inspect(fixture.request)).isEqualTo(status); verifyNoInteractions(unusedApi); assertThat(fixture.storage.inspect(fixture.mapping.registration())).isEqualTo(reservation); @@ -214,7 +214,7 @@ class ResidentWorkspaceRetirementProcessMySqlIT { return reply; }); }).when(api).deletePod(anyString(), anyString(), any(byte[].class)); - store(source, mapping).deletePod(request, "10", api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(store(source, mapping)).deletePod(request, "10", api); throw new AssertionError("Maintenance process unexpectedly reached the end before being killed"); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStoreTest.java similarity index 84% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStoreTest.java index 03888807e29..bf58f1b0418 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceRetirementStoreTest.java @@ -1,14 +1,15 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesHttpRuntimeClient; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -135,7 +136,7 @@ class ResidentWorkspaceRetirementStoreTest { assertThatThrownBy(() -> fixture.store().begin(request("tenant", "workspace", 1, "owner", UUID.randomUUID().toString()))) .isInstanceOf(RuntimeBrokerException.class); fixture.jdbc.update("INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" - + " VALUES ('tenant', 'workspace', ?, 'OWNER')", ManagedWorkspaceRegistry.actorKey("tenant", "other-owner")); + + " VALUES ('tenant', 'workspace', ?, 'OWNER')", com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "other-owner")); assertThatThrownBy(() -> fixture.store().inspect(request("tenant", "workspace", 1, "other-owner", fixture.request.retirementId()))) .isInstanceOf(RuntimeBrokerException.class); assertThat(fixture.store().inspect(fixture.request)).isEqualTo(status); @@ -172,7 +173,7 @@ class ResidentWorkspaceRetirementStoreTest { fixture.jdbc.update("UPDATE ma_operation SET request_json = '{}' WHERE operation_kind = 'WORKSPACE_RETIREMENT'"); assertThatThrownBy(() -> fixture.store().inspect(fixture.request)).isInstanceOf(RuntimeBrokerException.class); var api = fixture.api(); - assertThatThrownBy(() -> fixture.store().deletePod(fixture.request, "10", api)).isInstanceOf(RuntimeBrokerException.class); + assertThatThrownBy(() -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "10", api)).isInstanceOf(RuntimeBrokerException.class); verify(api, never()).deletePod(anyString(), anyString(), any(byte[].class)); } @@ -262,7 +263,7 @@ class ResidentWorkspaceRetirementStoreTest { byte[] reply = ("{\"code\":" + code + "}").getBytes(StandardCharsets.UTF_8); when(api.deletePod(anyString(), anyString(), any(byte[].class))).thenReturn( CompletableFuture.completedFuture(new KubernetesHttpRuntimeClient.DeleteResponse(code, reply, true))); - var status = fixture.store().deletePod(fixture.request, version, api); + var status = new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, version, api); assertThat(status.attempts().getLast().httpStatus()).isEqualTo(code); assertThat(status.attempts().getLast().state()).isEqualTo("RETURNED"); var saved = fixture.attempts().stream().filter(value -> version.equals(value.get("resourceVersion"))).findFirst().orElseThrow(); @@ -285,11 +286,11 @@ class ResidentWorkspaceRetirementStoreTest { fixture.store().begin(fixture.request); var api = fixture.api(); when(api.deletePod(anyString(), anyString(), any(byte[].class))).thenReturn(CompletableFuture.failedFuture(new IOException("reply lost"))); - assertThat(fixture.store().deletePod(fixture.request, "10", api).attempts().getFirst().state()).isEqualTo("UNKNOWN"); - assertThat(fixture.store().deletePod(fixture.request, "10", api).attempts()).hasSize(1); + assertThat(new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "10", api).attempts().getFirst().state()).isEqualTo("UNKNOWN"); + assertThat(new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "10", api).attempts()).hasSize(1); verify(api, times(1)).deletePod(anyString(), anyString(), any(byte[].class)); fixture.resourceVersion("11"); - assertThat(fixture.store().deletePod(fixture.request, "11", api).attempts()).hasSize(2); + assertThat(new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "11", api).attempts()).hasSize(2); verify(api, times(2)).deletePod(anyString(), anyString(), any(byte[].class)); assertThat(fixture.storage.inspect(fixture.mapping.registration()).phase()).isEqualTo("RESERVED"); } @@ -301,10 +302,10 @@ class ResidentWorkspaceRetirementStoreTest { for (int ordinal = 1; ordinal <= 8; ordinal++) { String version = "version-" + ordinal; fixture.resourceVersion(version); - assertThat(fixture.store().deletePod(fixture.request, version, api).attempts()).hasSize(ordinal); + assertThat(new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, version, api).attempts()).hasSize(ordinal); } fixture.resourceVersion("version-9"); - assertThatThrownBy(() -> fixture.store().deletePod(fixture.request, "version-9", api)).isInstanceOf(RuntimeBrokerException.class); + assertThatThrownBy(() -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "version-9", api)).isInstanceOf(RuntimeBrokerException.class); verify(api, times(8)).deletePod(anyString(), anyString(), any(byte[].class)); assertThat(fixture.store().inspect(fixture.request).attempts()).hasSize(8); } @@ -318,7 +319,7 @@ class ResidentWorkspaceRetirementStoreTest { + " WHERE operation_kind = 'WORKSPACE_RETIREMENT'"); return CompletableFuture.completedFuture(new KubernetesHttpRuntimeClient.DeleteResponse(200, new byte[] {1, 2}, true)); }); - assertThatThrownBy(() -> fixture.store().deletePod(fixture.request, "10", api)).isInstanceOf(RuntimeBrokerException.class); + assertThatThrownBy(() -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "10", api)).isInstanceOf(RuntimeBrokerException.class); assertThat(fixture.store().inspect(fixture.request).attempts().getFirst().state()).isEqualTo("UNKNOWN"); assertThat(fixture.attempts().getFirst()).doesNotContainKey("responseBase64"); assertThat(fixture.jdbc.queryForObject("SELECT claim_owner FROM ma_operation WHERE operation_kind = 'WORKSPACE_RETIREMENT'", @@ -330,13 +331,13 @@ class ResidentWorkspaceRetirementStoreTest { fixture.store().begin(fixture.request); var api = fixture.api(); fixture.jdbc.update("UPDATE ma_workspace_access SET role = 'READER'"); - assertThatThrownBy(() -> fixture.store().deletePod(fixture.request, "10", api)).isInstanceOf(RuntimeBrokerException.class); + assertThatThrownBy(() -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "10", api)).isInstanceOf(RuntimeBrokerException.class); fixture.jdbc.update("UPDATE ma_workspace_access SET role = 'OWNER'"); var pod = fixture.objects.get("pods"); map(pod.get("metadata")).put("uid", UUID.randomUUID().toString()); - assertThatThrownBy(() -> fixture.store().deletePod(fixture.request, "10", api)).isInstanceOf(RuntimeBrokerException.class); + assertThatThrownBy(() -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "10", api)).isInstanceOf(RuntimeBrokerException.class); fixture.objects.remove("pods"); - assertThatThrownBy(() -> fixture.store().deletePod(fixture.request, "10", api)).isInstanceOf(RuntimeBrokerException.class); + assertThatThrownBy(() -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "10", api)).isInstanceOf(RuntimeBrokerException.class); verify(api, never()).deletePod(anyString(), anyString(), any(byte[].class)); assertThat(fixture.store().inspect(fixture.request).attempts()).isEmpty(); } @@ -358,12 +359,12 @@ class ResidentWorkspaceRetirementStoreTest { var cycleFixture = current; assertThat(current.storage.inspect(current.mapping.registration()).revision()).isEqualTo(cycle * 2L + 1); var api = prepared(current); - assertThatThrownBy(() -> cycleFixture.store().reclaim(cycleFixture.request, api)).isInstanceOf(RuntimeBrokerException.class); + assertThatThrownBy(() -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(cycleFixture.store()).reclaim(cycleFixture.request, api)).isInstanceOf(RuntimeBrokerException.class); current.completePlatform(); - var terminal = current.store().reclaim(current.request, api); + var terminal = new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(current.store()).reclaim(current.request, api); assertThat(terminal.state()).isEqualTo("RELEASED"); assertThat(terminal.completion().releasedRevision()).isEqualTo(cycle * 2L + 2); - assertThat(terminal.completion().dataPolicy()).isEqualTo(ResidentWorkspaceReclamation.DATA_POLICY); + assertThat(terminal.completion().dataPolicy()).isEqualTo(com.alibaba.qwen.code.managedagent.contracts.workspace.ResidentReclamation.DATA_POLICY); historical.add(current.request); receipts.add(terminal.completion()); assertThat(current.bindings.findById(current.original.getBindingId()).getState()).isEqualTo(RuntimeBindingRecord.State.RELEASED); @@ -388,7 +389,7 @@ class ResidentWorkspaceRetirementStoreTest { void physicalReleasePreservesTenantAndRefusesAForeignAlias() { var api = prepared(fixture); fixture.completePlatform(); - fixture.store().reclaim(fixture.request, api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, api); var original = fixture.mapping.registration(); var foreign = new WorkspaceCsiRegistration("foreign", "foreign-storage", original.clusterDomain(), original.namespace(), original.pvcName(), original.pvcUid(), original.pvName(), original.pvUid(), original.driver(), original.volumeHandle(), @@ -410,8 +411,8 @@ class ResidentWorkspaceRetirementStoreTest { fixture.jdbc.update("UPDATE ma_operation SET request_json = ?, request_digest = ? WHERE operation_kind = 'WORKSPACE_RETIREMENT'", json.writeValueAsString(plan), com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity.digest(plan)); assertThat(fixture.store().inspect(fixture.request).state()).isEqualTo("DRAINING"); - assertThatThrownBy(() -> fixture.store().prepareReclamation(fixture.request, "10", fixture.api())).isInstanceOf(RuntimeBrokerException.class); - assertThatThrownBy(() -> fixture.store().reclaim(fixture.request, fixture.api())).isInstanceOf(RuntimeBrokerException.class); + assertThatThrownBy(() -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).prepareReclamation(fixture.request, "10", fixture.api())).isInstanceOf(RuntimeBrokerException.class); + assertThatThrownBy(() -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, fixture.api())).isInstanceOf(RuntimeBrokerException.class); assertThat(fixture.storage.inspect(fixture.mapping.registration()).phase()).isEqualTo("RESERVED"); } @@ -443,7 +444,7 @@ class ResidentWorkspaceRetirementStoreTest { default -> throw new AssertionError(defect); } var held = fixture.storage.inspect(fixture.mapping.registration()); - assertThatThrownBy(() -> fixture.store().reclaim(fixture.request, api)).isInstanceOf(RuntimeException.class); + assertThatThrownBy(() -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, api)).isInstanceOf(RuntimeException.class); assertThat(fixture.storage.inspect(fixture.mapping.registration())).isEqualTo(held); assertThat(fixture.bindings.findById(fixture.original.getBindingId()).getState()).isEqualTo(RuntimeBindingRecord.State.DRAINING); assertThat(fixture.store().inspect(fixture.request).state()).isEqualTo("DRAINING"); @@ -459,13 +460,13 @@ class ResidentWorkspaceRetirementStoreTest { when(api.patchPodFinalizers(anyString(), anyString(), any(byte[].class))).thenAnswer(call -> patches.incrementAndGet() == 1 ? CompletableFuture.failedFuture(new IOException("reply lost before effect")) : delegate.patchPodFinalizers(call.getArgument(0), call.getArgument(1), call.getArgument(2))); - var first = fixture.store().prepareReclamation(fixture.request, "10", api); + var first = new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).prepareReclamation(fixture.request, "10", api); assertThat(first.finalizerAttempts().getFirst().state()).isEqualTo("UNKNOWN"); - var second = fixture.store().prepareReclamation(fixture.request, "10", api); + var second = new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).prepareReclamation(fixture.request, "10", api); assertThat(second.finalizerAttempts()).hasSize(2); assertThat(second.finalizerAttempts().getFirst().state()).isEqualTo("UNKNOWN"); assertThat(second.finalizerAttempts().getLast().state()).isEqualTo("RETURNED"); - fixture.store().prepareReclamation(fixture.request, "11", api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).prepareReclamation(fixture.request, "11", api); assertThat(patches).hasValue(2); } @@ -474,12 +475,12 @@ class ResidentWorkspaceRetirementStoreTest { fixture.preparePlatform(); fixture.store().begin(fixture.request); var api = fixture.api(); - fixture.store().prepareReclamation(fixture.request, "10", api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).prepareReclamation(fixture.request, "10", api); when(api.deletePod(anyString(), anyString(), any(byte[].class))) .thenReturn(CompletableFuture.failedFuture(new IOException("reply lost"))); - assertThat(fixture.store().deletePod(fixture.request, "11", api).attempts().getFirst().state()).isEqualTo("UNKNOWN"); + assertThat(new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "11", api).attempts().getFirst().state()).isEqualTo("UNKNOWN"); fixture.completePlatform(); - var result = fixture.store().reclaim(fixture.request, api); + var result = new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, api); assertThat(result.state()).isEqualTo("RELEASED"); assertThat(result.attempts().getFirst().state()).isEqualTo("UNKNOWN"); verify(api, times(1)).deletePod(anyString(), anyString(), any(byte[].class)); @@ -504,22 +505,22 @@ class ResidentWorkspaceRetirementStoreTest { }; var store = new ResidentWorkspaceRetirementStore(failing, fixture.manager, fixture.bindings, List.of(fixture.mapping), new ObjectMapper()); - assertThatThrownBy(() -> store.reclaim(fixture.request, api)).isInstanceOf(IllegalStateException.class).hasMessage("release cut"); + assertThatThrownBy(() -> new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(store).reclaim(fixture.request, api)).isInstanceOf(IllegalStateException.class).hasMessage("release cut"); assertThat(fixture.storage.inspect(fixture.mapping.registration())).isEqualTo(held); assertThat(fixture.bindings.findById(fixture.original.getBindingId()).getState()).isEqualTo(RuntimeBindingRecord.State.DRAINING); assertThat(fixture.jdbc.queryForObject("SELECT active_binding_id FROM ma_runtime_slot", String.class)).isEqualTo(fixture.original.getBindingId()); assertThat(fixture.store().inspect(fixture.request).state()).isEqualTo("PLATFORM_CLEANED"); } - var released = fixture.store().reclaim(fixture.request, api); - assertThat(fixture.store().reclaim(fixture.request, api)).isEqualTo(released); + var released = new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, api); + assertThat(new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, api)).isEqualTo(released); } private static KubernetesHttpRuntimeClient prepared(ResidentRetirementFixture current) { current.preparePlatform(); current.store().begin(current.request); var api = current.api(); - current.store().prepareReclamation(current.request, "10", api); - current.store().deletePod(current.request, "11", api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(current.store()).prepareReclamation(current.request, "10", api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(current.store()).deletePod(current.request, "11", api); return api; } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReuseConcurrencyMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReuseConcurrencyMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReuseConcurrencyMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReuseConcurrencyMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReuseConcurrencyTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReuseConcurrencyTest.java similarity index 89% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReuseConcurrencyTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReuseConcurrencyTest.java index 03957a01090..ee57330388d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReuseConcurrencyTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspaceReuseConcurrencyTest.java @@ -1,6 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.ResidentWorkspace; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; @@ -58,10 +59,10 @@ class ResidentWorkspaceReuseConcurrencyTest { fixture.preparePlatform(); fixture.store().begin(fixture.request); var api = fixture.api(); - fixture.store().prepareReclamation(fixture.request, "10", api); - fixture.store().deletePod(fixture.request, "11", api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).prepareReclamation(fixture.request, "10", api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "11", api); fixture.completePlatform(); - assertThat(fixture.store().reclaim(fixture.request, api).state()).isEqualTo("RELEASED"); + assertThat(new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, api).state()).isEqualTo("RELEASED"); var released = fixture.storage.inspect(original); assertThat(released.revision()).isEqualTo(2); assertThatThrownBy(() -> fixture.storage.inspect(foreign)).isInstanceOf(RuntimeBrokerException.class); @@ -81,10 +82,10 @@ class ResidentWorkspaceReuseConcurrencyTest { fixture.preparePlatform(); fixture.store().begin(fixture.request); var api = fixture.api(); - fixture.store().prepareReclamation(fixture.request, "10", api); - fixture.store().deletePod(fixture.request, "11", api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).prepareReclamation(fixture.request, "10", api); + new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).deletePod(fixture.request, "11", api); fixture.completePlatform(); - var terminal = fixture.store().reclaim(fixture.request, api); + var terminal = new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, api); assertThat(terminal.state()).isEqualTo("RELEASED"); assertThat(terminal.completion().releasedRevision()).isEqualTo(2); assertThat(fixture.storage.inspect(fixture.mapping.registration()).phase()).isEqualTo("RELEASED"); @@ -124,7 +125,7 @@ class ResidentWorkspaceReuseConcurrencyTest { assertThat(fixture.storage.inspect(winner.registration())).isEqualTo(held); assertThat(fixture.storage.reserve(winner.registration(), fixture.bindings, winner.binding(), winner.reservationId())).isEqualTo(held); - assertThat(fixture.store().reclaim(fixture.request, api)).isEqualTo(terminal); + assertThat(new com.alibaba.qwen.code.managedagent.workspace.runtime.ResidentWorkspaceRetirementService(fixture.store()).reclaim(fixture.request, api)).isEqualTo(terminal); assertThat(fixture.store().begin(fixture.request)).isEqualTo(terminal); assertThatThrownBy(() -> fixture.bindings.findOrCreate(fixture.runtimeRequest)).isInstanceOf(RuntimeBrokerException.class); assertThat(fixture.storage.inspect(winner.registration())).isEqualTo(held); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspacesTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspacesTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspacesTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspacesTest.java index 3315aedf07f..2b0f7a0f44a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspacesTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ResidentWorkspacesTest.java @@ -2,17 +2,14 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.ResidentWorkspace; -import com.alibaba.qwen.code.managedagent.event.CommittedEventPublisher; +import com.alibaba.qwen.code.managedagent.contracts.event.CommittedEventPublisher; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.runtime.ProtectionIdentity; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.WorkspaceCsiResourceGuard.ProtectionIdentity; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeAttestation; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeObservation; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; @@ -23,9 +20,13 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -73,9 +74,9 @@ class ResidentWorkspacesTest { var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); properties.getRuntimeBroker().setResidentWorkspaces(List.of(mapping)); - authority = new WorkspaceExecutionStore(jdbc, manager, null, properties); - store = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), mock(CommittedEventPublisher.class), - new ManagedWorkspaceRegistry(jdbc), properties); + authority = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, manager, null, properties)); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), mock(CommittedEventPublisher.class), + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); residents = new ResidentWorkspaces(List.of(mapping)); storage = new WorkspaceCsiReservationStore(jdbc, manager, new ObjectMapper()); storage.register(mapping.registration()); @@ -86,7 +87,7 @@ class ResidentWorkspacesTest { + " config_ref, policy_ref, state) VALUES ('tenant', 'workspace', 1, 'storage', 'resident', ?, ?, 'ACTIVE')", WorkspaceExecutionProfile.CONFIG_REF, WorkspaceExecutionProfile.POLICY_REF); jdbc.update("INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" - + " VALUES ('tenant', 'workspace', ?, 'OWNER')", ManagedWorkspaceRegistry.actorKey("tenant", "owner")); + + " VALUES ('tenant', 'workspace', ?, 'OWNER')", com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "owner")); } @Test @@ -159,8 +160,8 @@ class ResidentWorkspacesTest { var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); properties.getRuntimeBroker().setResidentWorkspaces(List.of(mapping)); - var peer = new ManagedAgentStore(peerJdbc, new ObjectMapper(), Clock.systemUTC(), - mock(CommittedEventPublisher.class), new ManagedWorkspaceRegistry(peerJdbc), properties); + var peer = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(peerJdbc, new ObjectMapper(), Clock.systemUTC(), + mock(CommittedEventPublisher.class), new ManagedWorkspaceRegistry(peerJdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var peerTransaction = new TransactionTemplate(new DataSourceTransactionManager(source)); start.await(10, java.util.concurrent.TimeUnit.SECONDS); return peerTransaction.execute(status -> peer.insertWorkspaceSessionCommand("tenant", "owner", key, diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollectorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollectorTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollectorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollectorTest.java index 990072f5406..b501b736715 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollectorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionCollectorTest.java @@ -1,11 +1,14 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.ExtensionRecordJournal; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -13,7 +16,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; @@ -130,7 +132,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten } private void initSession() { - resScope = ManagedSessionStore.sessionScopeKey(tenant, session); + resScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); token = "a".repeat(32); } @@ -201,7 +203,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten private void tombstone(String id, String operation, long time) { jdbc.update( "INSERT INTO ma_session (tenant_id, session_id, session_kind, tenant_key, session_key, session_scope_key, retirement_operation_id, retirement_generation, retired_at, gmt_create, gmt_modified) VALUES (?, ?, 'CONTROL', ?, ?, ?, ?, 1, ?, ?, ?)", tenant, id, ManagedSchemaKeys.key(tenant), - ManagedSchemaKeys.key(id), ManagedSessionStore.sessionScopeKey(tenant, id), operation, time, time, time); + ManagedSchemaKeys.key(id), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, id), operation, time, time, time); } private SessionResourceCollectionCollector collector(boolean enabled, Duration grace) { @@ -662,7 +664,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten publish("segment", CONTENT, new byte[64]); var lease = retention.read(key); String other = "session-b"; - String otherScope = ManagedSessionStore.sessionScopeKey(tenant, other); + String otherScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, other); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create, last_verified_at) VALUES (?, ?, ?, 'workspace-1', ?, 'segment', ?, 1, ?, ?, 'MYSQL_INLINE', ?, 'segment', 'PUBLISHED', " + ManagedSchemaTime.SQL_NOW + ", " + ManagedSchemaTime.SQL_NOW + ")", ManagedSchemaKeys.key(otherScope, "segment"), otherScope, tenant, other, CONTENT, 64, ToolPublicationContract.sha256(new byte[64]), new byte[64]); @@ -843,7 +845,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten void readAfterCollectionAnswersTheSessionRetiredOutcome() { initSession(); head(session); - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); tx.executeWithoutResult(status -> sessions.acquireWriter(tenant, session, token, new ManagedSessionStoreModels.AcquireWriterRequest("workspace-1", "writer", 60000L))); publish("collected", CONTENT, new byte[64]); @@ -857,7 +859,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten assertThatThrownBy(() -> sessions.readResource(tenant, "workspace-1", session, "collected", token)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getCode()).isEqualTo("tool_output_session_retired"); - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); }); } @@ -909,7 +911,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten head(session); publish("segment-000", CONTENT, new byte[64]); String other = "session-2"; - String otherScope = ManagedSessionStore.sessionScopeKey(tenant, other); + String otherScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, other); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create, last_verified_at) VALUES (?, ?, ?, 'workspace-1', ?, 'segment-000', ?, 1, ?, ?, 'MYSQL_INLINE', ?, 'segment-000', 'PUBLISHED', " + ManagedSchemaTime.SQL_NOW + ", " + ManagedSchemaTime.SQL_NOW + ")", ManagedSchemaKeys.key(otherScope, "segment-000"), otherScope, tenant, other, CONTENT, 64, ToolPublicationContract.sha256(new byte[64]), new byte[64]); @@ -962,7 +964,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten long now = ToolPublicationRetentionStore.now(jdbc); tombstone(later, "purge-later", now - 1000); assertThat(collector.runOnce()).isFalse(); - String laterScope = ManagedSessionStore.sessionScopeKey(tenant, later); + String laterScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, later); assertThat(jdbc.queryForObject( "SELECT COUNT(*) FROM ma_operation WHERE operation_kind =" + " 'SESSION_RESOURCE_GC' AND session_scope_key = ?", Long.class, laterScope)).isZero(); @@ -1019,7 +1021,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten @Test void tamperedLiveRowStillFailsClosed() { initSession(); - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); tx.executeWithoutResult(status -> sessions.acquireWriter(tenant, session, token, new ManagedSessionStoreModels.AcquireWriterRequest("workspace-1", "writer", 60000L))); publish("tampered", CONTENT, new byte[64]); @@ -1028,7 +1030,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten assertThatThrownBy(() -> sessions.readResource(tenant, "workspace-1", session, "tampered", token)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getCode()).isEqualTo("managed_session_resource_corrupt"); - assertThat(error.getStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + assertThat(error.getStatus()).isEqualTo(500); }); } @@ -1122,7 +1124,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten void collectsRowsPublishedThroughTheRealProducerByteExact() { initSession(); head(session); - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var grant = tx.execute(status -> sessions.acquireWriter(tenant, session, token, new ManagedSessionStoreModels.AcquireWriterRequest("workspace-1", "writer", 60000L))); publishThroughStore(sessions, grant.writerGeneration(), "produced-content", CONTENT, new byte[1024]); @@ -1287,7 +1289,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten assertThat(countLedgers()).isEqualTo(1); String later = "session-later"; - String laterScope = ManagedSessionStore.sessionScopeKey(tenant, later); + String laterScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, later); retireSession(later, "purge-later"); // Inside the 60-second window the same instance does not re-scan. nanos.addAndGet(Duration.ofSeconds(30).toNanos()); @@ -1299,7 +1301,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten assertThat(ledgerCount(laterScope)).isEqualTo(1); String third = "session-third"; - String thirdScope = ManagedSessionStore.sessionScopeKey(tenant, third); + String thirdScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, third); retireSession(third, "purge-third"); // A backward host-clock step (NTP, VM resume) reads as "within the window", not as a // permanent stall: scanning resumes as soon as the monotonic clock catches up. @@ -1368,12 +1370,12 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten byte[] bytes = mediaBytes(original); assertThat(mediaBytes(other)).isEqualTo(bytes); assertThat(original.path("outcomeRef")).isEqualTo(other.path("outcomeRef")); - assertThat(ManagedSessionStore.resourceKey(resScope, "media-outcome")) - .isNotEqualTo(ManagedSessionStore.resourceKey( - ManagedSessionStore.sessionScopeKey(tenant, "media-other"), "media-outcome")); + assertThat(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(resScope, "media-outcome")) + .isNotEqualTo(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey( + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, "media-other"), "media-outcome")); assertThat(MEDIA_JSON.readTree(bytes).path("functionResponse").path("parts").get(0) .path("inlineData").path("data").asText()).isEqualTo("AQID"); - byte[] history = new ManagedSessionStore(jdbc).readResource(tenant, "workspace-1", session, + byte[] history = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).readResource(tenant, "workspace-1", session, original.path("historyRef").path("resourceId").asText(), MEDIA_TOKEN).bytes(); assertThat(history).isEqualTo(Base64.getDecoder().decode( original.required("historyBytesBase64").textValue())); @@ -1410,12 +1412,12 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten assertThat(ledger().get("collected_bytes")).isEqualTo(expected); assertThat(collector(true, Duration.ZERO).runOnce()).isFalse(); assertThat(ledger().get("collected_bytes")).isEqualTo(expected); - assertThatThrownBy(() -> new ManagedSessionStore(jdbc).readResource(tenant, "workspace-1", + assertThatThrownBy(() -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).readResource(tenant, "workspace-1", session, "media-outcome", MEDIA_TOKEN)) .hasFieldOrPropertyWithValue("code", "tool_output_session_retired"); assertThatThrownBy(() -> readRecoveryMedia(original)).hasMessageContaining("resource_collected"); assertMediaReadable("media-other", bytes); - assertThat(new ManagedSessionStore(jdbc).transactions(tenant, "workspace-1", "media-other", + assertThat(com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).transactions(tenant, "workspace-1", "media-other", MEDIA_TOKEN, 0, 100).transactions()).hasSize(other.required("transactions").size()); } @@ -1430,7 +1432,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten + " session_scope_key = ?", Long.class, resScope); boolean reader = "reader".equals(protection); if (!reader) { - tx.executeWithoutResult(status -> new ManagedSessionStore(jdbc).blockRecovery(tenant, + tx.executeWithoutResult(status -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).blockRecovery(tenant, session, MEDIA_TOKEN, new ManagedSessionStoreModels.BlockRecoveryRequest( "workspace-1", "media-writer", 1, "BLOCKED_RESOURCE", "media_evidence"))); } @@ -1464,8 +1466,8 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten commitNativeMedia("media-other"); long expectedBytes = jdbc.queryForObject("SELECT SUM(byte_length) FROM ma_resource WHERE" + " session_scope_key = ?", Long.class, resScope) - mediaBytes(original).length; - String otherScope = ManagedSessionStore.sessionScopeKey(tenant, "media-other"); - String resource = ManagedSessionStore.resourceKey(resScope, "media-outcome"); + String otherScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, "media-other"); + String resource = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(resScope, "media-outcome"); jdbc.update("INSERT INTO ma_resource_ref (session_scope_key, owner_kind, owner_key," + " owner_revision, resource_key, tenant_id, workspace_id, session_id," + " resource_id, gmt_create) VALUES (?, 'RECOVERY', ?, 1, ?, ?," @@ -1489,7 +1491,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten Path script = Path.of("src/test/resources/managed-media-retention-generator.mjs").toAbsolutePath(); Process process = new ProcessBuilder(System.getProperty("node.executable", "node"), script.toString(), tenant, sessionId, output.toString()) - .directory(Path.of("../..").toRealPath().toFile()).redirectErrorStream(true) + .directory(VerificationFixtures.repositoryRoot().toRealPath().toFile()).redirectErrorStream(true) .redirectOutput(log.toFile()).start(); try { assertThat(process.waitFor(30, TimeUnit.SECONDS)).as("native media producer finishes").isTrue(); @@ -1500,7 +1502,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten } } JsonNode fixture = MEDIA_JSON.readTree(output.toFile()); - var store = new ManagedSessionStore(jdbc); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); tx.executeWithoutResult(status -> store.acquireWriter(tenant, sessionId, MEDIA_TOKEN, new ManagedSessionStoreModels.AcquireWriterRequest("workspace-1", "media-writer", 60000L))); for (JsonNode transaction : fixture.required("transactions")) { @@ -1515,7 +1517,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten } private void assertMediaReadable(String sessionId, byte[] expected) { - assertThat(new ManagedSessionStore(jdbc).readResource(tenant, "workspace-1", sessionId, + assertThat(com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).readResource(tenant, "workspace-1", sessionId, "media-outcome", MEDIA_TOKEN).bytes()).isEqualTo(expected); } @@ -1531,7 +1533,7 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten } private void sealMedia(String sessionId) { - tx.executeWithoutResult(status -> new ManagedSessionStore(jdbc).sealWriter(tenant, + tx.executeWithoutResult(status -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).sealWriter(tenant, sessionId, MEDIA_TOKEN, new ManagedSessionStoreModels.SealWriterRequest( "workspace-1", "media-writer", 1))); } @@ -1560,11 +1562,10 @@ public class SessionResourceCollectionCollectorTest extends ToolPublicationReten void mcpEvidenceSurvivesReopenAndConnectionReleaseUntilRetirement(String operationKind) throws Exception { initSession(); - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var journal = tx.execute(status -> new ExtensionRecordJournal(sessions, tenant, "workspace-1", session).open()); - JsonNode templates = new ObjectMapper().readTree(Files.readString(Path.of( - "../core/src/managed-runtime/contracts/managed-mcp-record-v1.fixtures.json"))) + JsonNode templates = new ObjectMapper().readTree(Files.readString(VerificationFixtures.moduleRoot().resolve("../core/src/managed-runtime/contracts/managed-mcp-record-v1.fixtures.json"))) .required("templates"); ObjectNode configuration = templates.required("mcp_configuration").deepCopy(); commitMcp(journal, "configure-intent", "mcp_configuration", configuration, List.of()); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionResourceCollectionMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionRowLockOrderTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionRowLockOrderTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionRowLockOrderTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionRowLockOrderTest.java index 979da2878ec..c81d3c4ae81 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionRowLockOrderTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionRowLockOrderTest.java @@ -111,7 +111,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; // same-file parse can see, so the audit pins the order, not the // annotation. class SessionRowLockOrderTest { - private static final Path STORE_PACKAGE = Path.of("src", "main", "java") + private static final Path STORE_PACKAGE = Path.of("..", "persistence", "src", "main", "java") .resolve(ManagedAgentStore.class.getPackageName().replace('.', '/')); private static final String MANAGED_AGENT_STORE = "ManagedAgentStore.java"; private static final String SESSION_TABLE = "ma_session"; @@ -152,6 +152,7 @@ class SessionRowLockOrderTest { // writes, so it is a turn reader but not a session locker and stays out of // the session-first order audit below. private static final Set SIBLING_TURN_READERS = Set.of( + "JdbcSessionRecoveryRepository.java", "AutomationLedgerStore.java", "ChildResultRelayStore.java", "HostedCleanupDebtStore.java", "ManagedActionStore.java", "ManagedToolDetailsStore.java", @@ -187,40 +188,35 @@ class SessionRowLockOrderTest { Set.of("ChildResultRelayStore.readTree/1")), Map.entry("ChildWorkspaceStore.java", Set.of("ChildWorkspaceStore.find/3", - "ChildWorkspaceStore.row/2", - "Row.snapshot/0")), - // WorkspaceExecutionStore.unavailable shares the local name; + "ChildWorkspaceStore.row/2")), + // com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable shares the local name; // both factories only construct an exception. Map.entry("ExternalStorageRegistrationStore.java", Set.of("ExternalStorageRegistrationStore.unavailable/0")), // Same-package lifecycle refusals share this local name; // both factories only construct an exception. Map.entry("HarnessPlacementStore.java", - Set.of("HarnessPlacementStore.blocked/1")), + Set.of("HarnessPlacementStore.blocked/1", + "HarnessPlacementStore.releaseId/1")), Map.entry("JdbcChannelRouteRepository.java", Set.of("JdbcChannelRouteRepository.row/2")), Map.entry("ManagedAgentDefinitionStore.java", - Set.of("ManagedAgentDefinitionStore.notFound/0", - "ManagedAgentDefinitionStore.row/2", + Set.of("ManagedAgentDefinitionStore.row/2", "ManagedAgentDefinitionStore.update/7")), Map.entry("ManagedArtifactReader.java", Set.of("ManagedArtifactReader.open/1", "ManagedArtifactReader.readRange/3", "ManagedArtifactReader.readRange/5")), Map.entry("ManagedExtensionRecordStore.java", - Set.of("ManagedExtensionRecordStore.parse/1", - "ManagedExtensionRecordStore.sha256/1", + Set.of("ManagedExtensionRecordStore.sha256/1", "ManagedExtensionRecordStore.storedRow/2")), - Map.entry("ManagedExtensionRecords.java", - Set.of("ManagedExtensionRecords.require/2")), + // The closure callback validates resource/JOURNAL ownership // under its caller's pinned head; it never locks Session // rows or accesses Turn rows. - Map.entry("ManagedHostedRecoveryRecords.java", - Set.of("HistoricalResources.checkpointHistory/1")), + // Package configuration delegates to a same-named JSON-only validator. - Map.entry("ManagedMcpSettings.java", - Set.of("ManagedMcpSettings.require/1")), + Map.entry("ManagedSchemaJdbcTemplate.java", Set.of("ManagedSchemaJdbcTemplate.getSingleColumnRowMapper/1")), // Both publisher overloads avoid Session and Turn rows. @@ -234,8 +230,7 @@ class SessionRowLockOrderTest { Map.entry("ManagedToolResultProjector.java", Set.of("ManagedToolResultProjector.require/2")), Map.entry("ManagedToolResultStore.java", - Set.of("ManagedToolResultStore.artifact/2", - "Source.sessionKey/0")), + Set.of("ManagedToolResultStore.artifact/2")), // URLDecoder.decode shares the pure JSON helper's name/arity. Map.entry("ManagedUserMessageStore.java", Set.of("ManagedUserMessageStore.decode/2")), @@ -243,11 +238,9 @@ class SessionRowLockOrderTest { Set.of("ManagedWorkspaceRegistry.findReadable/3")), // Builder/API receiver calls share local names; retirement // has no Turn access and unavailable is a pure refusal. - Map.entry("ResidentWorkspaceRetirementMain.java", - Set.of("ResidentWorkspaceRetirementMain.run/1")), + Map.entry("ResidentWorkspaceRetirementStore.java", - Set.of("ResidentWorkspaceRetirementStore.deletePod/3", - "ResidentWorkspaceRetirementStore.unavailable/0")), + Set.of("ResidentWorkspaceRetirementStore.unavailable/0")), Map.entry("SessionMessageRelayStore.java", Set.of("SessionMessageRelayStore.pending/2")), Map.entry("SessionResourceCollectionCollector.java", @@ -255,21 +248,18 @@ class SessionRowLockOrderTest { // Value projections and the JDBC row mapper reach neither // Session locks nor Turn rows. Map.entry("SessionWorktreeStore.java", - Set.of("Descriptor.worktreeId/0", "Plan.source/0", "Plan.target/1", - "Row.descriptor/0", "Row.descriptor/1", "SessionWorktreeStore.row/2")), + Set.of("SessionWorktreeStore.row/2")), // D7-2's parked registration asks its record whether it // reserved, through a record accessor the parse cannot // resolve. Map.entry("SshWorkspaceStore.java", - Set.of("Parked.reserved/0", - "SshWorkspaceStore.physicalKey/1")), + Set.of("SshWorkspaceStore.physicalKey/1")), Map.entry("ToolPublicationAdmissionStore.java", Set.of("AcknowledgementBundle.captureIdentity/0", "AcknowledgementBundle.finished/0", "AcknowledgementBundle.outcomeRef/0", "ToolPublicationAdmissionStore.usesDataSource/1")), - Map.entry("ToolPublicationContract.java", - Set.of("ToolPublicationContract.id/1")), + Map.entry("ToolPublicationDataStore.java", Set.of("ToolPublicationDataStore.hash/1", "ToolPublicationDataStore.lockOriginalSettledResult/3", @@ -295,24 +285,32 @@ class SessionRowLockOrderTest { "WorkspaceCsiApplicationHistory.readFrozenOwner/2")), Map.entry("WorkspaceCsiApplicationInventory.java", Set.of("Cell.bytes/0|Row.bytes/0|Snapshot.bytes/0", - "Cell.equals/1", "Cell.hashCode/0", - "Cell.isNull/0", "Cell.kind/0", "Cell.text/0", + "Cell.equals/1", + "Cell.hashCode/0", + "Cell.isNull/0", + "Cell.kind/0", + "Cell.text/0", "Encoder.write/1", "FrozenOwnerReader.read/2|Inspection.read/2", "Inspection.context/2", - "Kind.accepts/1", "Kind.binary/0", "Kind.integral/0", + "Kind.accepts/1", + "Kind.binary/0", + "Kind.integral/0", "NativeReadEvidence.number/1|Row.number/1", "NativeReadEvidence.text/1|Row.text/1", - "Row.fields/0", "Row.key/0", "Row.membership/0", - "Row.withMembership/1", "Snapshot.table/1", "Snapshot.tables/0", + "Row.fields/0", + "Row.key/0", + "Row.membership/0", + "Row.withMembership/1", + "Snapshot.table/1", + "Snapshot.tables/0", "WorkspaceCsiApplicationInventory.compareKeys/2", - "WorkspaceCsiApplicationInventory.encode/1", "WorkspaceCsiApplicationInventory.hash/1", "WorkspaceCsiApplicationInventory.key/1", "WorkspaceCsiApplicationInventory.parse/1", "WorkspaceCsiApplicationInventory.write/2")), Map.entry("WorkspaceCsiApplicationStore.java", - Set.of("Sql.run/1", "WorkspaceCsiApplicationStore.encode/1")), + Set.of("Sql.run/1")), Map.entry("WorkspaceCsiApplicationWorkerMatch.java", Set.of("WorkspaceCsiApplicationWorkerMatch.number/1", "WorkspaceCsiApplicationWorkerMatch.object/1")), @@ -324,14 +322,12 @@ class SessionRowLockOrderTest { "WorkspaceCsiCheckpointSnapshotStore.scope/1")), // Text dispatch and environment delegation share local // method names without adding session/turn SQL. - Map.entry("WorkspaceCsiHostedMain.java", - Set.of("WorkspaceCsiHostedMain.required/1")), + Map.entry("WorkspaceCsiRuntimeAccess.java", Set.of("WorkspaceCsiRuntimeAccess.attest/3", "WorkspaceCsiRuntimeAccess.request/0", "WorkspaceCsiRuntimeAccess.unavailable/0")), - Map.entry("WorkspaceCsiRuntimeMain.java", - Set.of("WorkspaceCsiRuntimeMain.run/1")), + Map.entry("WorkspaceCsiWorkerAckStore.java", Set.of("WorkspaceCsiWorkerAckStore.decode/1", "WorkspaceCsiWorkerAckStore.hash/1")), @@ -347,13 +343,9 @@ class SessionRowLockOrderTest { Map.entry("WorkspaceProfileLifecycleVerifier.java", Set.of("WorkspaceProfileLifecycleVerifier.digest/0", "WorkspaceProfileLifecycleVerifier.source/1")), - Map.entry("WorkspaceRecoveryStore.java", - Set.of("WorkspaceRecoveryStore.digest/0", - "WorkspaceRecoveryStore.hash/1", - "WorkspaceRecoveryStore.parse/1")), + Map.entry("WorkspaceStorageGuard.java", - Set.of("IdentityReader.read/1", - "WorkspaceStorageGuard.verify/1"))); + Set.of("WorkspaceStorageGuard.verify/1"))); private static final Set DEFERRED_BODIES = Set.of(); @@ -512,8 +504,11 @@ class SessionRowLockOrderTest { // whose arity a method reference never spells — are pinned exactly: // a new unresolvable reference fails here, and an entry goes stale // the day the call is typed or moved. + // The nine-argument default delegate is covered through a real Spring proxy in + // AgentStateStoreProxyTransactionTest; its deepest writer is audited here. assertThat(audit.exemptions()) .containsExactly("ItemRow.toRecord/0", + "ManagedAgentStore.insertSessionCommand/9", "ManagedAgentStore.readMap/1", "ManagedAgentStore.renameDelivery/2"); // The harvest is asserted against the bytecode's own enumeration, diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStoreTest.java similarity index 84% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStoreTest.java index a907b9b6add..e5380fc9c1a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SessionWorktreeStoreTest.java @@ -1,10 +1,14 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; +import com.alibaba.qwen.code.managedagent.adapters.workspace.ChildWorktreeGit; +import com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles; +import com.alibaba.qwen.code.managedagent.adapters.workspace.SessionWorktreeGit; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore.Plan; -import com.alibaba.qwen.code.managedagent.workspace.ChildWorktreeGit; -import com.alibaba.qwen.code.managedagent.workspace.SessionWorktreeGit; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles; +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Plan; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; @@ -53,10 +57,10 @@ class SessionWorktreeStoreTest { properties.getRuntimeBroker().setVerifiedWorkspaceRecoveryEnabled(true); var registry = new ManagedWorkspaceRegistry(jdbc); var guard = new WorkspaceStorageGuard(jdbc, manager, properties, - path -> new WorkspaceStorageGuard.Identity(path.toString(), "test-host", "1", "2", "2026-10-15T00:00:00.123456789Z")); + path -> new StorageIdentityReader.Identity(path.toString(), "test-host", "1", "2", "2026-10-15T00:00:00.123456789Z")); workspace = new WorkspaceCreationStore(jdbc, manager, guard, registry, properties, new ObjectMapper(), Clock.systemUTC()) .create("tenant", "actor", "Worktrees", "create").workspaceId(); - store = new SessionWorktreeStore(jdbc, registry, guard); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new SessionWorktreeStore(jdbc, registry, guard)); } @Test @@ -71,6 +75,7 @@ class SessionWorktreeStoreTest { .withBean(WorkspaceStorageGuard.class, () -> org.mockito.Mockito.mock(WorkspaceStorageGuard.class)) .withBean(SshWorkspaceStore.class, () -> org.mockito.Mockito.mock(SshWorkspaceStore.class)) .withBean(ManagedAgentProperties.class, ManagedAgentProperties::new) + .withBean(WorktreeFiles.class, LocalWorktreeFiles::new) .run(context -> { assertThat(context).hasNotFailed(); assertThat(context.getBean(SessionWorktreeStore.class).describe("tenant", "missing")).isNull(); @@ -100,11 +105,11 @@ class SessionWorktreeStoreTest { jdbc.update("INSERT INTO ma_session (tenant_id,session_id,agent_id,gmt_create,gmt_modified) VALUES ('tenant','session','agent',1,1)"); store.admit("tenant", "actor", "session", plan, resolved); }); - var sessions = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); assertThat(sessions.listSessions("tenant", null, null, null, 1).worktrees().get("session").state()) .isEqualTo("preparing"); - assertThat(SessionWorktreeStore.isReady(jdbc, "tenant", "session")).isFalse(); + assertThat(SessionWorktreeStore.isReady(new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles(), jdbc, "tenant", "session")).isFalse(); assertThat(store.due()).hasSize(1); var candidate = store.due().getFirst(); SessionWorktreeStore.Row claim; @@ -125,7 +130,7 @@ class SessionWorktreeStoreTest { claim = claims.stream().filter(java.util.Objects::nonNull).findFirst().orElseThrow(); } assertThat(claim).isNotNull(); - String physical = WorkspaceExecutionStore.storageKey(jdbc, SessionWorktreeStore.storageBinding(plan)); + String physical = WorkspaceExecutionStore.storageKey(jdbc, com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.storageBinding(plan)); jdbc.update("UPDATE ma_storage_owner SET holder_key = ?, maintenance_id = ?, maintenance_generation = ? WHERE storage_key = ?", "live-step", claim.operationId(), claim.generation(), physical); assertThat(store.claim(claim, "other", 120000)).isNull(); @@ -137,20 +142,20 @@ class SessionWorktreeStoreTest { var ready = sessions.listSessions("tenant", null, null, null, 1).worktrees().get("session"); assertThat(ready.id()).isEqualTo(id); assertThat(ready.state()).isEqualTo("ready"); - SessionWorktreeStore.requireReady(jdbc, "tenant", "session"); + SessionWorktreeStore.requireReady(new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles(), jdbc, "tenant", "session"); assertThat(store.due()).isEmpty(); assertThat(store.list("tenant", "actor", workspace)).hasSize(1); assertThatThrownBy(() -> SessionWorktreeStore.requireCwd(jdbc, "tenant", "session", "project")) .hasMessageContaining("worktree_cwd_outside"); Files.move(location.root().resolve(cwd), location.root().resolve(cwd + "-saved")); assertThatThrownBy(() -> transaction.execute(status -> { - SessionWorktreeStore.requireReady(jdbc, "tenant", "session"); + SessionWorktreeStore.requireReady(new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles(), jdbc, "tenant", "session"); return null; })).hasMessageContaining("worktree_identity_conflict"); assertThat(sessions.listSessions("tenant", null, null, null, 1).worktrees().get("session").state()) .isEqualTo("blocked"); - assertThat(SessionWorktreeStore.descriptor(jdbc, "tenant", "session").state()).isEqualTo("blocked"); - assertThatThrownBy(() -> SessionWorktreeStore.requireReady(jdbc, "tenant", "session")) + assertThat(SessionWorktreeStore.descriptor(new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles(), jdbc, "tenant", "session").state()).isEqualTo("blocked"); + assertThatThrownBy(() -> SessionWorktreeStore.requireReady(new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles(), jdbc, "tenant", "session")) .hasMessageContaining("worktree_identity_conflict"); } } @@ -188,10 +193,10 @@ class SessionWorktreeStoreTest { assertThat(store.describe("tenant", "cleanup-session").state()).isEqualTo("ready"); var removal = store.cleanup("tenant", "actor", workspace, id, "remove"); assertThat(store.cleanup("tenant", "actor", workspace, id, "remove").operationId()).isEqualTo(removal.operationId()); - var execution = new WorkspaceExecutionStore(jdbc, new DataSourceTransactionManager(jdbc.getDataSource())); - var service = new com.alibaba.qwen.code.managedagent.workspace.SessionWorktreeService(store, execution, + var execution = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, new DataSourceTransactionManager(jdbc.getDataSource()))); + var service = VerificationFixtures.configure(new com.alibaba.qwen.code.managedagent.workspace.SessionWorktreeService(store, execution, new ManagedAgentProperties(), org.mockito.Mockito.mock(ManagedAgentStore.class), - org.mockito.Mockito.mock(com.alibaba.qwen.code.managedagent.session.HarnessCoordinator.class)); + org.mockito.Mockito.mock(com.alibaba.qwen.code.managedagent.session.HarnessCoordinator.class))); try { service.drive(removal); } finally { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SharedLocalProjectsMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SharedLocalProjectsMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SharedLocalProjectsMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SharedLocalProjectsMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SharedLocalProjectsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SharedLocalProjectsTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SharedLocalProjectsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SharedLocalProjectsTest.java index 80fdc962126..7b72a0e1012 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SharedLocalProjectsTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SharedLocalProjectsTest.java @@ -1,13 +1,14 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -51,7 +52,7 @@ class SharedLocalProjectsTest { JdbcTemplate jdbc = new ManagedSchemaJdbcTemplate(source); var manager = new DataSourceTransactionManager(source); var guard = new WorkspaceStorageGuard(jdbc, manager, properties, - path -> new WorkspaceStorageGuard.Identity(path.toString(), "test-host", "device", + path -> new StorageIdentityReader.Identity(path.toString(), "test-host", "device", Files.readAttributes(path, BasicFileAttributes.class).fileKey().toString(), "birth")); var store = new WorkspaceCreationStore(jdbc, manager, guard, new ManagedWorkspaceRegistry(jdbc), properties, new ObjectMapper(), Clock.systemUTC()); @@ -156,7 +157,7 @@ class SharedLocalProjectsTest { () -> catalog.guard().migrationSource(catalog.tenant(), storage, 1, operation), () -> catalog.guard().publishMigrationMarker(project.getParent(), new byte[0], registration, operation), () -> catalog.guard().promoteMigration(registration, catalog.guard().identity(project.getParent()), operation, operation), - () -> new WorkspaceExecutionStore(catalog.jdbc(), catalog.manager(), catalog.guard(), catalog.properties()) + () -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(catalog.jdbc(), catalog.manager(), catalog.guard(), catalog.properties())) .beginRewind(catalog.binding(imported), mock(RuntimeSessionRecord.class))); for (Runnable action : exclusive) { assertThatThrownBy(action::run).isInstanceOfSatisfying(RuntimeBrokerException.class, @@ -171,7 +172,7 @@ class SharedLocalProjectsTest { void legacyImportedReceiptKeepsItsOriginalMarkerContract() throws Exception { Path project = project(); Catalog legacy = catalog("legacy"); - byte[] actor = ManagedWorkspaceRegistry.actorKey(legacy.tenant(), "actor"); + byte[] actor = com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey(legacy.tenant(), "actor"); String scope = ManagedReceipts.actorScope(actor); String key = ManagedSchemaKeys.receiptKey(ReceiptNamespace.WORKSPACE_CREATE, legacy.tenant(), ReceiptScope.ACTOR, scope, "import"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStoreTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStoreTest.java index 6e724e6bb1a..7e9f372315a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/SshWorkspaceStoreTest.java @@ -2,13 +2,11 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.SshRuntimeSettings; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; @@ -17,9 +15,11 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -78,7 +78,7 @@ class SshWorkspaceStoreTest { assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ma_workspace", Integer.class)).isEqualTo(1); assertThat(store.verify(binding(intent))).isTrue(); assertThat(WorkspaceExecutionStore.storageKey(jdbc, binding(intent))) - .isEqualTo(SshWorkspaceStore.physicalKey(SshWorkspaceStore.object(registration, "identity"))); + .isEqualTo(SshWorkspaceStore.physicalKey(com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.object(registration, "identity"))); } @Test @@ -152,7 +152,7 @@ class SshWorkspaceStoreTest { jdbc.update("INSERT INTO ma_workspace (tenant_id, workspace_id, workspace_generation, storage_id, display_name, config_ref, policy_ref, state)" + " VALUES ('tenant', 'local', 1, 'local', 'Local', ?, ?, 'ACTIVE')", WorkspaceExecutionProfile.CONFIG_REF, WorkspaceExecutionProfile.POLICY_REF); jdbc.update("INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role) VALUES ('tenant', 'local', ?, 'OWNER')", - ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); var local = transaction.execute(ignored -> sessions.insertWorkspaceSessionCommand("tenant", "actor", "local", "digest", agent, null, null, input, "payload", new WorkspaceSelection("local", "."))); assertThat(sessions.requireSession("tenant", local.sessionId()).toolProfile()).isEqualTo(WorkspaceToolProfiles.MCP); @@ -178,8 +178,8 @@ class SshWorkspaceStoreTest { private ManagedAgentStore sessions(ManagedAgentProperties properties) { var registry = new ManagedWorkspaceRegistry(jdbc); registry.configureSsh(properties); - return new ManagedAgentStore(jdbc, new com.fasterxml.jackson.databind.ObjectMapper(), - Clock.systemUTC(), ignored -> {}, registry, properties); + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new com.fasterxml.jackson.databind.ObjectMapper(), + Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } @Test @@ -219,8 +219,8 @@ class SshWorkspaceStoreTest { var first = runtimeSession(intent, "first"); var second = runtimeSession(intent, "second"); var manager = new DataSourceTransactionManager(jdbc.getDataSource()); - var one = new WorkspaceExecutionStore(jdbc, manager); - var two = new WorkspaceExecutionStore(jdbc, manager); + var one = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, manager)); + var two = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, manager)); var binding = binding(intent); one.claim(binding, first); two.claim(binding, second); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGate.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGate.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGate.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGate.java index c75a3c91917..2d24d7ca0a5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGate.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGate.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -65,18 +65,18 @@ public final class StoreFaultGate { } /** FG13a: the fault is owed to the transaction's after-commit callback. */ - boolean ackArmed() { + public boolean ackArmed() { return loseAck && phase != null; } - void inspect(byte[] recordBytes, + public void inspect(byte[] recordBytes, Function resources) { if (!loseAck) { fault(recordBytes, resources, "Store fault injected: "); } } - void afterApply(byte[] recordBytes, + public void afterApply(byte[] recordBytes, Function resources) { if (loseAck) { fault(recordBytes, resources, "Store ack lost: "); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGateTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGateTest.java index ad83724af8e..f0292784dde 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGateTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreFaultGateTest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.StoredResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.StoredResource; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreModelsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreModelsTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreModelsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreModelsTest.java index 41ebc13f731..5750e2a8f60 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreModelsTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/StoreModelsTest.java @@ -56,7 +56,7 @@ class StoreModelsTest { // cannot identify a recorder. @Test void admitsReplayByFollowsTheRecorderStamp() { - byte[] recorder = ManagedWorkspaceRegistry.actorKey("tenant", "actor-a"); + byte[] recorder = com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor-a"); assertThat(command(recorder).admitsReplayBy("actor-a")).isTrue(); assertThat(command(recorder).admitsReplayBy("actor-b")).isFalse(); @@ -84,7 +84,7 @@ class StoreModelsTest { "digest", "session", "turn", "COMPLETED", null, 1); ManagedReceipts.insertCommand(jdbc, "tenant", "SUBMIT_TURN", "stamped", "digest", "session", "turn", "COMPLETED", null, - ManagedWorkspaceRegistry.actorKey("tenant", "actor-a"), 1); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor-a"), 1); byte[] unattributed = ManagedReceipts.command(jdbc, "tenant", "CANCEL_TURN", "server-keyed", false).actorKey(); @@ -94,7 +94,7 @@ class StoreModelsTest { assertThat(unattributed).isNotNull().isEmpty(); assertThat(command(unattributed).admitsReplayBy("actor-b")).isFalse(); assertThat(stamped).isEqualTo( - ManagedWorkspaceRegistry.actorKey("tenant", "actor-a")); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor-a")); } private static CommandRecord command(byte[] actorKey) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAcknowledgementMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAcknowledgementMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAcknowledgementMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAcknowledgementMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAcknowledgementTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAcknowledgementTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAcknowledgementTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAcknowledgementTest.java index 2a98f675f41..22f089895f7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAcknowledgementTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAcknowledgementTest.java @@ -1,9 +1,9 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; @@ -13,9 +13,12 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -93,7 +96,7 @@ class ToolPublicationAcknowledgementTest { ManagedSchema.initialize(source); jdbc = new ManagedSchemaJdbcTemplate(source); manager = new DataSourceTransactionManager(source); - sessions = new ManagedSessionStore(jdbc); + sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); bindings = new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("fixture", new byte[32])); runtimeSessions = new JdbcRuntimeSessionRepository(source); executions = new JdbcToolExecutionRepository(source); @@ -368,7 +371,7 @@ class ToolPublicationAcknowledgementTest { void mismatchedSessionAuthorityAtEveryInjectionSiteRefusesBeforeRpcAndObjectReads(String component) { var other = new JdbcDataSource(); other.setURL("jdbc:h2:mem:foreign-session-" + UUID.randomUUID()); - var foreignSessions = new ManagedSessionStore(new ManagedSchemaJdbcTemplate(other)); + var foreignSessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(new ManagedSchemaJdbcTemplate(other))); var selectedGrants = new ToolPublicationStore(jdbc, manager, "grants".equals(component) ? foreignSessions : sessions, executions, bindings, new ToolPublicationStore.Capacity(2048, 16 * 1024 * 1024, 16 * 1024 * 1024, 10)); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAsyncVerificationMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAsyncVerificationMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAsyncVerificationMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAsyncVerificationMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAsyncVerificationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAsyncVerificationTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAsyncVerificationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAsyncVerificationTest.java index 8878920d7f2..e7521b186b2 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAsyncVerificationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationAsyncVerificationTest.java @@ -1,15 +1,22 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.PublicationJournalFixture; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationStorageException; +import com.alibaba.qwen.code.managedagent.config.ManagedSessionReaderVersion; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationStorageException; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.StorageReadPolicy; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.security.WriterCredentialPolicy; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.security.WriterCredentialPolicy; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRules; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -19,7 +26,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.http.HttpStatus; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; @@ -309,6 +315,8 @@ public class ToolPublicationAsyncVerificationTest { .withBean("managedAgentTransactionManager", PlatformTransactionManager.class, () -> fixture.manager) .withBean(WriterCredentialPolicy.class, WriterCredentialPolicy::unbound) .withBean(ManagedSessionStore.class, () -> fixture.sessions) + .withBean(HarnessRules.class, SdkHarnessRules::new) + .withBean(StorageReadPolicy.class, ManagedSessionReaderVersion::new) .withBean(com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository.class, () -> fixture.executions) .withBean(com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository.class, () -> fixture.bindings) .withBean(org.springframework.boot.task.ThreadPoolTaskSchedulerBuilder.class, @@ -457,7 +465,7 @@ public class ToolPublicationAsyncVerificationTest { void transientVerificationErrorsRetryBeforeAndAfterClaim(boolean beforeClaim, String code) { data = replacement(Duration.ofMinutes(5), Duration.ofSeconds(2)); publish("retry", 0, "bytes"); - var error = new ManagedAgentException(HttpStatus.CONFLICT, code, "Temporary contention"); + var error = new ManagedAgentException(409, code, "Temporary contention"); if (beforeClaim) { var grants = spy(fixture.store); doThrow(error).doCallRealMethod().when(grants) diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollectorMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollectorMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollectorMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollectorMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollectorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollectorTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollectorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollectorTest.java index 2d1f2a4f939..7709079ea14 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollectorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationCollectorTest.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -353,7 +353,7 @@ public class ToolPublicationCollectorTest extends ToolPublicationRetentionStoreT @Test void resourceHandoffRetriesWithinSecondsUntilTheSessionLedgerSettles() { addObject("one", "handoff/exact-key", null); - String resourceScope = ManagedSessionStore.sessionScopeKey(tenant, session); + String resourceScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); jdbc.update("INSERT INTO ma_resource_ref (session_scope_key, owner_kind, owner_key," + " owner_revision, resource_key, tenant_id, workspace_id, session_id, resource_id," + " journal_revision, gmt_create) VALUES (?, 'JOURNAL', ?, 1, ?, ?, 'workspace-1', ?," @@ -483,10 +483,10 @@ public class ToolPublicationCollectorTest extends ToolPublicationRetentionStoreT for (String resource : List.of("target-result", "sibling-result")) { jdbc.update("UPDATE ma_resource SET state = 'REFERENCED', publish_command_id = 'publish' WHERE session_scope_key = ? AND resource_id = ?", - ManagedSessionStore.sessionScopeKey(tenant, session), resource); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), resource); } for (String resource : List.of("message-1", "checkpoint-1")) { - String resourceScope = ManagedSessionStore.sessionScopeKey(tenant, session); + String resourceScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create) VALUES (?, ?, ?, 'workspace-1', ?, ?, ?, 1, 1, ?, 'MYSQL_INLINE', ?, 'publish', 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ")", ManagedSchemaKeys.key(resourceScope, resource), resourceScope, tenant, session, resource, resource.equals("message-1") ? "managed-message" : "managed-checkpoint", scope, new byte[] {2}); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionClockFixtureMain.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionClockFixtureMain.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionClockFixtureMain.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionClockFixtureMain.java index fedbd01eb52..28ed4b45f7d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionClockFixtureMain.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionClockFixtureMain.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import com.fasterxml.jackson.databind.ObjectMapper; @@ -25,7 +26,7 @@ public final class ToolPublicationRetentionClockFixtureMain { var manager = new DataSourceTransactionManager(source); var tx = new TransactionTemplate(manager); var retention = new ToolPublicationRetentionStore(jdbc, manager); - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String tenant = "clock-" + UUID.randomUUID(); String session = "session-clock"; var key = new ObjectMapper().createObjectNode().put("tenantId", tenant) diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStoreTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStoreTest.java index f433a0fab6f..1b8e6c774a5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/ToolPublicationRetentionStoreTest.java @@ -1,13 +1,14 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.h2.jdbcx.JdbcDataSource; @@ -96,7 +97,7 @@ public class ToolPublicationRetentionStoreTest { Long.class, tenant)).isEqualTo(first); assertThatThrownBy(() -> retention.read(key)).isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("tool_output_session_retired")); - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); assertThatThrownBy(() -> tx.executeWithoutResult(status -> sessions.acquireWriter(tenant, session, "a".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace-1", "writer", 60000L)))) .isInstanceOf(ManagedAgentException.class); @@ -316,7 +317,7 @@ public class ToolPublicationRetentionStoreTest { "READY", "BLOCKED_RESOURCE", "BLOCKED_WORKSPACE", "BLOCKED_EXECUTION" }) void retirementDerivesRecoveryProtectionFromThePrivateHead(String recovery) { - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); tx.executeWithoutResult(status -> sessions.acquireWriter(tenant, session, "a".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace-1", "writer", 60000L))); tx.executeWithoutResult(status -> sessions.sealWriter(tenant, session, "a".repeat(32), @@ -331,7 +332,7 @@ public class ToolPublicationRetentionStoreTest { @Test void writerAcquisitionAndRetirementSerializeEvenWithoutAnExistingHead() throws Exception { - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var locked = new CountDownLatch(1); var release = new CountDownLatch(1); try (var executor = java.util.concurrent.Executors.newFixedThreadPool(2)) { @@ -357,15 +358,15 @@ public class ToolPublicationRetentionStoreTest { @Test void publicDeleteAndPrivateRetirementCommitAtomicallyAfterWriterStops() { var props = new ManagedAgentProperties(); - var publicStore = new ManagedAgentStore(jdbc, new ObjectMapper(), java.time.Clock.systemUTC(), events -> {}, - org.mockito.Mockito.mock(ManagedWorkspaceRegistry.class), props); + var publicStore = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), java.time.Clock.systemUTC(), events -> {}, + org.mockito.Mockito.mock(ManagedWorkspaceRegistry.class), props, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String original = session; session = tx.execute(status -> publicStore.insertSessionCommand(tenant, "CREATE_SESSION", "create", "digest", "qwen-code", null, null, java.util.List.of(), null).sessionId()); key.put("sessionId", session); jdbc.update("UPDATE ma_output_capture SET session_id = ? WHERE tenant_id = ? AND session_id = ?", session, tenant, original); - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); tx.executeWithoutResult(status -> sessions.acquireWriter(tenant, session, "a".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace-1", "writer", 60000L))); var operation = tx.execute(status -> publicStore.beginOperation(tenant, session, StoreModels.OperationKind.DELETE, @@ -394,7 +395,7 @@ public class ToolPublicationRetentionStoreTest { @Test void liveWriterPreventsRetirementAndDefaultsDisablePhysicalDeletion() { - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); tx.executeWithoutResult(status -> sessions.acquireWriter(tenant, session, "a".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace-1", "writer", 60000L))); assertThatThrownBy(this::retire).isInstanceOf(ManagedAgentException.class); @@ -463,7 +464,7 @@ public class ToolPublicationRetentionStoreTest { return super.update(sql, arguments); } }; - var sessions = new ManagedSessionStore(observed); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(observed)); tx.executeWithoutResult(status -> sessions.acquireWriter(tenant, session, "a".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace-1", "writer", 60000L))); assertThatThrownBy(() -> sessions.readResource(tenant, "workspace-1", session, "resource-1", "b".repeat(32))) @@ -500,7 +501,7 @@ public class ToolPublicationRetentionStoreTest { protected void addCaptureObject(String captureScope, String publication, String captureSession, String slot, String resourceId, String objectKey, byte[] inline, String captureState) { - String resourceScope = ManagedSessionStore.sessionScopeKey(tenant, captureSession); + String resourceScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, captureSession); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, state, scope_key, publication_id, slot_key, capture_state, capture_object_key, inline_bytes, operation_id, gmt_create) VALUES (?, ?, ?, 'workspace-1', ?, ?, 'managed-tool-result-content', 1, ?, ?, ?, 'PUBLISHED', ?, ?, ?, ?, ?, ?, 'op', " + ManagedSchemaTime.SQL_NOW + ")", ManagedSchemaKeys.key(resourceScope, resourceId), resourceScope, tenant, captureSession, resourceId, inline == null ? 1 : inline.length, ToolPublicationRetentionStore.hash(slot), @@ -517,7 +518,7 @@ public class ToolPublicationRetentionStoreTest { + " tenant_id, workspace_id, session_id, publication_id, target_kind, target_key, request_digest," + " request_json, state, phase, gmt_create, gmt_modified) VALUES (?, ?, ?, ?, ?, ?, 'workspace-1', ?, ?," + " 'OUTPUT_CAPTURE', ?, ?, '{}', ?, 'TEST', ?, ?)", - operationKey, ManagedSessionStore.sessionScopeKey(tenant, targetSession), opScope, kind.name(), id, + operationKey, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, targetSession), opScope, kind.name(), id, tenant, targetSession, publication, target, "b".repeat(64), state, time, time); return operationKey; } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStoreTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStoreTest.java index 6ab198c8d51..08755dcf5e1 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WakeRecoveryStoreTest.java @@ -1,7 +1,8 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.codec.digest.DigestUtils; @@ -142,7 +143,7 @@ public class WakeRecoveryStoreTest { append("input.accepted", "turn", "monitor"); var pending = candidate(); assertThat(store.claim(pending)).isTrue(); - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var transactions = new TransactionTemplate(new DataSourceTransactionManager(jdbc.getDataSource())); for (String status : List.of("CLOSING", "CLOSED")) { jdbc.update("UPDATE ma_session SET status = ?", status); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStoreTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStoreTest.java index 6f7bfc4f0ae..0b9fd7b4f18 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCreationStoreTest.java @@ -1,8 +1,10 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; @@ -58,7 +60,7 @@ class WorkspaceCreationStoreTest { properties.getRuntimeBroker().setVerifiedWorkspaceRecoveryEnabled(true); registry = new ManagedWorkspaceRegistry(jdbc); guard = new WorkspaceStorageGuard(jdbc, new DataSourceTransactionManager(source), properties, - path -> new WorkspaceStorageGuard.Identity(path.toString(), "test-host", "device", + path -> new StorageIdentityReader.Identity(path.toString(), "test-host", "device", Files.readAttributes(path, BasicFileAttributes.class).fileKey().toString(), "2026-10-15T00:00:00.123456789Z")); } @@ -181,8 +183,8 @@ class WorkspaceCreationStoreTest { void initialToolTurnAdmitsCompletedDynamicStorage() { var created = store(guard).create("tenant", "actor", "Tools", "tools"); properties.getHarness().setWorkspaceFilesEnabled(true); - var sessions = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), - ignored -> {}, registry, properties); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), + ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var admitted = new TransactionTemplate(new DataSourceTransactionManager(source)).execute(status -> sessions.insertWorkspaceSessionCommand("tenant", "actor", "session-key", "digest", "qwen-code", null, null, List.of(java.util.Map.of("type", "input_text", "text", "Read a file")), @@ -402,7 +404,7 @@ class WorkspaceCreationStoreTest { .isEqualTo(project.getParent()); guard.verify(new ContextBinding("tenant", created.workspaceId(), 1, storage(created.workspaceId()), "first", "config", 1)); properties.getHarness().setWorkspaceFilesEnabled(true); - var sessions = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, registry, properties); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var admitted = new TransactionTemplate(new DataSourceTransactionManager(source)).execute(status -> sessions.insertWorkspaceSessionCommand("tenant", "actor", "local-session", "digest", "qwen-code", null, null, List.of(java.util.Map.of("type", "input_text", "text", "Read README.md")), "payload-digest", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryTest.java similarity index 87% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryTest.java index abde07f4a64..133fab54192 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationHistoryTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; @@ -31,10 +31,10 @@ class WorkspaceCsiApplicationHistoryTest { var manager = mock(PlatformTransactionManager.class); var bindings = new JdbcRuntimeBindingRepository(first, new AesGcmSecretProtector("test", new byte[32])); assertThatThrownBy(() -> new WorkspaceCsiApplicationHistory(jdbc, manager, bindings, - new ManagedSessionStore(new JdbcTemplate(second)), new ObjectMapper())).isInstanceOf(IllegalArgumentException.class); + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(new JdbcTemplate(second))), new ObjectMapper())).isInstanceOf(IllegalArgumentException.class); var foreign = new JdbcRuntimeBindingRepository(second, new AesGcmSecretProtector("test", new byte[32])); assertThatThrownBy(() -> new WorkspaceCsiApplicationHistory(jdbc, manager, foreign, - new ManagedSessionStore(jdbc), new ObjectMapper())).isInstanceOf(IllegalArgumentException.class); + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)), new ObjectMapper())).isInstanceOf(IllegalArgumentException.class); verifyNoInteractions(manager); } @@ -45,7 +45,7 @@ class WorkspaceCsiApplicationHistoryTest { var manager = mock(PlatformTransactionManager.class); var reader = new WorkspaceCsiApplicationHistory(jdbc, manager, new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("test", new byte[32])), - new ManagedSessionStore(jdbc), new ObjectMapper()); + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)), new ObjectMapper()); boolean previous = TransactionSynchronizationManager.isActualTransactionActive(); TransactionSynchronizationManager.setActualTransactionActive(true); try { @@ -103,7 +103,7 @@ class WorkspaceCsiApplicationHistoryTest { var manager = new DataSourceTransactionManager(source); var reader = new WorkspaceCsiApplicationHistory(jdbc, manager, new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("test", new byte[32])), - new ManagedSessionStore(jdbc), new ObjectMapper()); + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)), new ObjectMapper()); // H2 ignores setReadOnly; the production guard must reject that actual Connection before SQL. assertThatThrownBy(() -> read(reader)) .isInstanceOf(IllegalArgumentException.class); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventoryMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventoryMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventoryMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventoryMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventoryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventoryTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventoryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventoryTest.java index be917fbce4e..917924ae5c7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventoryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiApplicationInventoryTest.java @@ -1,16 +1,22 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiApplicationJson; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiFilesRetirementGuard; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiFilesRetirementGuard; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -69,14 +75,14 @@ class WorkspaceCsiApplicationInventoryTest { + " VALUES ('tenant', 'workspace', 3, 'storage', 'CSI', ?, ?, 'ACTIVE')", CsiFilesRetirementProfile.CONFIG_REF, CsiFilesRetirementProfile.POLICY_REF); jdbc.update("INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" - + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); var properties = new ManagedAgentProperties(); properties.setAgentRevision("reviewed-agent/1"); - session = WorkspaceCsiSessionMain.create(jdbc, manager, json, properties, - new WorkspaceCsiSessionMain.Request(registration, "actor", "create", null, null, + session = com.alibaba.qwen.code.managedagent.persistence.PrivateCsiFixture.create(jdbc, manager, json, properties, + new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request(registration, "actor", "create", null, null, new WorkspaceSelection("workspace", "."))).sessionId(); - var managed = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + var managed = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var transaction = new TransactionTemplate(manager); var request = transaction.execute(status -> managed.requireCsiRequest(registration, session)); var bindings = new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("test", new byte[32])); @@ -94,7 +100,7 @@ class WorkspaceCsiApplicationInventoryTest { @Test void liveInventoryFindsForeignCanonicalResourceReferencesWithoutTheirParentSession() throws Exception { - String resource = ManagedSessionStore.resourceKey(sessionScope(), "original-resource"); + String resource = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(sessionScope(), "original-resource"); byte[] bytes = "{}".getBytes(StandardCharsets.UTF_8); jdbc.update("INSERT INTO ma_resource (resource_key,session_scope_key,tenant_id,workspace_id,session_id,resource_id," + "kind,schema_version,byte_length,sha256,storage_kind,inline_bytes,state,gmt_create)" @@ -108,7 +114,7 @@ class WorkspaceCsiApplicationInventoryTest { + " VALUES ('foreign','foreign-session','item','turn','message','completed','{}',1,1,1,1,'[]',?)", resource); jdbc.update("INSERT INTO ma_event_stream (tenant_id,session_id,stream_kind,stream_id,artifact_refs_resource_key,gmt_modified)" + " VALUES ('foreign','foreign-session','SESSION','',?,1)", resource); - String foreignScope = ManagedSessionStore.sessionScopeKey("foreign", "foreign-session"); + String foreignScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("foreign", "foreign-session"); jdbc.update("INSERT INTO ma_resource_ref (session_scope_key,owner_kind,owner_key,owner_revision,resource_key," + "tenant_id,session_id,resource_id,gmt_create) VALUES (?,'MESSAGE',?,1,?,'foreign'," + "'foreign-session','original-resource'," + ManagedSchemaTime.SQL_NOW + ")", foreignScope, "a".repeat(64), resource); @@ -128,9 +134,9 @@ class WorkspaceCsiApplicationInventoryTest { .allMatch(row -> row.membership() == WorkspaceCsiApplicationInventory.Membership.CONTEXT); var transactions = new TransactionTemplate(new DataSourceTransactionManager(source)); transactions.executeWithoutResult(status -> ManagedReceipts.insert(jdbc, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_MODE, - "foreign", com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope.TENANT, - "foreign", "reserved-only", com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("false"), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace.SESSION_CREATE_MODE, + "foreign", com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope.TENANT, + "foreign", "reserved-only", com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("false"), null, null, null, null, "RESERVED", null, false, 1)); var found = inspect(); assertThat(found.blockers()).noneMatch(blocker -> blocker.table().equals("ma_request_receipt")); @@ -343,7 +349,7 @@ class WorkspaceCsiApplicationInventoryTest { if (failRegistration) { throw new IllegalStateException("ordinary-registration-unavailable"); } - return new WorkspaceStorageGuard.Identity(root.toString(), "ordinary-test-host", "device", + return new StorageIdentityReader.Identity(root.toString(), "ordinary-test-host", "device", Files.readAttributes(root, BasicFileAttributes.class).fileKey().toString(), "2026-10-16T00:00:00.123456789Z"); }); return new WorkspaceCreationStore(jdbc, manager, guard, new ManagedWorkspaceRegistry(jdbc), @@ -444,7 +450,7 @@ class WorkspaceCsiApplicationInventoryTest { WorkspaceCsiApplicationInventory.readHistoricalOwner(changed, "tenant", intent.operationId()); // Inject a same-snapshot owned-row fault through the underlying H2 connection only. try (var statement = target.prepareStatement("UPDATE ma_runtime_binding SET record_version = record_version + 1 WHERE binding_id = ?")) { - statement.setBytes(1, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.identity(binding.getBindingId())); + statement.setBytes(1, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.identity(binding.getBindingId())); assertThat(statement.executeUpdate()).isEqualTo(1); } var refused = WorkspaceCsiApplicationInventory.scanRetainedImage(changed, expected, Map.of()); @@ -643,7 +649,7 @@ class WorkspaceCsiApplicationInventoryTest { var key = fixture.path("expected").path("sessionKey"); String nativeSession = key.path("sessionId").textValue(); String nativeTenant = key.path("tenantId").textValue(); - String scope = ManagedSessionStore.sessionScopeKey(nativeTenant, nativeSession); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(nativeTenant, nativeSession); Map bodies = new java.util.LinkedHashMap<>(); for (var resource : fixture.path("originalResources")) { bodies.put(resource.path("ref").path("resourceId").textValue(), @@ -676,7 +682,7 @@ class WorkspaceCsiApplicationInventoryTest { String id = resource.path("resourceId").textValue(); var resourceValues = nativeFixtureScope(catalogue, "ma_resource", key); for (var field : Map.of("session_scope_key", scope, "resource_id", id, - "resource_key", ManagedSessionStore.resourceKey(scope, id), + "resource_key", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, id), "kind", resource.path("kind").textValue(), "schema_version", resource.path("schemaVersion").asText(), "byte_length", resource.path("byteLength").asText(), "sha256", resource.path("digest").textValue(), "storage_kind", "MYSQL_INLINE", "state", "REFERENCED").entrySet()) { @@ -691,7 +697,7 @@ class WorkspaceCsiApplicationInventoryTest { refValues.put("owner_kind", "JOURNAL".getBytes(StandardCharsets.UTF_8)); refValues.put("owner_key", scope.getBytes(StandardCharsets.UTF_8)); refValues.put("owner_revision", Integer.toString(index + 1).getBytes(StandardCharsets.UTF_8)); - refValues.put("resource_key", ManagedSessionStore.resourceKey(scope, id).getBytes(StandardCharsets.UTF_8)); + refValues.put("resource_key", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, id).getBytes(StandardCharsets.UTF_8)); refValues.put("journal_revision", Integer.toString(index + 1).getBytes(StandardCharsets.UTF_8)); rows.get("ma_resource_ref").add(WorkspaceCsiApplicationInventory.expectedRow( "ma_resource_ref", refValues, WorkspaceCsiApplicationInventory.Membership.ORIGINAL)); @@ -719,7 +725,7 @@ class WorkspaceCsiApplicationInventoryTest { var derived = nativeFixtureScope(catalogue, "ma_resource_ref", key); Map.of("session_scope_key", scope, "owner_kind", "SESSION_RECORD", "owner_key", projectionKey, "owner_revision", latest.path("sequence").asText(), "resource_id", usageId, - "resource_key", ManagedSessionStore.resourceKey(scope, usageId), "gmt_create", "1") + "resource_key", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, usageId), "gmt_create", "1") .forEach((field, value) -> derived.put(field, value.getBytes(StandardCharsets.UTF_8))); rows.get("ma_resource_ref").add(WorkspaceCsiApplicationInventory.expectedRow("ma_resource_ref", derived, WorkspaceCsiApplicationInventory.Membership.ORIGINAL)); @@ -781,7 +787,7 @@ class WorkspaceCsiApplicationInventoryTest { @Test void inventoriesEveryDeclaredTableAndPreservesRealPrivateCreationFacts() throws Exception { var snapshot = inspect(); - assertThat(snapshot.tables()).hasSize(com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.TABLES.size()); + assertThat(snapshot.tables()).hasSize(com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.TABLES.size()); assertThat(snapshot.tables().stream().map(table -> table.columns().size()).mapToInt(Integer::intValue).sum()).isEqualTo(805); assertThat(snapshot.blockers()).noneMatch(blocker -> blocker.code().equals("schema_drift") || blocker.code().equals("query_unavailable")); @@ -801,7 +807,7 @@ class WorkspaceCsiApplicationInventoryTest { jdbc.update("INSERT INTO ma_session_worktree (tenant_id, session_id, workspace_id, storage_id, physical_key," + " worktree_id, owner_actor_key, state, revision, plan_json, operation_id, gmt_create, gmt_modified)" + " VALUES ('tenant', ?, 'workspace', 'storage', ?, ?, ?, 'PREPARING', 1, '{}', 'worktree', 1, 1)", - session, "a".repeat(64), "b".repeat(32), ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + session, "a".repeat(64), "b".repeat(32), com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); var snapshot = inspect(); assertThat(snapshot.table("ma_session_worktree").rows()).hasSize(1); assertThat(snapshot.blockers()).anyMatch(blocker -> blocker.table().equals("ma_session_worktree")); @@ -993,11 +999,11 @@ class WorkspaceCsiApplicationInventoryTest { + " (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version," + " byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create)" + " VALUES (?, ?, 'tenant', 'workspace', ?, 'extra', 'native-fixture', 1, ?, ?, 'MYSQL_INLINE', ?," - + " 'publish', 'PUBLISHED', " + ManagedSchemaTime.SQL_NOW + ")", ManagedSessionStore.resourceKey(sessionScope(), "extra"), sessionScope(), session, bytes.length, sha(bytes), bytes); + + " 'publish', 'PUBLISHED', " + ManagedSchemaTime.SQL_NOW + ")", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(sessionScope(), "extra"), sessionScope(), session, bytes.length, sha(bytes), bytes); jdbc.update("INSERT INTO ma_resource_ref" + " (owner_kind, owner_key, owner_revision, resource_key, session_scope_key, tenant_id, workspace_id, session_id, journal_revision, resource_id, gmt_create)" + " VALUES ('JOURNAL', ?, 99, ?, ?, 'foreign', 'workspace', ?, 99, 'missing', " + ManagedSchemaTime.SQL_NOW + ")", - sessionScope(), ManagedSessionStore.resourceKey(sessionScope(), "extra"), sessionScope(), UUID.randomUUID().toString()); + sessionScope(), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(sessionScope(), "extra"), sessionScope(), UUID.randomUUID().toString()); var snapshot = inspect(); assertThat(snapshot.table("ma_resource").rows()).hasSize(1); assertThat(snapshot.table("ma_resource_ref").rows()).hasSize(1); @@ -1009,11 +1015,11 @@ class WorkspaceCsiApplicationInventoryTest { @Test void independentlyFindsCommandsAndDeliveriesWithNoScheduleClaimOrSessionColumn() throws Exception { - var namespace = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptNamespace.AUTOMATION; - var scopeKind = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.ReceiptScope.TENANT; + var namespace = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptNamespace.AUTOMATION; + var scopeKind = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.ReceiptScope.TENANT; for (String owner : List.of("tenant", "foreign")) { String id = owner.equals("tenant") ? "orphan" : "original-json"; - String receipt = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.receiptKey(namespace, owner, scopeKind, owner, id); + String receipt = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.receiptKey(namespace, owner, scopeKind, owner, id); jdbc.update("INSERT INTO ma_request_receipt (receipt_key, namespace, tenant_key, tenant_id, scope_kind, scope_key, scope_id," + " idempotency_key, idempotency_digest, actor_id, request_digest, schedule_id, result_json, state, gmt_create, gmt_modified)" + " VALUES (?, 'AUTOMATION', ?, ?, 'TENANT', ?, ?, ?, ?, 'actor', 'digest', ?, ?, 'COMPLETED', 1, 1)", @@ -1112,10 +1118,10 @@ class WorkspaceCsiApplicationInventoryTest { + " resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id," + " state, gmt_create) VALUES (?, ?, 'tenant', 'workspace', ?, 'resource', 'inventory-fixture', 1, ?, ?," + " 'MYSQL_INLINE', ?, 'publish', 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ")", - ManagedSessionStore.resourceKey(sessionScope(), "resource"), sessionScope(), session, resource.length, sha(resource), resource); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(sessionScope(), "resource"), sessionScope(), session, resource.length, sha(resource), resource); jdbc.update("INSERT INTO ma_resource_ref (owner_kind, owner_key, owner_revision, resource_key, session_scope_key, tenant_id, workspace_id, session_id," + " journal_revision, resource_id, gmt_create) VALUES ('JOURNAL', ?, 1, ?, ?, 'tenant', 'workspace', ?, 1, 'resource', " + ManagedSchemaTime.SQL_NOW + ")", - sessionScope(), ManagedSessionStore.resourceKey(sessionScope(), "resource"), sessionScope(), session); + sessionScope(), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(sessionScope(), "resource"), sessionScope(), session); for (String state : List.of("PREPARED", "DISPATCHING", "EXECUTING", "CANCEL_REQUESTED", "UNKNOWN", "ABANDONED", "SETTLED")) { insertExecution(state, state, binding.getBindingId(), session, brokerHash(session)); } @@ -1264,7 +1270,7 @@ class WorkspaceCsiApplicationInventoryTest { var valueOnly = WorkspaceCsiApplicationInventory.expectedImage(observed.tables()); var restored = WorkspaceCsiApplicationInventory.restoreCanonical(valueOnly.bytes()); assertThat(restored.bytes()).isEqualTo(valueOnly.bytes()); - assertThat(restored.tables()).hasSize(com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.TABLES.size()); + assertThat(restored.tables()).hasSize(com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.TABLES.size()); assertThat(restored.rowCount()).isEqualTo(observed.rowCount()); assertThat(observed.blockers()).isNotEmpty(); byte[] trailing = java.util.Arrays.copyOf(valueOnly.bytes(), valueOnly.bytes().length + 1); @@ -1346,7 +1352,7 @@ class WorkspaceCsiApplicationInventoryTest { .put("frozenRequestSha256", sha(frozen)).put("baselineSha256", sha(baseline.bytes())) .put("preparedCommitSha256", sha(body)).put("writtenAt", writtenAt); var image = WorkspaceCsiApplicationImage.expected(baseline, - com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement.Stage.CUT, frozen, json.writeValueAsBytes(delta), null); + com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement.Stage.CUT, frozen, json.writeValueAsBytes(delta), null); long millis = Instant.parse(writtenAt).toEpochMilli(); assertThat(image.table("ma_session_head").rows().getFirst().number("gmt_modified")).isEqualTo(millis); for (String table : List.of("ma_resource", "ma_resource_ref", "ma_session_journal_tx")) { @@ -1360,7 +1366,7 @@ class WorkspaceCsiApplicationInventoryTest { } delta.put("writtenAt", "2040-01-01T00:00:00.123001Z"); assertThatThrownBy(() -> WorkspaceCsiApplicationImage.expected(baseline, - com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement.Stage.CUT, frozen, json.writeValueAsBytes(delta), null)) + com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement.Stage.CUT, frozen, json.writeValueAsBytes(delta), null)) .isInstanceOf(IllegalArgumentException.class); } @@ -1369,7 +1375,7 @@ class WorkspaceCsiApplicationInventoryTest { var baseline = WorkspaceCsiApplicationInventory.expectedImage(inspect().tables()); byte[] frozen = "{\"saved\":true}".getBytes(StandardCharsets.UTF_8); var expected = WorkspaceCsiApplicationImage.expected(baseline, - com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement.Stage.FROZEN, frozen, null, null); + com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement.Stage.FROZEN, frozen, null, null); var progress = expected.table("ma_operation").rows().getFirst(); assertThat(progress.text("progress_stage")).isEqualTo("FROZEN"); assertThat(progress.number("progress_version")).isEqualTo(2); @@ -1389,7 +1395,7 @@ class WorkspaceCsiApplicationInventoryTest { var drift = WorkspaceCsiApplicationInventory.expectedImage(inspect().tables()); assertThatThrownBy(() -> WorkspaceCsiApplicationImage.verify(drift, expected)).isInstanceOf(IllegalArgumentException.class); assertThatThrownBy(() -> WorkspaceCsiApplicationImage.expected(actual, - com.alibaba.qwen.code.runtimebroker.JdbcCsiApplicationRetirement.Stage.FROZEN, frozen, null, null)) + com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiApplicationRetirement.Stage.FROZEN, frozen, null, null)) .isInstanceOf(IllegalArgumentException.class); } @@ -1430,9 +1436,9 @@ class WorkspaceCsiApplicationInventoryTest { private static void bindAction(java.sql.PreparedStatement statement, String tenant, String session, String id, String payload) throws java.sql.SQLException { - String scope = ManagedSessionStore.sessionScopeKey(tenant, session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session); statement.setString(1, scope); - statement.setString(2, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(scope, "ACTION", id)); + statement.setString(2, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(scope, "ACTION", id)); statement.setBytes(3, tenant.getBytes(StandardCharsets.UTF_8)); statement.setBytes(4, session.getBytes(StandardCharsets.UTF_8)); statement.setBytes(5, id.getBytes(StandardCharsets.UTF_8)); @@ -1473,7 +1479,7 @@ class WorkspaceCsiApplicationInventoryTest { assertThat(result.blockers()).isEmpty(); assertThat(result.bytes()).isEqualTo(expected.bytes()); assertThat(queries.stream().filter(sql -> sql.startsWith("SELECT t.* FROM ma_"))) - .hasSize(com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.TABLES.size()); + .hasSize(com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.TABLES.size()); assertThat(queries).noneMatch(sql -> sql.contains("FOR UPDATE")); assertThat(queries).anyMatch(sql -> sql.startsWith("SELECT t.* FROM ma_storage_owner t WHERE") && sql.contains("storage_key = ?")); assertThat(queries).noneMatch(sql -> sql.contains("physical_key = ?")); @@ -1534,7 +1540,7 @@ class WorkspaceCsiApplicationInventoryTest { catalog.add(Map.of("name", "imported-" + i, "description", "Imported Skill", "body", i == 0 ? "x".repeat(404465) : "Instruction", "model_invocable", false)); } - var resources = new ManagedSessionStore(jdbc); + var resources = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); resources.publishAgentSkills("foreign", "foreign-workspace", "foreign-session", catalog); assertThat(readHistory(expected, new java.util.ArrayList<>(), true).blockers()).isEmpty(); resources.publishAgentSkills("foreign", "foreign-workspace", "foreign-session", List.of( @@ -1677,15 +1683,15 @@ class WorkspaceCsiApplicationInventoryTest { jdbc.update("INSERT INTO ma_resource (resource_key,session_scope_key,tenant_id,workspace_id,session_id,resource_id," + "kind,schema_version,byte_length,sha256,storage_kind,inline_bytes,publish_command_id,state,gmt_create)" + " VALUES (?,?,'tenant','workspace',?,?,?,1,?,?,'MYSQL_INLINE',?,'publish','REFERENCED'," + ManagedSchemaTime.SQL_NOW + ")", - ManagedSessionStore.resourceKey(sessionScope(), id), sessionScope(), session, id, kind, bytes.length, sha(bytes), bytes); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(sessionScope(), id), sessionScope(), session, id, kind, bytes.length, sha(bytes), bytes); } private void foreignResource(String id, String kind, byte[] bytes) { - String scope = ManagedSessionStore.sessionScopeKey("foreign", "foreign-session"); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("foreign", "foreign-session"); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id," + " kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create)" + " VALUES (?, ?, 'foreign', 'other-workspace', 'foreign-session', ?, ?, 1, ?, ?, 'MYSQL_INLINE', ?, 'publish', 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ")", - ManagedSessionStore.resourceKey(scope, id), scope, id, kind, bytes.length, sha(bytes), bytes); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, id), scope, id, kind, bytes.length, sha(bytes), bytes); } @Test @@ -1754,7 +1760,7 @@ class WorkspaceCsiApplicationInventoryTest { void referencedReaderTreatsDeclaredRawOutputAsDataAndRejectsExternalStructuredPayloads() throws Exception { var expected = historicalKeys(); var json = new ObjectMapper(); - var contract = json.readTree(java.nio.file.Path.of("../core/src/managed-runtime/contracts/managed-tool-publication-v1.fixtures.json").toFile()) + var contract = json.readTree(VerificationFixtures.moduleRoot().resolve("../core/src/managed-runtime/contracts/managed-tool-publication-v1.fixtures.json").toFile()) .path("cases").get(0).path("value"); byte[] bindingBytes = json.writeValueAsBytes(contract); ToolPublicationContract.parseBytes("binding", bindingBytes); @@ -1766,12 +1772,12 @@ class WorkspaceCsiApplicationInventoryTest { ToolPublicationContract.bindingDigest(contract), "c".repeat(64)); byte[] raw = ("{\"bindingId\":\"" + binding.getBindingId() + "\"}").getBytes(StandardCharsets.UTF_8); String contentId = "result-" + sha((scope + ":publication-1:content:output").getBytes(StandardCharsets.UTF_8)).substring(0, 32); - String resourceScope = ManagedSessionStore.sessionScopeKey("tenant-1", "session-1"); + String resourceScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant-1", "session-1"); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, scope_key," + " publication_id, slot_key, resource_id, kind, schema_version, byte_length, sha256, inline_bytes, storage_kind, state, capture_state, operation_id, gmt_create)" + " VALUES (?, ?, 'tenant-1', 'workspace-1', 'session-1', ?, 'publication-1', 'content:output', ?, 'managed-tool-result-content'," + " 1, ?, ?, ?, 'MYSQL_INLINE', 'PUBLISHED', 'VERIFIED', 'publish', " + ManagedSchemaTime.SQL_NOW + ")", - ManagedSessionStore.resourceKey(resourceScope, contentId), resourceScope, scope, contentId, raw.length, sha(raw), raw); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(resourceScope, contentId), resourceScope, scope, contentId, raw.length, sha(raw), raw); assertThat(readReferences(expected).blockers()).isEmpty(); assertThat(ToolPublicationContract.bindingDigest(contract)).isNotEqualTo(sha(bindingBytes)); jdbc.update("UPDATE ma_output_capture SET binding_digest = ?", sha(bindingBytes)); @@ -1782,7 +1788,7 @@ class WorkspaceCsiApplicationInventoryTest { assertThat(readReferences(expected).blockers()).isEmpty(); String terminalId = "result-" + sha((scope + ":publication-1:terminal").getBytes(StandardCharsets.UTF_8)).substring(0, 32); jdbc.update("UPDATE ma_resource SET slot_key = 'terminal', kind = 'managed-tool-terminal', resource_id = ?, resource_key = ?, capture_object_key = ?", - terminalId, ManagedSessionStore.resourceKey(resourceScope, terminalId), + terminalId, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(resourceScope, terminalId), "managed-tool-results/" + scope + "/publication-1/" + sha("terminal".getBytes(StandardCharsets.UTF_8))); assertThat(readReferences(expected).blockers()).anyMatch(blocker -> blocker.code().equals("unsupported_historical_payload")); jdbc.update("UPDATE ma_resource SET inline_bytes = ?, storage_kind = 'MYSQL_INLINE', capture_object_key = NULL", raw); @@ -1853,10 +1859,10 @@ class WorkspaceCsiApplicationInventoryTest { jdbc.update("INSERT INTO ma_request_receipt (receipt_key,namespace,tenant_key,tenant_id,scope_kind,scope_key,scope_id," + "idempotency_key,idempotency_digest,request_digest,operation,state,session_id,result_json,gmt_create,gmt_modified)" + " VALUES (?,'SESSION_COMMAND',?,'tenant','SESSION',?,?,?,?,?,'CSI_RESOURCE_READ',?,?,?,1,?)", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("csi-read", id), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("tenant"), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("csi-read", "tenant", session), session, - id, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(id), requestDigest, + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("csi-read", id), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("tenant"), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("csi-read", "tenant", session), session, + id, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(id), requestDigest, state, session, value.toString(), ended == null ? 1L : ended); } @@ -1979,9 +1985,9 @@ class WorkspaceCsiApplicationInventoryTest { "runtimeSessionId", "foreign-session", "payloadJson", payload)); var values = new java.util.LinkedHashMap(); resource.columns().forEach(column -> values.put(column.name(), null)); - for (var entry : Map.of("session_scope_key", ManagedSessionStore.sessionScopeKey("foreign", "foreign-session"), + for (var entry : Map.of("session_scope_key", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("foreign", "foreign-session"), "tenant_id", "foreign", "workspace_id", "foreign-workspace", "session_id", "foreign-session", - "resource_id", "local", "resource_key", ManagedSessionStore.resourceKey(ManagedSessionStore.sessionScopeKey("foreign", "foreign-session"), "local"), + "resource_id", "local", "resource_key", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("foreign", "foreign-session"), "local"), "kind", "managed-tool-input", "schema_version", "1", "byte_length", Integer.toString(bytes.length), "sha256", sha(bytes)).entrySet()) { values.put(entry.getKey(), entry.getValue().getBytes(StandardCharsets.UTF_8)); @@ -2088,7 +2094,7 @@ class WorkspaceCsiApplicationInventoryTest { } private String sessionScope() { - return ManagedSessionStore.sessionScopeKey("tenant", session); + return com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session); } private static String sha(byte[] bytes) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStoreTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStoreTest.java index fb4eda23be9..43dc80ad413 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiCheckpointSnapshotStoreTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; @@ -11,10 +11,13 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -385,8 +388,8 @@ class WorkspaceCsiCheckpointSnapshotStoreTest { private void resource(String id, String kind, JsonNode body) { byte[] bytes = body.toString().getBytes(StandardCharsets.UTF_8); - String scope = ManagedSessionStore.sessionScopeKey("tenant", "harness"); - String resource = ManagedSessionStore.resourceKey(scope, id); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "harness"); + String resource = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, id); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create) VALUES (?, ?, 'tenant', 'workspace', 'harness', ?, ?, 1, ?, ?, 'MYSQL_INLINE', ?, 'fixture', 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ")", resource, scope, id, kind, bytes.length, hash(bytes), bytes); jdbc.update("INSERT INTO ma_resource_ref (session_scope_key, owner_kind, owner_key, owner_revision, resource_key, tenant_id, workspace_id, session_id, journal_revision, resource_id, gmt_create) VALUES (?, 'JOURNAL', ?, 1, ?, 'tenant', 'workspace', 'harness', 1, ?, " + ManagedSchemaTime.SQL_NOW + ")", scope, scope, resource, id); @@ -394,8 +397,8 @@ class WorkspaceCsiCheckpointSnapshotStoreTest { private void publicationObject(String scope, String slot, String id, String kind, JsonNode body) { byte[] bytes = body.toString().getBytes(StandardCharsets.UTF_8); - String sessionScope = ManagedSessionStore.sessionScopeKey("tenant", "harness"); - String resource = ManagedSessionStore.resourceKey(sessionScope, id); + String sessionScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "harness"); + String resource = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(sessionScope, id); if (jdbc.queryForObject("SELECT COUNT(*) FROM ma_resource WHERE resource_key = ?", Integer.class, resource) == 0) { resource(id, kind, body); jdbc.update("DELETE FROM ma_resource_ref WHERE resource_key = ?", resource); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiHostedFilesTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiHostedFilesTest.java similarity index 85% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiHostedFilesTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiHostedFilesTest.java index 3948066b942..7e2b220d231 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiHostedFilesTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiHostedFilesTest.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; @@ -19,7 +20,7 @@ class WorkspaceCsiHostedFilesTest { @ParameterizedTest(name = "cold recovery = {0}") @ValueSource(booleans = {false, true}) void hostedFileTurnsRetainResultsAcrossOriginalAndRecoveredOwners(boolean coldRecovery) throws Exception { - Assumptions.assumeTrue(Files.isRegularFile(Path.of("../../packages/cli/dist/src/serve/hosted-csi-text-turn.js")), + Assumptions.assumeTrue(Files.isRegularFile(VerificationFixtures.repositoryRoot().resolve("packages/cli/dist/src/serve/hosted-csi-text-turn.js")), "Run npm run build to include the Hosted JavaScript acceptance test"); var source = new DriverManagerDataSource("jdbc:h2:mem:csi-hosted-files-" + UUID.randomUUID() + ";MODE=MySQL;DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE", "sa", ""); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiNativeActivationGateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiNativeActivationGateTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiNativeActivationGateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiNativeActivationGateTest.java index ffaa4d8f78e..46d884750d2 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiNativeActivationGateTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiNativeActivationGateTest.java @@ -1,14 +1,13 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiFilesRetirementGuard; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; @@ -17,8 +16,12 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiFilesRetirementGuard; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -92,19 +95,19 @@ class WorkspaceCsiNativeActivationGateTest { CsiFilesRetirementProfile.CONFIG_REF, CsiFilesRetirementProfile.POLICY_REF); jdbc.update( "INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" - + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); var properties = new ManagedAgentProperties(); properties.setAgentRevision("reviewed-agent/1"); - sessionId = WorkspaceCsiSessionMain.create(jdbc, manager, JSON, properties, - new WorkspaceCsiSessionMain.Request(registration, "actor", "create", null, null, + sessionId = com.alibaba.qwen.code.managedagent.persistence.PrivateCsiFixture.create(jdbc, manager, JSON, properties, + new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request(registration, "actor", "create", null, null, new WorkspaceSelection("workspace", "."))).sessionId(); - var managed = new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + var managed = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); request = transaction.execute(status -> managed.requireCsiRequest(registration, sessionId)); bindings = new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("test", new byte[32])); sessions = new JdbcRuntimeSessionRepository(source); executions = new JdbcToolExecutionRepository(source, bindings); - journal = new ManagedSessionStore(jdbc); + journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); binding = bindings.findOrCreate(request); binding = bindings.claimOperation(binding.getBindingId(), "operator", Duration.ofSeconds(120)); reservation = reservations.reserve(registration, bindings, binding, UUID.randomUUID().toString()); @@ -154,7 +157,7 @@ class WorkspaceCsiNativeActivationGateTest { assertThat(jdbc.queryForObject("SELECT activation_expires_at FROM ma_session_head", Long.class)).isEqualTo(lease.expiresAt()); assertThat(journal.restore("tenant", "workspace", sessionId, token).activationLease()).isEqualTo(lease); transaction.executeWithoutResult(status -> jdbc.execute((org.springframework.jdbc.core.ConnectionCallback) connection -> { - var head = com.alibaba.qwen.code.runtimebroker.JdbcCsiActivationAdmission.lockNativeHead(connection, + var head = com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiActivationAdmission.lockNativeHead(connection, JdbcCsiFilesRetirementGuard.lockBindingControl(connection, binding.getBindingId())); assertThat(head.activation().expiresAt()).isEqualTo(lease.expiresAt()); assertThat(head.activation().renewalSequence()).isEqualTo(100); @@ -181,7 +184,7 @@ class WorkspaceCsiNativeActivationGateTest { assertThatThrownBy(() -> transaction.execute(status -> journal.lockPublicationWriter( "tenant", "workspace", sessionId, writerId, 1, token))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(org.springframework.http.HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(org.springframework.http.HttpStatus.CONFLICT.value()); assertThat(error.getCode()).isEqualTo("csi_managed_mutation_unavailable"); }); assertThat(allRows()).isEqualTo(before); @@ -877,7 +880,7 @@ class WorkspaceCsiNativeActivationGateTest { } private void generate(boolean conversation, boolean extracted, boolean legacyTerminal) throws Exception { - Path root = Path.of("../..").toRealPath(); + Path root = VerificationFixtures.repositoryRoot().toRealPath(); Path input = directory.resolve("input.json"); var value = JSON.createObjectNode().put("cwd", request.getScope().getCanonicalCwd()) .put("capabilityDigest", CsiFilesRetirementProfile.CAPABILITY_DIGEST); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiNativeActivationRenewalMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiNativeActivationRenewalMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiNativeActivationRenewalMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiNativeActivationRenewalMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStoreTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStoreTest.java index 804256fa389..51c816f82b6 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiReservationStoreTest.java @@ -1,8 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; @@ -14,9 +12,12 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -218,7 +219,7 @@ class WorkspaceCsiReservationStoreTest { var registration = registration("tenant", "storage", "backend", "handle", 1); var context = new ContextBinding("tenant", "workspace", 1, "storage", ".", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1); - var local = new WorkspaceExecutionStore(jdbc, new DataSourceTransactionManager(source)); + var local = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, new DataSourceTransactionManager(source))); var session = localSession(context); var original = bindings.findById(session.getBindingId()); assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ma_storage_owner", Long.class)).isZero(); @@ -267,7 +268,7 @@ class WorkspaceCsiReservationStoreTest { var registration = registration("tenant", "storage", "backend", "handle", 1); var context = new ContextBinding("tenant", "workspace", 1, "storage", ".", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1); - var local = new WorkspaceExecutionStore(jdbc, new DataSourceTransactionManager(source)); + var local = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, new DataSourceTransactionManager(source))); WorkspaceTestStorage.register(jdbc, context.getTenantId(), context.getStorageId()); var session = localSession(context); local.claim(context, session); @@ -332,7 +333,7 @@ class WorkspaceCsiReservationStoreTest { var context = new ContextBinding("tenant", "workspace", 1, "storage", ".", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1); var session = localSession(context); - var local = new WorkspaceExecutionStore(jdbc, new DataSourceTransactionManager(source)); + var local = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, new DataSourceTransactionManager(source))); var outer = new TransactionTemplate(new DataSourceTransactionManager(source)); outer.executeWithoutResult(status -> { assertUnavailable(() -> store.register(registration)); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetainedArtifactsMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetainedArtifactsMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetainedArtifactsMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetainedArtifactsMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetainedArtifactsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetainedArtifactsTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetainedArtifactsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetainedArtifactsTest.java index b863d0834c1..ef287dcbdb8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetainedArtifactsTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetainedArtifactsTest.java @@ -1,9 +1,9 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.catalog.AgentDefinitionExtensions; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.catalog.AgentDefinitionExtensions; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -54,12 +54,12 @@ class WorkspaceCsiRetainedArtifactsTest { expected = fixture.historicalKeys(); tx = new TransactionTemplate(new DataSourceTransactionManager(source)); clock.advance(Duration.between(clock.instant(), Instant.now())); - store = new ManagedAgentStore(jdbc, JSON, clock, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, JSON, clock, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); accepted(); jdbc.update("INSERT INTO ma_workspace (tenant_id,workspace_id,workspace_generation,storage_id,display_name,config_ref,policy_ref,state)" + " VALUES ('foreign','workspace',1,'ordinary-storage','Fixture',?,?,'ACTIVE')", WorkspaceExecutionProfile.CONFIG_REF, WorkspaceExecutionProfile.POLICY_REF); jdbc.update("INSERT INTO ma_workspace_access (tenant_id,workspace_id,actor_id,role) VALUES ('foreign','workspace',?,'OPERATOR')", - ManagedWorkspaceRegistry.actorKey("foreign", "actor")); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("foreign", "actor")); session = tx.execute(status -> store.insertWorkspaceSessionCommand("foreign", "actor", "create", "a".repeat(64), "qwen-code", null, null, List.of(), null, new WorkspaceSelection("workspace", ".")).sessionId()); } @@ -76,7 +76,7 @@ class WorkspaceCsiRetainedArtifactsTest { void acceptsEmptyMessagesAndActualAgentResourcesIncludingEmptyAndLiteralText() throws Exception { materialize(); assertThat(tx.execute(status -> store.findMessagePage("foreign", session, null, 100, true)).items()).isEmpty(); - var resources = new ManagedSessionStore(jdbc); + var resources = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String literal = "Unicode 指令 {\"sessionId\":\"" + expected.table("ma_session").rows().getFirst().text("session_id") + "\"}"; for (String text : List.of("", literal)) { tx.execute(status -> resources.publishAgentInstructions("foreign", "workspace", session, text)); @@ -214,7 +214,7 @@ class WorkspaceCsiRetainedArtifactsTest { default -> throw new AssertionError(fault); } String id = String.join(":", identity); - String key = ManagedSessionStore.resourceKey((String) original.get("session_scope_key"), id); + String key = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey((String) original.get("session_scope_key"), id); assertThat(jdbc.update("UPDATE ma_resource SET resource_key=?,resource_id=? WHERE resource_key=?", key, id, oldBody)).isEqualTo(1); } @@ -226,7 +226,7 @@ class WorkspaceCsiRetainedArtifactsTest { @ParameterizedTest @ValueSource(strings = {"instructions-utf8", "skills-shape", "skills-path", "skills-field", "publish-command", "agent-scope"}) void refusesMalformedAgentArtifactsWithOtherwiseCorrectDigests(String fault) throws Exception { - var resources = new ManagedSessionStore(jdbc); + var resources = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); tx.execute(status -> resources.publishAgentInstructions("foreign", "workspace", session, "valid")); tx.execute(status -> resources.publishAgentSkills("foreign", "workspace", session, List.of(Map.of("name", "review", "description", "description", "body", "body")))); accepted(); @@ -286,7 +286,7 @@ class WorkspaceCsiRetainedArtifactsTest { String oldId = (String) original.get("resource_id"); String digest = WorkspaceEvidenceResources.digest(bytes); String id = oldId.substring(0, oldId.lastIndexOf(':') + 1) + digest; - String key = ManagedSessionStore.resourceKey((String) original.get("session_scope_key"), id); + String key = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey((String) original.get("session_scope_key"), id); assertThat(jdbc.update("INSERT INTO ma_resource (resource_key,session_scope_key,tenant_id,workspace_id,session_id,resource_id,kind," + "schema_version,byte_length,sha256,storage_kind,inline_bytes,state,gmt_create,last_verified_at,retention_until)" + " SELECT ?,session_scope_key,tenant_id,workspace_id,session_id,?,kind,schema_version,?,?,storage_kind,?,state," @@ -301,9 +301,9 @@ class WorkspaceCsiRetainedArtifactsTest { private void replaceAgent(String kind, byte[] bytes) { String digest = WorkspaceEvidenceResources.digest(bytes); String id = (kind.equals("managed-agent-instructions") ? "agent-instructions-" : "agent-skills-") + digest; - String scope = ManagedSessionStore.sessionScopeKey("foreign", session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("foreign", session); jdbc.update("UPDATE ma_resource SET resource_key=?,resource_id=?,publish_command_id=?,inline_bytes=?,byte_length=?,sha256=? WHERE kind=?", - ManagedSessionStore.resourceKey(scope, id), id, id, bytes, bytes.length, digest, kind); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, id), id, id, bytes, bytes.length, digest, kind); } private List artifactBytes() { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetirementStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetirementStoreTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetirementStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetirementStoreTest.java index 5131466498e..39de6f6aa75 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetirementStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRetirementStoreTest.java @@ -1,8 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; @@ -12,9 +10,12 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import org.junit.jupiter.api.BeforeEach; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeConstructionFixture.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeConstructionFixture.java similarity index 69% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeConstructionFixture.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeConstructionFixture.java index f8563e8f584..f5ad832d0fb 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeConstructionFixture.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiRuntimeConstructionFixture.java @@ -1,7 +1,8 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.fasterxml.jackson.databind.ObjectMapper; @@ -28,14 +29,14 @@ public final class WorkspaceCsiRuntimeConstructionFixture { CsiFilesRetirementProfile.CONFIG_REF, CsiFilesRetirementProfile.POLICY_REF); jdbc.update( "INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" - + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); var properties = new ManagedAgentProperties(); properties.setAgentRevision("construction-fixture/1"); - var created = WorkspaceCsiSessionMain.create(jdbc, manager, json, properties, - new WorkspaceCsiSessionMain.Request(registration, "actor", "create", null, null, + var created = com.alibaba.qwen.code.managedagent.persistence.PrivateCsiFixture.create(jdbc, manager, json, properties, + new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request(registration, "actor", "create", null, null, new WorkspaceSelection("workspace", "."))); - var managed = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + var managed = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); return new TransactionTemplate(manager).execute(status -> managed.requireCsiRequest(registration, created.sessionId())); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionGuardTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionGuardTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionGuardTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionGuardTest.java index f370f64b27d..b8ee9ea6717 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionGuardTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSessionGuardTest.java @@ -1,14 +1,12 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; -import com.alibaba.qwen.code.runtimebroker.JdbcCsiFilesRetirementGuard; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; @@ -17,8 +15,12 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcCsiFilesRetirementGuard; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.flywaydb.core.Flyway; import org.junit.jupiter.api.BeforeEach; @@ -79,20 +81,20 @@ class WorkspaceCsiSessionGuardTest { CsiFilesRetirementProfile.CONFIG_REF, CsiFilesRetirementProfile.POLICY_REF); jdbc.update( "INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role)" - + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + + " VALUES ('tenant', 'workspace', ?, 'OPERATOR')", com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); var properties = new ManagedAgentProperties(); properties.setAgentRevision("reviewed-agent/1"); - sessionId = WorkspaceCsiSessionMain.create(jdbc, manager, json, properties, - new WorkspaceCsiSessionMain.Request(registration, "actor", "create", null, null, + sessionId = com.alibaba.qwen.code.managedagent.persistence.PrivateCsiFixture.create(jdbc, manager, json, properties, + new com.alibaba.qwen.code.managedagent.contracts.workspace.PrivateCsiSession.Request(registration, "actor", "create", null, null, new WorkspaceSelection("workspace", "."))).sessionId(); - var managed = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + var managed = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); request = transaction.execute(status -> managed.requireCsiRequest(registration, sessionId)); bindings = new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("test", new byte[32])); sessions = new JdbcRuntimeSessionRepository(source); executions = new JdbcToolExecutionRepository(source); - journal = new ManagedSessionStore(jdbc); + journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); } @ParameterizedTest @@ -100,8 +102,8 @@ class WorkspaceCsiSessionGuardTest { void publicLifecycleCannotFenceAnOriginalPrivateCsiSession(StoreModels.OperationKind kind) { var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - var managed = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + var managed = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var before = jdbc.queryForMap("SELECT " + ManagedSchemaProjection.SESSION.columns() + " FROM ma_session WHERE session_id = ?", sessionId); var ownership = reservations.inspect(registration); assertApiCode(() -> transaction.execute(status -> managed.beginWorkspaceLifecycle("tenant", sessionId, diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSqlWorkerMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSqlWorkerMySqlIT.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSqlWorkerMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSqlWorkerMySqlIT.java index 7f79e5f7922..cbbb6daaa2e 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSqlWorkerMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiSqlWorkerMySqlIT.java @@ -1,20 +1,23 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.http.RuntimeBrokerHttpServer; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiFilesProtocol; -import com.alibaba.qwen.code.runtimebroker.transport.RuntimeBrokerHttpServer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.sun.net.httpserver.HttpExchange; @@ -144,7 +147,7 @@ class WorkspaceCsiSqlWorkerMySqlIT { private static void acceptance(DataSource source, Path directory, boolean hostedProducer, boolean coldRecovery, String recoveryCut, int isolation, boolean retirement) throws Exception { - Path root = Path.of("../..").toRealPath(); + Path root = VerificationFixtures.repositoryRoot().toRealPath(); assertThat(Files.isRegularFile(root.resolve("packages/cli/dist/src/serve/managed-csi-file-worker.js"))) .as("Run npm run build before this explicitly invoked integration gate").isTrue(); Flyway.configure().dataSource(source).locations("classpath:db/managed-schema").load().migrate(); @@ -170,7 +173,7 @@ class WorkspaceCsiSqlWorkerMySqlIT { var binding = bindings.claimOperation(initial.getBindingId(), "owned-test", Duration.ofMinutes(5)); var reserved = storage.reserve(registration, bindings, binding, UUID.randomUUID().toString()); bindings.releaseOperation(binding.getBindingId(), "owned-test", binding.getOperationGeneration()); - var journal = new ManagedSessionStore(jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); journal.setCsiBindings(bindings); var properties = new ManagedAgentProperties(); properties.setAgentRevision("construction-fixture/1"); @@ -193,7 +196,7 @@ class WorkspaceCsiSqlWorkerMySqlIT { }).when(mappedClient).sendAsync(any(HttpRequest.class), any()); var application = new WorkspaceCsiApplicationStore(jdbc, manager, journal, bindings, sessions, new HttpRuntimeTransport(mappedClient)); var access = new WorkspaceCsiRuntimeAccess(jdbc, manager, JSON, properties, registration, - sessionId, request.requestKey(), bindings, sessions, new HttpRuntimeTransport(mappedClient)); + sessionId, request.requestKey(), bindings, sessions, new HttpRuntimeTransport(mappedClient), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); var provisioner = mock(RuntimeProvisioner.class); var placement = new AtomicReference(); when(provisioner.kind()).thenReturn("kubernetes-workspace"); @@ -346,7 +349,7 @@ class WorkspaceCsiSqlWorkerMySqlIT { } } catch (ManagedAgentException refusal) { try { - respond(exchange, refusal.getStatus().value(), Map.of("error", Map.of("code", refusal.getCode(), + respond(exchange, refusal.getStatus(), Map.of("error", Map.of("code", refusal.getCode(), "message", refusal.getMessage()))); } catch (java.io.IOException ignored) { exchange.close(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStoreTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStoreTest.java index 5adb53dec23..b06b8c89553 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceCsiWorkerAckStoreTest.java @@ -1,12 +1,13 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -32,9 +33,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoInteractions; /** Historical decoder fixtures are not proof of a newly acknowledged worker or trusted Pod. */ -class WorkspaceCsiWorkerAckStoreTest { - static final ObjectMapper JSON = new ObjectMapper(); - static final long OBSERVED = 1_791_012_345_678_901L; +public class WorkspaceCsiWorkerAckStoreTest { + public static final ObjectMapper JSON = new ObjectMapper(); + public static final long OBSERVED = 1_791_012_345_678_901L; private Fixture fixture; @BeforeEach @@ -227,7 +228,7 @@ class WorkspaceCsiWorkerAckStoreTest { verifyNoInteractions(fixture.transport(), fixture.objects()); } - static Fixture fixture(String url) throws Exception { + public static Fixture fixture(String url) throws Exception { var source = new JdbcDataSource(); source.setURL(url); source.setUser("sa"); @@ -235,14 +236,14 @@ class WorkspaceCsiWorkerAckStoreTest { return fixture(source); } - static Fixture fixture(javax.sql.DataSource source) throws Exception { + public static Fixture fixture(javax.sql.DataSource source) throws Exception { ManagedSchema.initialize(source); var jdbc = new ManagedSchemaJdbcTemplate(source); var manager = new DataSourceTransactionManager(source); var bindings = new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("fixture", new byte[32])); var sessions = new JdbcRuntimeSessionRepository(source); var executions = new JdbcToolExecutionRepository(source); - var managed = new ManagedSessionStore(jdbc); + var managed = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); var grants = new ToolPublicationStore(jdbc, manager, managed, executions, bindings, new ToolPublicationStore.Capacity(1024, 16 * 1024 * 1024, 16 * 1024 * 1024, 10)); var objects = mock(ToolPublicationObjectStore.class); @@ -317,9 +318,9 @@ class WorkspaceCsiWorkerAckStoreTest { throw new IllegalStateException("Shared ACK fixtures are missing"); } - record Fixture(JdbcTemplate jdbc, WorkspaceCsiWorkerAckStore store, WorkspaceCsiWorkerAckStore.Selector selector, + public record Fixture(JdbcTemplate jdbc, WorkspaceCsiWorkerAckStore store, WorkspaceCsiWorkerAckStore.Selector selector, ObjectNode document, RuntimeTransport transport, ToolPublicationObjectStore objects) { - void save(String encoded) { + public void save(String encoded) { new TransactionTemplate(new DataSourceTransactionManager(jdbc.getDataSource())).executeWithoutResult(ignored -> { String owner = WorkspaceOperations.hash(selector.retirementId(), "fixture-proof"); String resource = WorkspaceEvidenceResources.publish(jdbc, selector.tenantId(), selector.workspaceId(), selector.sessionId(), @@ -328,24 +329,24 @@ class WorkspaceCsiWorkerAckStoreTest { }); } - void observed(long value) { + public void observed(long value) { String target = hash(document.path("confirmation").path("captureIdentity").path("executionCallId").asText()); new TransactionTemplate(new DataSourceTransactionManager(jdbc.getDataSource())).executeWithoutResult(ignored -> new WorkspaceOperations(jdbc, selector.tenantId()).updateWork(WorkspaceOperations.Kind.CSI_RETIREMENT, selector.retirementId(), "CSI_WORKER_ACK", target, WorkspaceOperations.values("recorded_at_epoch_micros", value))); } - ObjectNode publication() { + public ObjectNode publication() { return (ObjectNode) ToolPublicationContract.parseBytes("binding", jdbc.queryForObject("SELECT binding_json FROM ma_output_capture", String.class) .getBytes(StandardCharsets.UTF_8)); } - void savePublication(ObjectNode publication) { + public void savePublication(ObjectNode publication) { jdbc.update("UPDATE ma_output_capture SET binding_json = ?, binding_digest = ?", publication.toString(), ToolPublicationContract.bindingDigest(publication)); } - long rows() { + public long rows() { return jdbc.queryForObject("SELECT COUNT(*) FROM ma_operation_work", Long.class); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceDirectorySelectionMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceDirectorySelectionMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceDirectorySelectionMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceDirectorySelectionMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStoreTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStoreTest.java index 4494c574fc1..2286c56aef7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileStoreTest.java @@ -2,6 +2,8 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.SshRuntimeSettings; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.workspace.SshWorkspaceService; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; @@ -29,6 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.verifyNoInteractions; @@ -61,12 +64,12 @@ class WorkspaceFileStoreTest { var manager = new DataSourceTransactionManager(source); registry = new ManagedWorkspaceRegistry(jdbc); guard = new WorkspaceStorageGuard(jdbc, manager, properties, - path -> new WorkspaceStorageGuard.Identity(path.toString(), "test-host", "1", "2", "2026-10-15T00:00:00.123456789Z")); + path -> new StorageIdentityReader.Identity(path.toString(), "test-host", "1", "2", "2026-10-15T00:00:00.123456789Z")); var mapper = new ObjectMapper(); workspace = new WorkspaceCreationStore(jdbc, manager, guard, registry, properties, mapper, Clock.systemUTC()) .create("tenant", "actor", "Files", "create").workspaceId(); ssh = mock(SshWorkspaceService.class); - files = new WorkspaceFileStore(registry, guard, ssh, properties, jdbc, manager, mapper); + files = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceFileStore(registry, guard, ssh, properties, jdbc, manager, mapper)); Files.createDirectory(Path.of(guard.fileRoot(files.scope("tenant", "actor", workspace, ".").binding()).get("root")).resolve("src")); } @@ -85,7 +88,7 @@ class WorkspaceFileStoreTest { + " workspace_storage_id, cwd_relative, context_config_ref, context_revision, workspace_config_ref, workspace_policy_ref, owner_actor_key)" + " SELECT tenant_id, ?, ?, 0, 0, workspace_id, workspace_generation, storage_id, 'src', ?, 7, config_ref, policy_ref, ?" + " FROM ma_workspace WHERE tenant_id = ? AND workspace_id = ?", id, "agent", binding.getContextConfigRef(), - ManagedWorkspaceRegistry.actorKey("tenant", "actor"), "tenant", workspace); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor"), "tenant", workspace); return new WorkspaceFileStore.Locator(id, null, null); } @@ -99,7 +102,7 @@ class WorkspaceFileStoreTest { registry.configureSsh(properties); jdbc.update("UPDATE ma_storage SET storage_kind = 'SSH_LOCAL', registration_json = ?", "{\"nodeId\":\"mac\"}"); jdbc.update("INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role) VALUES (?, ?, ?, 'OPERATOR')", - "tenant", workspace, ManagedWorkspaceRegistry.actorKey("tenant", "unlisted")); + "tenant", workspace, com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "unlisted")); var allowed = files.scope("tenant", "actor", locator); assertThat(allowed.binding().getCwdRelative()).isEqualTo("src"); assertThat(allowed.binding().getContextRevision()).isEqualTo(7); @@ -111,8 +114,8 @@ class WorkspaceFileStoreTest { .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("runtime_node_forbidden")); assertThatThrownBy(() -> files.read("tenant", "unlisted", locator, "a.txt", allowed.revision())) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("runtime_node_forbidden")); - var uploads = new WorkspaceFileUploadStore(files, registry, guard, ssh, properties, jdbc, - new DataSourceTransactionManager(jdbc.getDataSource()), new ObjectMapper(), Runnable::run); + var uploads = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceFileUploadStore(files, registry, guard, ssh, properties, jdbc, + new DataSourceTransactionManager(jdbc.getDataSource()), new ObjectMapper(), Runnable::run)); assertThatThrownBy(() -> uploads.upload("tenant", "unlisted", locator, "a.txt", allowed.revision(), UUID.randomUUID().toString(), new java.io.ByteArrayInputStream(new byte[] {1}), 1)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("runtime_node_forbidden")); @@ -140,7 +143,7 @@ class WorkspaceFileStoreTest { assertThatThrownBy(() -> files.query("tenant", "actor", locator, ".", "", first.scopeRevision())) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("workspace_file_scope_changed")); jdbc.update("DELETE FROM ma_workspace_access WHERE tenant_id = ? AND workspace_id = ? AND actor_id = ?", - "tenant", workspace, ManagedWorkspaceRegistry.actorKey("tenant", "actor")); + "tenant", workspace, com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "actor")); assertThatThrownBy(() -> files.query("tenant", "actor", locator, ".", "", null)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("session_not_found")); } @@ -150,7 +153,7 @@ class WorkspaceFileStoreTest { void anotherWorkspaceOperatorCanReadSessionFilesUntilTheSessionIsDeleted() { var locator = session("session"); jdbc.update("INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role) VALUES (?, ?, ?, 'OPERATOR')", - "tenant", workspace, ManagedWorkspaceRegistry.actorKey("tenant", "operator")); + "tenant", workspace, com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "operator")); try (var process = mockStatic(WorkspaceFileProcess.class)) { process.when(() -> WorkspaceFileProcess.read(anyString(), anyString(), any())).thenReturn(result()); assertThat(files.query("tenant", "operator", locator, ".", "", null).entries()).isNotEmpty(); @@ -166,8 +169,8 @@ class WorkspaceFileStoreTest { void unreadySessionCannotReachTheFileProcess() { var locator = session("session"); try (var worktrees = mockStatic(SessionWorktreeStore.class); var process = mockStatic(WorkspaceFileProcess.class)) { - worktrees.when(() -> SessionWorktreeStore.requireReady(jdbc, "tenant", "session")) - .thenThrow(new ManagedAgentException(org.springframework.http.HttpStatus.CONFLICT, "worktree_not_ready", "Not ready")); + worktrees.when(() -> SessionWorktreeStore.requireReady(any(WorktreeFiles.class), eq(jdbc), eq("tenant"), eq("session"))) + .thenThrow(new ManagedAgentException(409, "worktree_not_ready", "Not ready")); assertThatThrownBy(() -> files.query("tenant", "actor", locator, ".", "", null)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("worktree_not_ready")); process.verifyNoInteractions(); @@ -184,7 +187,7 @@ class WorkspaceFileStoreTest { var identity = Files.readAttributes(root.resolve(".qwen-managed-worktrees/owned"), "unix:dev,ino"); jdbc.update("UPDATE ma_session SET cwd_relative = ? WHERE session_id = ?", cwd, "session"); try (var worktrees = mockStatic(SessionWorktreeStore.class); var process = mockStatic(WorkspaceFileProcess.class)) { - worktrees.when(() -> SessionWorktreeStore.descriptor(jdbc, "tenant", "session")) + worktrees.when(() -> SessionWorktreeStore.descriptor(any(WorktreeFiles.class), eq(jdbc), eq("tenant"), eq("session"))) .thenReturn(new SessionWorktreeStore.Descriptor("owned", "session", workspace, "ready", "branch", "a".repeat(40), cwd, "op", 2, null)); process.when(() -> WorkspaceFileProcess.read(anyString(), anyString(), any())).thenAnswer(call -> { Map request = call.getArgument(2); @@ -198,7 +201,7 @@ class WorkspaceFileStoreTest { return result(); }); String revision = files.query("tenant", "actor", locator, ".", "", null).scopeRevision(); - worktrees.when(() -> SessionWorktreeStore.descriptor(jdbc, "tenant", "session")) + worktrees.when(() -> SessionWorktreeStore.descriptor(any(WorktreeFiles.class), eq(jdbc), eq("tenant"), eq("session"))) .thenReturn(new SessionWorktreeStore.Descriptor("owned", "session", workspace, "ready", "branch", "a".repeat(40), cwd, "op", 3, null)); assertThatThrownBy(() -> files.query("tenant", "actor", locator, ".", "", revision)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("workspace_file_scope_changed")); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStoreTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStoreTest.java index 256971f2756..2c0e19ee3a1 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceFileUploadStoreTest.java @@ -1,6 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.workspace.SshWorkspaceService; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; @@ -73,18 +74,18 @@ class WorkspaceFileUploadStoreTest { manager = new DataSourceTransactionManager(source); registry = new ManagedWorkspaceRegistry(jdbc); guard = new WorkspaceStorageGuard(jdbc, manager, properties, - path -> new WorkspaceStorageGuard.Identity(path.toString(), "test-host", "1", "2", "2026-10-15T00:00:00.123456789Z")); + path -> new StorageIdentityReader.Identity(path.toString(), "test-host", "1", "2", "2026-10-15T00:00:00.123456789Z")); mapper = new ObjectMapper(); workspace = new WorkspaceCreationStore(jdbc, manager, guard, registry, properties, mapper, Clock.systemUTC()) .create("tenant", "actor", "Uploads", "create").workspaceId(); ssh = mock(SshWorkspaceService.class); - files = new WorkspaceFileStore(registry, guard, ssh, properties, jdbc, manager, mapper); + files = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceFileStore(registry, guard, ssh, properties, jdbc, manager, mapper)); Files.createDirectory(Path.of(guard.fileRoot(files.scope("tenant", "actor", workspace, ".").binding()).get("root")).resolve("src")); uploads = store(); } WorkspaceFileUploadStore store() { - return new WorkspaceFileUploadStore(files, registry, guard, ssh, properties, jdbc, manager, mapper, Runnable::run); + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceFileUploadStore(files, registry, guard, ssh, properties, jdbc, manager, mapper, Runnable::run)); } String revision() { @@ -103,7 +104,7 @@ class WorkspaceFileUploadStoreTest { + " workspace_storage_id, cwd_relative, context_config_ref, context_revision, workspace_config_ref, workspace_policy_ref, owner_actor_key)" + " SELECT tenant_id, ?, ?, 0, 0, workspace_id, workspace_generation, storage_id, 'src', ?, 7, config_ref, policy_ref, ?" + " FROM ma_workspace WHERE tenant_id = ? AND workspace_id = ?", "session", "agent", binding.getContextConfigRef(), - ManagedWorkspaceRegistry.actorKey("tenant", "other"), "tenant", workspace); + com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.actorKey("tenant", "other"), "tenant", workspace); var locator = new WorkspaceFileStore.Locator("session", null, null); String revision = files.scope("tenant", "actor", locator).revision(); String id = UUID.randomUUID().toString(); @@ -195,7 +196,7 @@ class WorkspaceFileUploadStoreTest { }); assertThatThrownBy(() -> upload(id, "file.bin", revision, new byte[] {1})) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus().value()).isEqualTo(503); + assertThat(error.getStatus()).isEqualTo(503); assertThat(error.getCode()).isEqualTo("workspace_upload_pending"); }); assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ma_storage_owner WHERE holder_key IS NOT NULL", Integer.class)).isEqualTo(1); @@ -277,7 +278,7 @@ class WorkspaceFileUploadStoreTest { try (var process = mockStatic(WorkspaceFileProcess.class)) { assertThatThrownBy(() -> uploads.upload("tenant", "actor", workspace, ".", "large.bin", revision, UUID.randomUUID().toString(), InputStream.nullInputStream(), WorkspaceFileProcess.MAX_UPLOAD_BYTES + 1L)) - .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getStatus().value()).isEqualTo(413)); + .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getStatus()).isEqualTo(413)); assertThatThrownBy(() -> upload(UUID.randomUUID().toString(), "../escape", revision, new byte[0])) .isInstanceOf(ManagedAgentException.class); for (String reserved : List.of(".git", ".qwen-managed-cache", ".qwen-child-workspaces")) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleMySqlIT.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleMySqlIT.java index b2862214464..b8765f73305 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleMySqlIT.java @@ -3,10 +3,12 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.ExtensionRecordJournal; import com.alibaba.qwen.code.managedagent.ManagedHookRecordContractTest; import com.alibaba.qwen.code.managedagent.MySqlLockProbe; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AuthorizeLifecycleRequest; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AuthorizeLifecycleRequest; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.AfterEach; @@ -77,7 +79,7 @@ class WorkspaceLifecycleMySqlIT extends WorkspaceLifecycleStoreTest { @Test void lifecycleEffectsKeepOtherTenantExtensionRecordsUnlocked() throws Exception { var fixture = fixture(); - String scope = ManagedSessionStore.sessionScopeKey("neighbor", "other-session"); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("neighbor", "other-session"); String record = ManagedExtensionProjection.recordKey("other-session", "hook_execution", "other-hook"); fixture.jdbc.update("INSERT INTO ma_session_record (session_scope_key, record_key, tenant_id, workspace_id," + " session_id, domain, record_id, operation_hash, revision, record_resource_id, gmt_create, first_sequence)" @@ -130,7 +132,7 @@ class WorkspaceLifecycleMySqlIT extends WorkspaceLifecycleStoreTest { + "connectionTimeZone=" + URLEncoder.encode(connectionZone, StandardCharsets.UTF_8) + "&sessionVariables=" + URLEncoder.encode("time_zone='" + databaseZone + "'", StandardCharsets.UTF_8)); var fixture = fixture(); - var journal = new ManagedSessionStore(fixture.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); var writer = fixture.transactions.execute(ignored -> journal.acquireWriter("tenant", fixture.session, "w".repeat(32), new AcquireWriterRequest("workspace", "original", 60_000L))); String id = fixture.transactions.execute(ignored -> fixture.store.beginWorkspaceLifecycle("tenant", fixture.session, @@ -140,7 +142,7 @@ class WorkspaceLifecycleMySqlIT extends WorkspaceLifecycleStoreTest { fixture.bindings.requestHarnessDrain("tenant", fixture.session); var legacy = new AuthorizeLifecycleRequest("workspace", "original", writer.writerGeneration(), "legacy-close"); var ordinary = new AuthorizeLifecycleRequest("workspace", "original", writer.writerGeneration()); - var execution = new WorkspaceExecutionStore(fixture.jdbc, new DataSourceTransactionManager(source)); + var execution = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(fixture.jdbc, new DataSourceTransactionManager(source))); assertThat(operation.lifecycleProtocolVersion()).isZero(); assertThat(fixture.transactions.execute(ignored -> WorkspaceLifecycleStore.legacyClose(fixture.jdbc, "tenant", fixture.session))) .isTrue(); @@ -171,7 +173,7 @@ class WorkspaceLifecycleMySqlIT extends WorkspaceLifecycleStoreTest { @Test void admissionSerializesAnOrdinaryWriterOnThePlacementFence() throws Exception { var fixture = fixture(); - var journal = new ManagedSessionStore(fixture.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); try (var pool = Executors.newSingleThreadExecutor()) { var entered = new CountDownLatch(1); var queued = new java.util.concurrent.atomic.AtomicReference>(); @@ -204,7 +206,7 @@ class WorkspaceLifecycleMySqlIT extends WorkspaceLifecycleStoreTest { @Test void ordinaryAuthorizationFencesOnlyWithinOneSession() throws Exception { var fixture = fixture(); - var journal = new ManagedSessionStore(fixture.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); String token = "w".repeat(32); String otherTenant = "other-tenant"; String otherSession = "other-session"; @@ -291,7 +293,7 @@ class WorkspaceLifecycleMySqlIT extends WorkspaceLifecycleStoreTest { @ValueSource(booleans = {false, true}) void idleJournalCheckReadsARevisionItsSnapshotCannotSee(boolean throughAdmission) throws Exception { var fixture = fixture(); - var store = new ManagedSessionStore(fixture.jdbc); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); var journal = fixture.transactions.execute(ignored -> new ExtensionRecordJournal(store, "tenant", "workspace", fixture.session).open()); var txManager = new DataSourceTransactionManager(source); @@ -328,7 +330,7 @@ class WorkspaceLifecycleMySqlIT extends WorkspaceLifecycleStoreTest { @Test void idleJournalCheckFailsClosedOnAJournalShortOfItsHead() throws Exception { var fixture = fixture(); - var store = new ManagedSessionStore(fixture.jdbc); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); var journal = fixture.transactions.execute(ignored -> new ExtensionRecordJournal(store, "tenant", "workspace", fixture.session).open()); commitPendingTurn(fixture, journal); @@ -345,7 +347,7 @@ class WorkspaceLifecycleMySqlIT extends WorkspaceLifecycleStoreTest { @ValueSource(strings = {"accepted-settled", "other-settled", "accepted-pending"}) void unadmittedCancellationUsesTheFencedJournalSnapshot(String cut) throws Exception { var fixture = fixture(); - var store = new ManagedSessionStore(fixture.jdbc); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); fixture.transactions.executeWithoutResult(ignored -> new ExtensionRecordJournal(store, "tenant", "workspace", fixture.session).open()); String turn = fixture.transactions.execute(ignored -> fixture.store.insertTurnCommand("tenant", "SUBMIT_TURN", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStoreTest.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStoreTest.java index c3982e8ac46..350cf4f3c21 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceLifecycleStoreTest.java @@ -3,13 +3,14 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.ExtensionRecordJournal; import com.alibaba.qwen.code.managedagent.ManagedHookRecordContractTest; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedHookRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; @@ -20,10 +21,12 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; @@ -35,7 +38,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.support.TransactionTemplate; @@ -59,9 +61,9 @@ class WorkspaceLifecycleStoreTest { void lifecycleHookTransitionsRecheckNewEffectsAndKeepOriginalSettlement(boolean authority, boolean granted, String before, String after, boolean replacement, boolean accepted, boolean batch, boolean draining) throws Exception { var fixture = fixture(); - var store = new ManagedSessionStore(fixture.jdbc); - store.setLifecycleExecution(new WorkspaceExecutionStore(fixture.jdbc, - new DataSourceTransactionManager(fixture.jdbc.getDataSource())), fixture.store); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); + store.setLifecycleExecution(com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(fixture.jdbc, + new DataSourceTransactionManager(fixture.jdbc.getDataSource()))), fixture.store); var journal = fixture.transactions.execute(ignored -> new ExtensionRecordJournal(store, "tenant", "workspace", fixture.session).open()); var templates = ManagedHookRecordContractTest.fixtures().get("templates"); var data = new ManagedSessionStoreModels.CommitResource("hook-data", "hook-data", 1, 2, @@ -126,14 +128,14 @@ class WorkspaceLifecycleStoreTest { long revision = fixture.jdbc.queryForObject("SELECT journal_revision FROM ma_session_head", Long.class); int resources = fixture.jdbc.queryForObject("SELECT COUNT(*) FROM ma_resource", Integer.class); Runnable commit = () -> fixture.transactions.executeWithoutResult(ignored -> store.commit("tenant", fixture.session, - "extension-writer-token-0123456789", commitRequest, authority ? WorkspaceLifecycleStore.authority(operation) : null)); + "extension-writer-token-0123456789", commitRequest, authority ? com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation) : null)); if (accepted && !batch) { commit.run(); assertThat(fixture.jdbc.queryForObject("SELECT journal_revision FROM ma_session_head", Long.class)) .isEqualTo(revision + 1); } else { assertThatThrownBy(commit::run).isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.CONFLICT); + assertThat(error.getStatus()).isEqualTo(409); assertThat(error.getCode()).isEqualTo(accepted && batch ? "managed_session_extension_record_rejected" : draining ? "workspace_lifecycle_claim_fenced" : authority && !replacement ? "workspace_lifecycle_authorization_revoked" : "workspace_lifecycle_admission_closed"); @@ -192,9 +194,9 @@ class WorkspaceLifecycleStoreTest { void lifecycleClaimRefusalRemainsANonRetryableBrokerConflict() { var fixture = fixture(); var operation = fixture.admit(OperationKind.DELETE); - var execution = new WorkspaceExecutionStore(fixture.jdbc, new DataSourceTransactionManager(fixture.jdbc.getDataSource())); + var execution = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(fixture.jdbc, new DataSourceTransactionManager(fixture.jdbc.getDataSource()))); var session = fixture.store.requireSession("tenant", fixture.session); - execution.authorizeLifecycle(session, WorkspaceLifecycleStore.authority(operation)); + execution.authorizeLifecycle(session, com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation)); assertThatThrownBy(() -> execution.authorizeLifecycle(session, new RuntimeLifecycleAuthority(operation.operationId(), operation.claimGeneration() + 1))) .isInstanceOfSatisfying(RuntimeBrokerException.class, error -> { @@ -203,7 +205,7 @@ class WorkspaceLifecycleStoreTest { assertThat(error.isRetryable()).isFalse(); }); fixture.jdbc.update("UPDATE ma_operation SET claim_until = 0"); - assertThatThrownBy(() -> execution.authorizeLifecycle(session, WorkspaceLifecycleStore.authority(operation))) + assertThatThrownBy(() -> execution.authorizeLifecycle(session, com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation))) .isInstanceOfSatisfying(RuntimeBrokerException.class, error -> { assertThat(error.getStatusCode()).isEqualTo(409); assertThat(error.getCode()).isEqualTo("workspace_lifecycle_claim_fenced"); @@ -219,7 +221,7 @@ class WorkspaceLifecycleStoreTest { (row, index) -> row.getLong(1) * 1000 + row.getLong(2) / 1000); fixture.jdbc.update("UPDATE ma_session_head SET claim_lease_until = ?", now - 1); assertThatThrownBy(() -> fixture.bindings.requireHarnessAdmission(fixture.scope(), fixture.session, - WorkspaceLifecycleStore.authority(operation))).isInstanceOfSatisfying(RuntimeBrokerException.class, error -> { + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation))).isInstanceOfSatisfying(RuntimeBrokerException.class, error -> { assertThat(error.getStatusCode()).isEqualTo(409); assertThat(error.getCode()).isEqualTo("runtime_admission_closed"); assertThat(error.isRetryable()).isFalse(); @@ -246,7 +248,7 @@ class WorkspaceLifecycleStoreTest { @MethodSource("invalidJournalRecords") void lifecycleJournalValidationRejectsInvalidRecordsAndRollsBack(String line, String mode) throws Exception { var fixture = fixture(); - var journal = new ManagedSessionStore(fixture.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); var writer = fixture.transactions.execute(ignored -> journal.acquireWriter("tenant", fixture.session, "w".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "original", 60_000L))); var operation = "ordinary".equals(mode) ? null : fixture.admit(OperationKind.DELETE); @@ -259,9 +261,9 @@ class WorkspaceLifecycleStoreTest { Base64.getEncoder().encodeToString(bytes), HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(bytes)), List.of(resource)); assertThatThrownBy(() -> fixture.transactions.execute(ignored -> journal.commit("tenant", fixture.session, "w".repeat(32), - request, "authority".equals(mode) ? WorkspaceLifecycleStore.authority(operation) : null))) + request, "authority".equals(mode) ? com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation) : null))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThat(error.getStatus()).isEqualTo(400); assertThat(error.getCode()).isEqualTo(ManagedSessionStoreModels.ERROR_INVALID_REQUEST); }); assertThat(fixture.jdbc.queryForObject("SELECT journal_revision FROM ma_session_head", Long.class)).isZero(); @@ -279,7 +281,7 @@ class WorkspaceLifecycleStoreTest { void hookReceiptsUseCompactProtocolIdentitiesWithAnIndentedApplicationMapper(OperationKind kind, boolean recover) throws Exception { var fixture = fixture(); fixture.json.enable(SerializationFeature.INDENT_OUTPUT); - var journal = new ManagedSessionStore(fixture.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); var writer = fixture.transactions.execute(ignored -> journal.acquireWriter("tenant", fixture.session, "w".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "original", 60_000L))); byte[] definition = "{\"toolProfile\":\"hosted-workspace-files/1\",\"hookCatalog\":{}}".getBytes(StandardCharsets.UTF_8); @@ -325,7 +327,7 @@ class WorkspaceLifecycleStoreTest { fixture.jdbc.update("INSERT INTO ma_session_record (session_scope_key, record_key, tenant_id, workspace_id," + " session_id, domain, record_id, operation_hash, revision, record_resource_id, task_kind, task_state, gmt_create, first_sequence)" + " VALUES (?, ?, 'tenant', 'workspace', ?, 'hook_execution', ?, ?, 1, ?, 'hook', 'settled', 1, 1)", - ManagedSessionStore.sessionScopeKey("tenant", fixture.session), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", fixture.session), ManagedExtensionProjection.recordKey(fixture.session, "hook_execution", id), fixture.session, id, "a".repeat(64), ref.path("resourceId").asText()); effects.addObject().put("event", event).set("recordRef", ref); @@ -345,7 +347,7 @@ class WorkspaceLifecycleStoreTest { assertThat(fixture.jdbc.queryForObject("SELECT phase FROM ma_session_head", String.class)).isEqualTo("DRAINING"); fixture.transactions.executeWithoutResult(ignored -> journal.sealWriter("tenant", fixture.session, "w".repeat(32), new ManagedSessionStoreModels.SealWriterRequest("workspace", "original", writer.writerGeneration()), - WorkspaceLifecycleStore.authority(operation))); + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation))); fixture.transactions.executeWithoutResult(ignored -> fixture.lifecycle.verifyCompletion(operation)); } @@ -362,7 +364,7 @@ class WorkspaceLifecycleStoreTest { @Test void aResumedCloseVerifiesTheCarriedReceiptAgainstTheFailedOperationsHooks() throws Exception { var fixture = fixture(); - var journal = new ManagedSessionStore(fixture.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); var writer = fixture.transactions.execute(ignored -> journal.acquireWriter("tenant", fixture.session, "w".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "original", 60_000L))); byte[] definition = "{\"toolProfile\":\"hosted-workspace-files/1\",\"hookCatalog\":{}}".getBytes(StandardCharsets.UTF_8); @@ -403,7 +405,7 @@ class WorkspaceLifecycleStoreTest { fixture.jdbc.update("INSERT INTO ma_session_record (session_scope_key, record_key, tenant_id, workspace_id," + " session_id, domain, record_id, operation_hash, revision, record_resource_id, task_kind, task_state, gmt_create, first_sequence)" + " VALUES (?, ?, 'tenant', 'workspace', ?, 'hook_execution', ?, ?, 1, ?, 'hook', 'settled', 1, 1)", - ManagedSessionStore.sessionScopeKey("tenant", fixture.session), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", fixture.session), ManagedExtensionProjection.recordKey(fixture.session, "hook_execution", occurrence), fixture.session, occurrence, "a".repeat(64), ref.path("resourceId").asText()); // The first close persists its effects — the fence moves to @@ -414,7 +416,7 @@ class WorkspaceLifecycleStoreTest { assertThat(fixture.jdbc.queryForObject("SELECT phase FROM ma_session_head", String.class)).isEqualTo("DRAINING"); fixture.transactions.executeWithoutResult(ignored -> journal.sealWriter("tenant", fixture.session, "w".repeat(32), new ManagedSessionStoreModels.SealWriterRequest("workspace", "original", writer.writerGeneration()), - WorkspaceLifecycleStore.authority(first))); + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(first))); fixture.transactions.executeWithoutResult(ignored -> fixture.store.failOperation("tenant", fixture.session, first.operationId(), "worker", first.claimGeneration(), "session_close_completion_unsettled")); assertThat(fixture.jdbc.queryForObject("SELECT operation_id FROM ma_session_head", String.class)) @@ -441,7 +443,7 @@ class WorkspaceLifecycleStoreTest { @Test void detachOnlyAuthorizationAcceptsOriginalExpiredOrSealedWriterAfterEffects() throws Exception { var fixture = fixture(); - var journal = new ManagedSessionStore(fixture.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); var writer = fixture.transactions.execute(ignored -> journal.acquireWriter("tenant", fixture.session, "w".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "original", 60_000L))); byte[] definition = "{\"toolProfile\":\"hosted-workspace-files/1\"}".getBytes(StandardCharsets.UTF_8); @@ -457,7 +459,7 @@ class WorkspaceLifecycleStoreTest { definition.length, digest, Base64.getEncoder().encodeToString(definition)))); fixture.transactions.execute(ignored -> journal.commit("tenant", fixture.session, "w".repeat(32), commit)); var operation = fixture.admit(OperationKind.DELETE); - var authority = WorkspaceLifecycleStore.authority(operation); + var authority = com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation); var cleanup = new ManagedSessionStoreModels.AuthorizeLifecycleRequest("workspace", "original", writer.writerGeneration()); assertThatThrownBy(() -> fixture.transactions.executeWithoutResult(ignored -> journal.authorizeLifecycle( "tenant", fixture.session, "w".repeat(32), cleanup, authority))).hasMessageContaining("blocked"); @@ -490,7 +492,7 @@ class WorkspaceLifecycleStoreTest { @Test void legacyCloseKeepsItsOriginalHookPathButCannotAdmitOrdinaryWork() { var fixture = fixture(); - var journal = new ManagedSessionStore(fixture.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); var writer = fixture.transactions.execute(ignored -> journal.acquireWriter("tenant", fixture.session, "w".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "original", 60_000L))); String id = fixture.transactions.execute(ignored -> fixture.store.beginWorkspaceLifecycle("tenant", fixture.session, @@ -507,7 +509,7 @@ class WorkspaceLifecycleStoreTest { fixture.bindings.requireHookAdmission(fixture.scope(), fixture.session, null); assertThatThrownBy(() -> fixture.bindings.requireHarnessAdmission(fixture.scope(), fixture.session, null)) .hasMessageContaining("closed"); - var execution = new WorkspaceExecutionStore(fixture.jdbc, new DataSourceTransactionManager(fixture.jdbc.getDataSource())); + var execution = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(fixture.jdbc, new DataSourceTransactionManager(fixture.jdbc.getDataSource()))); execution.authorizeLegacyClose(fixture.store.requireSession("tenant", fixture.session)); fixture.jdbc.update("UPDATE ma_workspace_access SET role = 'READER'"); assertThatThrownBy(() -> execution.authorizeLegacyClose(fixture.store.requireSession("tenant", fixture.session))) @@ -522,7 +524,7 @@ class WorkspaceLifecycleStoreTest { @Test void l3FenceCannotUseTheLegacyCloseException() { var fixture = fixture(); - var journal = new ManagedSessionStore(fixture.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); var writer = fixture.transactions.execute(ignored -> journal.acquireWriter("tenant", fixture.session, "w".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "original", 60_000L))); fixture.admit(OperationKind.CLOSE); @@ -536,7 +538,7 @@ class WorkspaceLifecycleStoreTest { @Test void lifecycleAttachmentCannotDetachBeforeEffectsAreSaved() { var fixture = fixture(); - var journal = new ManagedSessionStore(fixture.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)); var writer = fixture.transactions.execute(ignored -> journal.acquireWriter("tenant", fixture.session, "w".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "original", 60_000L))); var operation = fixture.admit(OperationKind.DELETE); @@ -549,9 +551,9 @@ class WorkspaceLifecycleStoreTest { var request = new ManagedSessionStoreModels.AuthorizeLifecycleRequest("workspace", "original", writer.writerGeneration()); fixture.transactions.executeWithoutResult(ignored -> journal.authorizeLifecycle("tenant", fixture.session, "w".repeat(32), new ManagedSessionStoreModels.AuthorizeLifecycleRequest("workspace", "original", writer.writerGeneration(), "delete"), - WorkspaceLifecycleStore.authority(operation))); + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation))); assertThatThrownBy(() -> fixture.transactions.executeWithoutResult(ignored -> journal.authorizeLifecycle("tenant", fixture.session, - "w".repeat(32), request, WorkspaceLifecycleStore.authority(operation)))).hasMessageContaining("blocked"); + "w".repeat(32), request, com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation)))).hasMessageContaining("blocked"); assertThat(journal.hasLiveWriter("tenant", fixture.session)).isTrue(); assertThat(fixture.jdbc.queryForObject("SELECT phase FROM ma_session_head", String.class)).isEqualTo("LIFECYCLE_ONLY"); } @@ -620,18 +622,18 @@ class WorkspaceLifecycleStoreTest { var fixture = fixture(); OperationRecord first = fixture.admit(OperationKind.DELETE); RuntimeScope scope = fixture.scope(); - fixture.bindings.requireHarnessAdmission(scope, fixture.session, WorkspaceLifecycleStore.authority(first)); + fixture.bindings.requireHarnessAdmission(scope, fixture.session, com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(first)); assertThatThrownBy(() -> fixture.bindings.requireHarnessAdmission(scope, fixture.session, null)) .hasMessageContaining("closed"); fixture.jdbc.update("UPDATE ma_operation SET claim_until = 0"); OperationRecord second = fixture.transactions.execute(ignored -> fixture.store.claimOperation( "tenant", fixture.session, first.operationId(), "second", Duration.ofSeconds(30)).orElseThrow()); assertThat(second.claimGeneration()).isGreaterThan(first.claimGeneration()); - assertThatThrownBy(() -> fixture.bindings.requireHarnessAdmission(scope, fixture.session, WorkspaceLifecycleStore.authority(first))) + assertThatThrownBy(() -> fixture.bindings.requireHarnessAdmission(scope, fixture.session, com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(first))) .hasMessageContaining("closed"); assertThatThrownBy(() -> fixture.transactions.execute(ignored -> fixture.lifecycle.recoverEffects(first))) .hasMessageContaining("blocked"); - fixture.bindings.requireHarnessAdmission(scope, fixture.session, WorkspaceLifecycleStore.authority(second)); + fixture.bindings.requireHarnessAdmission(scope, fixture.session, com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(second)); assertThat(fixture.transactions.execute(ignored -> fixture.lifecycle.recoverEffects(second))).isNotNull(); assertThat(fixture.transactions.execute(ignored -> fixture.store.completeOperation( "tenant", fixture.session, first.operationId(), "worker", first.claimGeneration(), true))).isFalse(); @@ -668,13 +670,13 @@ class WorkspaceLifecycleStoreTest { assertThat(fixture.jdbc.queryForObject("SELECT COUNT(*) FROM ma_session_record WHERE record_kind = 'ACTION'" + " AND state = 'requested'", Integer.class)).isZero(); - var journal = new ExtensionRecordJournal(new ManagedSessionStore(fixture.jdbc), "tenant", "workspace", fixture.session); + var journal = new ExtensionRecordJournal(com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)), "tenant", "workspace", fixture.session); fixture.transactions.executeWithoutResult(ignored -> journal.open()); byte[] genesis = fixture.jdbc.queryForObject("SELECT record_bytes FROM ma_session_journal_tx" + " WHERE journal_revision = 1", byte[].class); String prompt = fixture.store.findTurn("tenant", fixture.session, turn).orElseThrow().promptId(); var request = fixture.promptCommit(prompt, false); - fixture.transactions.executeWithoutResult(ignored -> new ManagedSessionStore(fixture.jdbc).commit( + fixture.transactions.executeWithoutResult(ignored -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)).commit( "tenant", fixture.session, "extension-writer-token-0123456789", request)); assertThat(fixture.jdbc.queryForObject("SELECT journal_revision FROM ma_session_head", Long.class)).isEqualTo(2L); assertThat(fixture.jdbc.queryForObject("SELECT record_bytes FROM ma_session_journal_tx" @@ -705,7 +707,7 @@ class WorkspaceLifecycleStoreTest { "expired-monitor-v1", "expired-monitor-v2"}) void unacknowledgedCancellationRequiresFencedWriterAndIdleJournal(String cut) throws Exception { var fixture = fixture(); - var journal = new ExtensionRecordJournal(new ManagedSessionStore(fixture.jdbc), "tenant", "workspace", fixture.session); + var journal = new ExtensionRecordJournal(com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)), "tenant", "workspace", fixture.session); fixture.transactions.executeWithoutResult(ignored -> journal.open()); byte[] genesis = fixture.jdbc.queryForObject("SELECT record_bytes FROM ma_session_journal_tx" + " WHERE journal_revision = 1", byte[].class); @@ -728,7 +730,7 @@ class WorkspaceLifecycleStoreTest { : fixture.store.findTurn("tenant", fixture.session, turn).orElseThrow().promptId(); var request = fixture.promptCommit(prompt, !"expired-pending".equals(cut) && !monitor, monitor ? "monitor" : "user"); - fixture.transactions.executeWithoutResult(ignored -> new ManagedSessionStore(fixture.jdbc).commit( + fixture.transactions.executeWithoutResult(ignored -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(fixture.jdbc)).commit( "tenant", fixture.session, "extension-writer-token-0123456789", request)); assertThat(fixture.jdbc.queryForObject("SELECT journal_revision FROM ma_session_head", Long.class)).isEqualTo(2L); } @@ -778,7 +780,7 @@ class WorkspaceLifecycleStoreTest { transactions = new TransactionTemplate(new DataSourceTransactionManager(source)); var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - store = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); bindings = new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("test", new byte[32])); var beans = new DefaultListableBeanFactory(); beans.registerSingleton("bindings", bindings); @@ -830,7 +832,7 @@ class WorkspaceLifecycleStoreTest { byte[] bytes = body.toString().getBytes(StandardCharsets.UTF_8); String digest = HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(bytes)); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create) VALUES (?, ?, 'tenant', 'workspace', ?, ?, ?, 1, ?, ?, 'MYSQL_INLINE', ?, 'committed-hooks', 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ")", - ManagedSchemaKeys.key(ManagedSessionStore.sessionScopeKey("tenant", session), id), ManagedSessionStore.sessionScopeKey("tenant", session), session, id, kind, bytes.length, digest, bytes); + ManagedSchemaKeys.key(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), id), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), session, id, kind, bytes.length, digest, bytes); return json.createObjectNode().put("resourceId", id).put("kind", kind).put("schemaVersion", 1).put("byteLength", bytes.length).put("digest", digest); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceLinuxAcceptanceMain.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceLinuxAcceptanceMain.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceLinuxAcceptanceMain.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceLinuxAcceptanceMain.java index b8d6cae4d18..62f05a8561c 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceLinuxAcceptanceMain.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMaintenanceLinuxAcceptanceMain.java @@ -2,7 +2,7 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; @@ -57,8 +57,8 @@ public final class WorkspaceMaintenanceLinuxAcceptanceMain { properties.getHarness().setWorkspaceFilesEnabled(true); properties.getRuntimeBroker().setVerifiedWorkspaceRecoveryEnabled(true); properties.getRuntimeBroker().setWorkspaceMounts(List.of(new WorkspaceMount(tenant, "storage", root.toString()))); - var guard = new WorkspaceStorageGuard(jdbc, manager, properties); - var authority = new WorkspaceExecutionStore(jdbc, manager, guard); + var guard = new WorkspaceStorageGuard(jdbc, manager, properties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()); + var authority = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, manager, guard)); var children = new ChildWorkspaceStore(jdbc, manager); var recovery = new WorkspaceMaintenanceRecovery(jdbc, manager, guard); var binding = new ContextBinding(tenant, "workspace", 1, "storage", ".", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1); @@ -69,7 +69,7 @@ public final class WorkspaceMaintenanceLinuxAcceptanceMain { tenant, WorkspaceExecutionProfile.CONFIG_REF, WorkspaceExecutionProfile.POLICY_REF); jdbc.update("INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role) VALUES (?, 'workspace', ?, 'OPERATOR')", tenant, "native-actor".getBytes(java.nio.charset.StandardCharsets.UTF_8)); - var sessions = new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String parent = transactions.execute(ignored -> sessions.insertWorkspaceSessionCommand(tenant, "native-actor", "create", "digest", "qwen-code", null, null, List.of(), null, new WorkspaceSelection("workspace", "."))).sessionId(); var child = children.admit(tenant, parent, "run", binding, System.currentTimeMillis()); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationMySqlIT.java similarity index 90% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationMySqlIT.java index 109a7285ec2..98e7242e140 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationMySqlIT.java @@ -1,14 +1,15 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -128,8 +129,8 @@ class WorkspaceMigrationMySqlIT { transaction.setTimeout(15); var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - var store = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var original = create(store, transaction, "migrating", "original"); var other = cwd ? create(store, transaction, "unrelated", "other") : null; var originalCwd = cwd ? com.alibaba.qwen.code.managedagent.LegacyCwdOperations.seed(jdbc, store, @@ -151,8 +152,8 @@ class WorkspaceMigrationMySqlIT { }); } }; - var waitingStore = new ManagedAgentStore(waitingJdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(waitingJdbc), properties); + var waitingStore = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(waitingJdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(waitingJdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); try (var migration = data.getConnection(); var pool = Executors.newFixedThreadPool(2)) { migration.setAutoCommit(false); JdbcRuntimeBindingRepository.lockPlacementDomain(migration, "migrating"); @@ -181,7 +182,7 @@ class WorkspaceMigrationMySqlIT { } migration.commit(); if (cwd) { - var outcome = (com.alibaba.qwen.code.managedagent.session.AgentStateStore.CwdChangeOutcome) + var outcome = (com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.CwdChangeOutcome) creation.get(5, TimeUnit.SECONDS); assertThat(outcome.completed()).isFalse(); assertThat(outcome.failureCode()).isEqualTo("workspace_unavailable"); @@ -237,17 +238,17 @@ class WorkspaceMigrationMySqlIT { jdbc.update( "UPDATE ma_session SET retirement_operation_id = ?, retirement_generation = 1," + " retired_at = 1 WHERE tenant_id = ? AND session_id = ?", "delete", "tenant-a", "headless"); - String scopeKey = ManagedSessionStore.sessionScopeKey("tenant-a", "headless"); - String targetKey = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key("headless"); - String operationKey = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.operationKey( - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind.DELETE, scopeKey, targetKey, "delete"); + String scopeKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant-a", "headless"); + String targetKey = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key("headless"); + String operationKey = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.operationKey( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind.DELETE, scopeKey, targetKey, "delete"); jdbc.update( "INSERT INTO ma_operation (operation_key, scope_key, session_scope_key, target_key, target_kind, request_json, phase, tenant_id, session_id, operation_id, operation_kind, request_digest, state, admission_stage, delivery_state, session_status_before, available_at, gmt_create, gmt_modified, completed_at) VALUES (?, ?, ?, ?, 'SESSION', '{}', 'COMPLETED', ?, ?, 'delete', 'DELETE', 'digest', 'COMPLETED', 'HARNESS_CONFIRMED', 'CONFIRMED', 'CLOSED', 1, 1, 1, 1)", operationKey, scopeKey, scopeKey, targetKey, "tenant-a", "headless"); var manager = new DataSourceTransactionManager(data); var transaction = new TransactionTemplate(manager); transaction.setIsolationLevel(Connection.TRANSACTION_REPEATABLE_READ); - var sessions = new ManagedSessionStore(jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String token = "a".repeat(64); transaction.execute(status -> sessions.acquireWriter("tenant-b", "m-successor", token, new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "writer", 30000L))); @@ -289,9 +290,9 @@ class WorkspaceMigrationMySqlIT { private void seed(String tenant, String session, String state) { jdbc.update( "INSERT INTO ma_session (tenant_key, session_key, session_scope_key, tenant_id, session_id, agent_id, status, gmt_create, gmt_modified, workspace_id, workspace_generation, workspace_storage_id, cwd_relative, context_config_ref, context_revision, workspace_config_ref, workspace_policy_ref, deleted_at) VALUES (?, ?, ?, ?, ?, 'qwen-code', ?, 1, 1, 'workspace', 1, 'storage', '.', ?, 1, ?, ?, ?)", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(tenant), - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(session), - ManagedSessionStore.sessionScopeKey(tenant, session), tenant, session, state, WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, WorkspaceExecutionProfile.CONFIG_REF, + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(tenant), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(session), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, session), tenant, session, state, WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, WorkspaceExecutionProfile.CONFIG_REF, WorkspaceExecutionProfile.POLICY_REF, "DELETED".equals(state) ? 1L : null); ManagedReceipts.insertCreation(jdbc, tenant, new byte[]{1}, session, "digest", session, null, 1); jdbc.update( diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStoreTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStoreTest.java index 01dec1048b8..e8d3b9351a8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceMigrationStoreTest.java @@ -2,16 +2,18 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -38,6 +40,8 @@ import static org.awaitility.Awaitility.await; import static org.mockito.Mockito.mock; class WorkspaceMigrationStoreTest { + private static final com.fasterxml.jackson.databind.ObjectMapper JSON = new com.fasterxml.jackson.databind.ObjectMapper(); + @TempDir Path temp; private JdbcTemplate jdbc; private DataSourceTransactionManager manager; @@ -64,14 +68,14 @@ class WorkspaceMigrationStoreTest { if (path.equals(unreadableIdentity)) { throw new IOException("Unverified directory birth time"); } - return new WorkspaceStorageGuard.Identity(path.toRealPath().toString(), "test-host", "device", + return new StorageIdentityReader.Identity(path.toRealPath().toString(), "test-host", "device", path.getFileName().toString(), "2026-10-02T00:00:00Z"); }); guard.register("tenant", "storage", UUID.randomUUID().toString()); Files.createDirectory(temp.resolve("history")); Files.createDirectory(temp.resolve("runtime"), PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"))); - request = WorkspaceRecoveryStore.JSON.createObjectNode().put("version", 1) + request = JSON.createObjectNode().put("version", 1) .put("migrationOperationId", UUID.randomUUID().toString()).put("tenantId", "tenant").put("storageId", "storage") .put("fenceOperationId", UUID.randomUUID().toString()).put("captureOperationId", UUID.randomUUID().toString()) .put("mountRevision", 1).put("sourceRoot", source.toString()).put("targetRoot", target.toString()) @@ -82,7 +86,7 @@ class WorkspaceMigrationStoreTest { private WorkspaceMigrationStore store(boolean create) { return new WorkspaceMigrationStore(jdbc, manager, guard, new InMemoryRuntimeBindingRepository(), - request.toString().getBytes(StandardCharsets.UTF_8), create); + request.toString().getBytes(StandardCharsets.UTF_8), create, new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalRecoveryWorker()); } @ParameterizedTest @@ -176,7 +180,7 @@ class WorkspaceMigrationStoreTest { new WorkspaceOperations(jdbc, "tenant").update(WorkspaceOperations.Kind.WORKSPACE_RECOVERY, request.path("captureOperationId").asText(), WorkspaceOperations.values("state", "SEALED", "manifest_digest", "0".repeat(64), "result_json", "{}")); assertThatThrownBy(() -> operation.verify(false)).isInstanceOfSatisfying( - WorkspaceRecoveryStore.RecoveryFailure.class, error -> { + com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.RecoveryFailure.class, error -> { assertThat(error.code).isEqualTo(code); operation.failed(error.code); }); @@ -228,8 +232,8 @@ class WorkspaceMigrationStoreTest { captureRequest.put("operationId", request.path("captureOperationId").asText()); var capture = new WorkspaceRecoveryStore(jdbc, manager, guard, null, "capture", captureRequest.toString().getBytes(StandardCharsets.UTF_8)); - var context = capture.call("context", WorkspaceRecoveryStore.JSON.createObjectNode()); - var manifest = WorkspaceRecoveryStore.JSON.createObjectNode().put("version", 1) + var context = capture.call("context", JSON.createObjectNode()); + var manifest = JSON.createObjectNode().put("version", 1) .put("provider", "local-workspace-bundle/1").put("tenantId", "tenant").put("storageId", "storage") .put("captureOperationId", request.path("captureOperationId").asText()).put("activation", false); for (String name : List.of("fenceOperationId", "mountRevision")) { @@ -241,12 +245,12 @@ class WorkspaceMigrationStoreTest { for (String name : List.of("sessions", "assets")) { manifest.putObject(name) .put("path", ".w1-recovery/" + name + ".ndjson").put("count", 0).put("byteLength", 0) - .put("digest", WorkspaceRecoveryStore.hash(new byte[0])); + .put("digest", com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(new byte[0])); } Path metadata = Files.createDirectories(temp.resolve("bundle/.w1-recovery")); byte[] bytes = manifest.toString().getBytes(StandardCharsets.UTF_8); Files.write(metadata.resolve("manifest.json"), bytes); - var finish = WorkspaceRecoveryStore.JSON.createObjectNode().put("manifestDigest", WorkspaceRecoveryStore.hash(bytes)); + var finish = JSON.createObjectNode().put("manifestDigest", com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(bytes)); finish.putObject("result").put("contentVerified", true).put("activation", false); capture.call("finish", finish); // Protocol fixture for the success boundary, not file-content verification. @@ -254,10 +258,10 @@ class WorkspaceMigrationStoreTest { + "const rl = require('node:readline').createInterface({input: process.stdin});\n" + "rl.once('line', line => {\n" + " if (JSON.parse(line).error) process.exit(1);\n" - + " fs.writeFileSync(" + WorkspaceRecoveryStore.JSON.valueToTree(ready.toString()) + ", 'ready');\n" + + " fs.writeFileSync(" + JSON.valueToTree(ready.toString()) + ", 'ready');\n" + " const deadline = Date.now() + 10000;\n" + " setInterval(() => {\n" - + " if (fs.existsSync(" + WorkspaceRecoveryStore.JSON.valueToTree(release.toString()) + ")) process.exit(0);\n" + + " if (fs.existsSync(" + JSON.valueToTree(release.toString()) + ")) process.exit(0);\n" + " if (Date.now() > deadline) process.exit(2);\n" + " }, 10);\n" + "});\n" @@ -305,11 +309,11 @@ class WorkspaceMigrationStoreTest { new AesGcmSecretProtector("test", new byte[32])); jdbc.update("UPDATE ma_storage SET gmt_modified = 1"); var operation = new WorkspaceMigrationStore(jdbc, manager, guard, bindings, - request.toString().getBytes(StandardCharsets.UTF_8), true); + request.toString().getBytes(StandardCharsets.UTF_8), true, new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalRecoveryWorker()); assertThat(jdbc.queryForObject("SELECT gmt_modified FROM ma_storage", Long.class)).isGreaterThan(1); jdbc.update("UPDATE ma_storage SET gmt_modified = 1"); new WorkspaceMigrationStore(jdbc, manager, guard, bindings, - request.toString().getBytes(StandardCharsets.UTF_8), true); + request.toString().getBytes(StandardCharsets.UTF_8), true, new com.alibaba.qwen.code.managedagent.adapters.workspace.LocalRecoveryWorker()); assertThat(jdbc.queryForObject("SELECT gmt_modified FROM ma_storage", Long.class)).isEqualTo(1); String id = request.path("migrationOperationId").asText(); assertThat(bindings.isStorageFenced("tenant", "storage", id)).isTrue(); @@ -351,7 +355,7 @@ class WorkspaceMigrationStoreTest { + " VALUES ('tenant', 'session', 'qwen-code', 'ACTIVE', 1, 1, 'workspace', 1, 'storage', '.', ?, 1, ?, ?)", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, WorkspaceExecutionProfile.CONFIG_REF, WorkspaceExecutionProfile.POLICY_REF); - String scope = ManagedSessionStore.sessionScopeKey("tenant", "session"); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "session"); jdbc.update("UPDATE ma_session SET tenant_key = ?, session_key = ?, session_scope_key = ? WHERE tenant_id = X'74656e616e74' AND session_id = X'73657373696f6e'", WorkspaceOperations.hash("tenant"), WorkspaceOperations.hash("session"), scope); ManagedReceipts.insertCommand(jdbc, "tenant", "RENAME_SESSION", "rename", "digest", "session", null, "FAILED", null, 1); @@ -378,7 +382,7 @@ class WorkspaceMigrationStoreTest { + " VALUES ('tenant', 'session', 'qwen-code', 'ACTIVE', 1, 1, 'workspace', 1, 'storage', '.', ?, 1, ?, ?)", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, WorkspaceExecutionProfile.CONFIG_REF, WorkspaceExecutionProfile.POLICY_REF); - String scope = ManagedSessionStore.sessionScopeKey("tenant", "session"); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "session"); jdbc.update("UPDATE ma_session SET tenant_key = ?, session_key = ?, session_scope_key = ? WHERE tenant_id = X'74656e616e74' AND session_id = X'73657373696f6e'", WorkspaceOperations.hash("tenant"), WorkspaceOperations.hash("session"), scope); jdbc.update("INSERT INTO ma_operation (operation_key, scope_key, session_scope_key, operation_kind, operation_id, tenant_id, session_id," @@ -402,7 +406,7 @@ class WorkspaceMigrationStoreTest { + " VALUES ('tenant', 'session', 'qwen-code', 'ACTIVE', 1, 1, 'workspace', 1, 'storage', '.', ?, 1, ?, ?)", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, WorkspaceExecutionProfile.CONFIG_REF, WorkspaceExecutionProfile.POLICY_REF); - String scope = ManagedSessionStore.sessionScopeKey("tenant", "session"); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "session"); jdbc.update("UPDATE ma_session SET tenant_key = ?, session_key = ?, session_scope_key = ? WHERE tenant_id = X'74656e616e74' AND session_id = X'73657373696f6e'", WorkspaceOperations.hash("tenant"), WorkspaceOperations.hash("session"), scope); jdbc.update("INSERT INTO ma_operation (operation_key, scope_key, session_scope_key, operation_kind, operation_id, tenant_id, session_id," @@ -498,8 +502,8 @@ class WorkspaceMigrationStoreTest { assertThatThrownBy(() -> operation.verify(false)).isInstanceOfSatisfying(RuntimeBrokerException.class, error -> { assertThat(error.getCode()).isEqualTo("workspace_unavailable"); - assertThat(WorkspaceRecoveryStore.errorCode(error)).isEqualTo("workspace_unavailable"); - operation.failed(WorkspaceRecoveryStore.errorCode(error)); + assertThat(com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.errorCode(error)).isEqualTo("workspace_unavailable"); + operation.failed(com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.errorCode(error)); }); assertThat(operation.inspect().path("state").asText()).isEqualTo("RETIRED"); assertThat(operation.inspect().path("lastErrorCode").asText()).isEqualTo("workspace_unavailable"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceNativeAcceptanceMain.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceNativeAcceptanceMain.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceNativeAcceptanceMain.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceNativeAcceptanceMain.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleMySqlIT.java similarity index 88% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleMySqlIT.java index 0d10148fa6e..0354b57ac57 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleMySqlIT.java @@ -1,7 +1,8 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.springframework.jdbc.core.JdbcTemplate; @@ -57,9 +58,9 @@ class WorkspaceProfileLifecycleMySqlIT extends WorkspaceProfileLifecycleTest { var winner = claims.getFirst(); org.assertj.core.api.Assertions.assertThat(winner.claimGeneration()).isGreaterThan(first.claimGeneration()); org.assertj.core.api.Assertions.assertThat(f.bindings.requireHarnessRecovery(f.scope(), f.session, - WorkspaceLifecycleStore.authority(winner))).isTrue(); + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(winner))).isTrue(); org.assertj.core.api.Assertions.assertThatThrownBy(() -> f.bindings.requireHarnessRecovery(f.scope(), f.session, - WorkspaceLifecycleStore.authority(first))).hasMessageContaining("closed"); + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(first))).hasMessageContaining("closed"); f.transactions.executeWithoutResult(ignored -> f.lifecycle.verifyResources(winner)); } } @@ -67,7 +68,7 @@ class WorkspaceProfileLifecycleMySqlIT extends WorkspaceProfileLifecycleTest { @org.junit.jupiter.api.Test void expiredWriterRenewalAndColdAcquireCannotBothOwnTheHead() throws Exception { var f = fixture(); - var journal = new ManagedSessionStore(f.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(f.jdbc)); var writer = f.transactions.execute(ignored -> journal.acquireWriter("tenant", f.session, "w".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "resident", 60_000L))); var operation = admit(f, WorkspaceToolProfiles.SHELL, StoreModels.OperationKind.DELETE); @@ -82,10 +83,10 @@ class WorkspaceProfileLifecycleMySqlIT extends WorkspaceProfileLifecycleTest { return f.transactions.execute(ignored -> index == 0 ? journal.renewWriter("tenant", f.session, "w".repeat(32), new ManagedSessionStoreModels.RenewWriterRequest("workspace", "resident", writer.writerGeneration(), 60_000L), - WorkspaceLifecycleStore.authority(operation)) + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation)) : journal.acquireWriter("tenant", f.session, "r".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "recovery", 60_000L), - WorkspaceLifecycleStore.authority(operation))); + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation))); } catch (com.alibaba.qwen.code.managedagent.exception.ManagedAgentException conflict) { org.assertj.core.api.Assertions.assertThat(conflict.getCode()).isEqualTo("managed_session_writer_conflict"); return null; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleTest.java index b859d2728be..29b7942dee3 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceProfileLifecycleTest.java @@ -3,16 +3,20 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.ExtensionRecordJournal; import com.alibaba.qwen.code.managedagent.ManagedHookRecordContractTest; import com.alibaba.qwen.code.managedagent.PublicationJournalFixture; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionRecords; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -64,17 +68,17 @@ class WorkspaceProfileLifecycleTest { f.jdbc.update("UPDATE ma_operation SET claim_until = 946684800000 WHERE operation_kind IN ('CLOSE', 'ARCHIVE', 'DELETE')"); var next = f.transactions.execute(ignored -> f.store.claimOperation("tenant", f.session, first.operationId(), "server-2", Duration.ofSeconds(30)).orElseThrow()); - var current = WorkspaceLifecycleStore.authority(next); + var current = com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(next); assertThat(f.bindings.requireHarnessRecovery(f.scope(), f.session, current)).isTrue(); assertThatThrownBy(() -> f.bindings.requireHarnessRecovery(f.scope(), f.session, null)).hasMessageContaining("closed"); - assertThatThrownBy(() -> f.bindings.requireHarnessRecovery(f.scope(), f.session, WorkspaceLifecycleStore.authority(first))).hasMessageContaining("closed"); + assertThatThrownBy(() -> f.bindings.requireHarnessRecovery(f.scope(), f.session, com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(first))).hasMessageContaining("closed"); assertThatThrownBy(() -> f.bindings.requireHookAdmission(f.scope(), f.session, current)).hasMessageContaining("closed"); - var journal = new ManagedSessionStore(f.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(f.jdbc)); var request = new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "cleanup", 60_000L); assertThatThrownBy(() -> f.transactions.execute(ignored -> journal.acquireWriter("tenant", f.session, "w".repeat(32), request))) .isInstanceOf(ManagedAgentException.class); assertThatThrownBy(() -> f.transactions.execute(ignored -> journal.acquireWriter("tenant", f.session, "w".repeat(32), request, - WorkspaceLifecycleStore.authority(first)))).isInstanceOf(ManagedAgentException.class); + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(first)))).isInstanceOf(ManagedAgentException.class); var writer = f.transactions.execute(ignored -> journal.acquireWriter("tenant", f.session, "w".repeat(32), request, current)); assertThat(writer.writerGeneration()).isPositive(); assertThatThrownBy(() -> f.transactions.execute(ignored -> journal.renewWriter("tenant", f.session, "w".repeat(32), @@ -87,7 +91,7 @@ class WorkspaceProfileLifecycleTest { "hosted-workspace-mcp/1,CLOSE", "hosted-workspace-mcp/1,DELETE"}) void initialAdmissionPreservesTheLiveWriterForOneHandoff(String profile, OperationKind kind) { var f = fixture(); - var journal = new ManagedSessionStore(f.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(f.jdbc)); String token = "w".repeat(32); var writer = f.transactions.execute(ignored -> journal.acquireWriter("tenant", f.session, token, new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "resident", 1000L))); @@ -104,7 +108,7 @@ class WorkspaceProfileLifecycleTest { assertThat(f.jdbc.queryForObject("SELECT writer_lease_until FROM ma_session_head", Long.class)).isEqualTo(retained); org.awaitility.Awaitility.await().atMost(Duration.ofSeconds(3)).until(() -> f.jdbc.queryForObject("SELECT " + ManagedSchemaTime.SQL_NOW, Long.class) > original); - var authority = WorkspaceLifecycleStore.authority(operation); + var authority = com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation); var renewed = f.transactions.execute(ignored -> journal.renewWriter("tenant", f.session, token, new ManagedSessionStoreModels.RenewWriterRequest("workspace", "resident", writer.writerGeneration(), 60_000L), authority)); assertThat(renewed.writerGeneration()).isEqualTo(writer.writerGeneration()); @@ -118,7 +122,7 @@ class WorkspaceProfileLifecycleTest { @ValueSource(ints = {1, 2}) void admissionNeverRevivesAnExpiredWriter(int protocol) { var f = fixture(); - var journal = new ManagedSessionStore(f.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(f.jdbc)); f.transactions.execute(ignored -> journal.acquireWriter("tenant", f.session, "w".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "expired", 60_000L))); f.jdbc.update("UPDATE ma_session_head SET writer_lease_until = 0"); @@ -132,7 +136,7 @@ class WorkspaceProfileLifecycleTest { @ValueSource(strings = {WorkspaceToolProfiles.SHELL, WorkspaceToolProfiles.MCP}) void newClaimsNeverRefreshTheAdmissionHandoff(String profile) { var f = fixture(); - var journal = new ManagedSessionStore(f.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(f.jdbc)); f.transactions.execute(ignored -> journal.acquireWriter("tenant", f.session, "w".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "resident", 1000L))); var first = admit(f, profile, OperationKind.CLOSE); @@ -149,7 +153,7 @@ class WorkspaceProfileLifecycleTest { @ValueSource(strings = {WorkspaceToolProfiles.SHELL, WorkspaceToolProfiles.MCP}) void expiredResidentWriterRenewsOnlyUnderTheCurrentResourceClaim(String profile) { var f = fixture(); - var journal = new ManagedSessionStore(f.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(f.jdbc)); String token = "w".repeat(32); var writer = f.transactions.execute(ignored -> journal.acquireWriter("tenant", f.session, token, new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "resident", 60_000L))); @@ -158,7 +162,7 @@ class WorkspaceProfileLifecycleTest { f.jdbc.update("UPDATE ma_session_head SET writer_lease_until = ?", java.sql.Timestamp.from(java.time.Instant.parse("2000-01-01T00:00:00Z"))); assertThatThrownBy(() -> f.transactions.execute(ignored -> journal.renewWriter("tenant", f.session, token, - renew, WorkspaceLifecycleStore.authority(first)))) + renew, com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(first)))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("managed_session_writer_conflict")); f.transactions.executeWithoutResult(ignored -> f.lifecycle.recoverEffects(first)); f.jdbc.update("UPDATE ma_operation SET claim_until = 946684800000 WHERE operation_kind IN ('CLOSE', 'ARCHIVE', 'DELETE')"); @@ -167,27 +171,27 @@ class WorkspaceProfileLifecycleTest { assertThatThrownBy(() -> f.transactions.execute(ignored -> journal.renewWriter("tenant", f.session, token, renew))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("workspace_lifecycle_claim_fenced")); assertThatThrownBy(() -> f.transactions.execute(ignored -> journal.renewWriter("tenant", f.session, token, - renew, WorkspaceLifecycleStore.authority(first)))) + renew, com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(first)))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("workspace_lifecycle_claim_fenced")); var renewed = f.transactions.execute(ignored -> journal.renewWriter("tenant", f.session, token, - renew, WorkspaceLifecycleStore.authority(current))); + renew, com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(current))); assertThat(renewed.writerGeneration()).isEqualTo(writer.writerGeneration()); assertThat(journal.hasLiveWriter("tenant", f.session)).isTrue(); f.jdbc.update("UPDATE ma_session_head SET writer_lease_until = ?", java.sql.Timestamp.from(java.time.Instant.parse("2000-01-01T00:00:00Z"))); var replacement = f.transactions.execute(ignored -> journal.acquireWriter("tenant", f.session, "r".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "recovery", 60_000L), - WorkspaceLifecycleStore.authority(current))); + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(current))); assertThat(replacement.writerGeneration()).isGreaterThan(writer.writerGeneration()); assertThatThrownBy(() -> f.transactions.execute(ignored -> journal.renewWriter("tenant", f.session, token, - renew, WorkspaceLifecycleStore.authority(current)))) + renew, com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(current)))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("managed_session_writer_conflict")); } @Test void filesV1DrainingStillRejectsAnExpiredWriter() { var f = fixture(); - var journal = new ManagedSessionStore(f.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(f.jdbc)); var writer = f.transactions.execute(ignored -> journal.acquireWriter("tenant", f.session, "w".repeat(32), new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "resident", 60_000L))); String id = f.transactions.execute(ignored -> f.store.beginWorkspaceLifecycle("tenant", f.session, OperationKind.CLOSE, @@ -199,7 +203,7 @@ class WorkspaceProfileLifecycleTest { java.sql.Timestamp.from(java.time.Instant.parse("2000-01-01T00:00:00Z"))); assertThatThrownBy(() -> f.transactions.execute(ignored -> journal.renewWriter("tenant", f.session, "w".repeat(32), new ManagedSessionStoreModels.RenewWriterRequest("workspace", "resident", writer.writerGeneration(), 60_000L), - WorkspaceLifecycleStore.authority(operation)))) + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation)))) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("managed_session_writer_conflict")); } @@ -207,13 +211,13 @@ class WorkspaceProfileLifecycleTest { @ValueSource(strings = {WorkspaceToolProfiles.SHELL, WorkspaceToolProfiles.MCP}) void sealedCleanupWriterCanBeReacquiredOnlyWithTheCurrentResourceClaim(String profile) { var f = fixture(); - var journal = new ManagedSessionStore(f.jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(f.jdbc)); String token = "w".repeat(32); var original = f.transactions.execute(ignored -> journal.acquireWriter("tenant", f.session, token, new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "original", 60_000L))); var operation = admit(f, profile, OperationKind.DELETE); f.transactions.executeWithoutResult(ignored -> f.lifecycle.recoverEffects(operation)); - var authority = WorkspaceLifecycleStore.authority(operation); + var authority = com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation); f.transactions.execute(ignored -> journal.sealWriter("tenant", f.session, token, new ManagedSessionStoreModels.SealWriterRequest("workspace", "original", original.writerGeneration()), authority)); var recovery = new ManagedSessionStoreModels.AcquireWriterRequest("workspace", "cleanup", 60_000L); @@ -239,7 +243,7 @@ class WorkspaceProfileLifecycleTest { var f = fixture(); f.jdbc.update("UPDATE ma_session SET tool_profile = ?", profile); var disabled = new ManagedAgentProperties(); - var store = new ManagedAgentStore(f.jdbc, f.json, Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(f.jdbc), disabled); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(f.jdbc, f.json, Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(f.jdbc), disabled, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var admission = f.transactions.execute(ignored -> store.beginWorkspaceLifecycle("tenant", f.session, OperationKind.CLOSE, "owner", "a".repeat(64), "cleanup", "digest", true, 2)); assertThat(admission.operation().lifecycleProtocolVersion()).isEqualTo(2); @@ -574,7 +578,7 @@ class WorkspaceProfileLifecycleTest { f.resource(sealId, "managed-tool-result-content", f.json.createObjectNode().put("segmentCount", 0).put("byteLength", 0) .put("digest", ToolPublicationContract.sha256(new byte[0]))); } - var record = f.json.readTree(java.nio.file.Path.of("../core/src/managed-runtime/contracts/managed-child-run-record-v1.fixtures.json").toFile()) + var record = f.json.readTree(VerificationFixtures.moduleRoot().resolve("../core/src/managed-runtime/contracts/managed-child-run-record-v1.fixtures.json").toFile()) .path("templates").path("child_run").deepCopy(); var shell = (com.fasterxml.jackson.databind.node.ObjectNode) record; shell.set("startReceiptRef", f.resource("receipt", "managed-runtime-receipt", f.json.createObjectNode() @@ -596,7 +600,7 @@ class WorkspaceProfileLifecycleTest { f.jdbc.update("INSERT INTO ma_session_record (session_scope_key, record_key, tenant_id, workspace_id, session_id," + " domain, record_id, operation_hash, revision, record_resource_id, task_kind, task_state, gmt_create)" + " VALUES (?, ?, 'tenant', 'workspace', ?, 'child_run', 'shell-1', ?, 1, ?, 'background_shell', 'settled', 1)", - ManagedSessionStore.sessionScopeKey("tenant", f.session), "r".repeat(64), f.session, "o".repeat(64), ref.path("resourceId").asText()); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", f.session), "r".repeat(64), f.session, "o".repeat(64), ref.path("resourceId").asText()); var operation = admit(f, WorkspaceToolProfiles.SHELL, OperationKind.CLOSE); var beans = new org.springframework.beans.factory.support.DefaultListableBeanFactory(); var verifier = new WorkspaceProfileLifecycleVerifier(f.jdbc, beans.getBeanProvider(ToolPublicationDataStore.class), @@ -616,7 +620,7 @@ class WorkspaceProfileLifecycleTest { @ValueSource(booleans = {false, true}) void queuedNotificationIsCleanupWorkButAnActivatedNotificationIsATurn(boolean activated) throws Exception { var f = fixture(); - var journal = new com.alibaba.qwen.code.managedagent.ExtensionRecordJournal(new ManagedSessionStore(f.jdbc), "tenant", "workspace", f.session); + var journal = new com.alibaba.qwen.code.managedagent.ExtensionRecordJournal(com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(f.jdbc)), "tenant", "workspace", f.session); f.transactions.executeWithoutResult(ignored -> journal.open()); var input = f.json.createObjectNode(); var event = input.putObject("managedSession").put("kind", "input.accepted"); @@ -649,7 +653,7 @@ class WorkspaceProfileLifecycleTest { f.transactions.executeWithoutResult(ignored -> f.lifecycle.recoverEffects(operation)); } boolean resourcePhase = WorkspaceLifecycleStore.resourceClaim(f.jdbc, "tenant", f.session, - WorkspaceLifecycleStore.authority(operation)); + com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation)); assertThat(resourcePhase).isEqualTo(resources); assertThatThrownBy(() -> records.hasNewLifecycleDispatch("tenant", f.session, bytes, ignored -> null, true, resourcePhase)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> assertThat(error.getCode()).isEqualTo("workspace_lifecycle_admission_closed")); @@ -661,7 +665,7 @@ class WorkspaceProfileLifecycleTest { var f = fixture(); var operation = admit(f, WorkspaceToolProfiles.MCP, OperationKind.CLOSE); f.jdbc.update("UPDATE ma_operation SET lifecycle_protocol_version = 3"); - assertThatThrownBy(() -> f.bindings.requireHarnessRecovery(f.scope(), f.session, WorkspaceLifecycleStore.authority(operation))) + assertThatThrownBy(() -> f.bindings.requireHarnessRecovery(f.scope(), f.session, com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation))) .hasMessageContaining("closed"); } @@ -684,7 +688,7 @@ class WorkspaceProfileLifecycleTest { @Test void duplicateOrdinaryCommitIsFencedByAVersionTwoClaim() throws Exception { var f = fixture(); - var sessions = new ManagedSessionStore(f.jdbc); + var sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(f.jdbc)); var journal = f.transactions.execute(ignored -> new ExtensionRecordJournal(sessions, "tenant", "workspace", f.session).open()); var templates = ManagedHookRecordContractTest.fixtures().get("templates"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceQueryScaleMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceQueryScaleMySqlIT.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceQueryScaleMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceQueryScaleMySqlIT.java index 0f72565c91a..bcc710e3fde 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceQueryScaleMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceQueryScaleMySqlIT.java @@ -3,9 +3,9 @@ package com.alibaba.qwen.code.managedagent.persistence; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.Test; @@ -29,11 +29,11 @@ class WorkspaceQueryScaleMySqlIT { var jdbc = new CapturingJdbc(database.source()); var manager = new DataSourceTransactionManager(database.source()); var transactions = new TransactionTemplate(manager); - var store = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String session = transactions.execute(ignored -> store.insertSessionCommand("tenant", "actor", "CREATE_SESSION", "create", "digest", "qwen-code", null, null, List.of(), null)).sessionId(); - String scope = ManagedSessionStore.sessionScopeKey("tenant", session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session); ManagedReceipts.insertCreation(jdbc, "tenant", "actor".getBytes(java.nio.charset.StandardCharsets.UTF_8), "creation", "digest", session, null, 1); jdbc.update("UPDATE ma_session SET workspace_id = X'726f6f74', workspace_generation = 1, workspace_storage_id = X'73746f72616765'," + " cwd_relative = '.', context_config_ref = ?, context_revision = 1, workspace_config_ref = ?, workspace_policy_ref = ?" @@ -60,7 +60,7 @@ class WorkspaceQueryScaleMySqlIT { .put("sessionId", session).put("journalRevision", 1); ObjectNode ref = new ObjectMapper().createObjectNode().put("resourceId", "history-resource-" + (size - 1)) .put("kind", "managed-definition").put("schemaVersion", 1).put("byteLength", 2) - .put("digest", WorkspaceRecoveryStore.hash("{}".getBytes(java.nio.charset.StandardCharsets.UTF_8))); + .put("digest", com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash("{}".getBytes(java.nio.charset.StandardCharsets.UTF_8))); assertThat(new WorkspaceRecoveryReader(jdbc, null).resource(source, ref).path("bytesBase64").asText()).isEqualTo("e30="); assertBounded(jdbc, jdbc.resource, "PRIMARY", 5); assertBounded(jdbc, jdbc.reference, "ix_ma_ref_resource", 5); @@ -74,12 +74,12 @@ class WorkspaceQueryScaleMySqlIT { ManagedSchema.initialize(database.source()); var jdbc = new CapturingJdbc(database.source()); WorkspaceTestStorage.register(jdbc, "other-tenant", "other-storage"); - String alias = com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository.storageAliasKey("other-tenant", "other-storage"); + String alias = com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository.storageAliasKey("other-tenant", "other-storage"); String tenantKey = ManagedSchemaKeys.key("other-tenant"); jdbc.update("INSERT INTO ma_workspace (tenant_id, workspace_id, workspace_generation, storage_id, display_name, config_ref, policy_ref, state)" + " VALUES ('other-tenant', 'other-workspace', 1, 'other-storage', 'Workspace', 'config', 'policy', 'ACTIVE')"); - var repository = new com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository(database.source(), - new com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector("test", new byte[32])); + var repository = new com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository(database.source(), + new com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector("test", new byte[32])); var request = new com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest( new com.alibaba.qwen.code.runtimebroker.RuntimeScope("other-tenant", "other-workspace", "1", "/scale", "sha256:" + "a".repeat(64), "workspace"), null, "local-process", "other-storage"); @@ -107,7 +107,7 @@ class WorkspaceQueryScaleMySqlIT { for (int index = from; index < size; index++) { String id = "closed-" + index; String sessionKey = ManagedSchemaKeys.key(id); - sessions.add(new Object[] {id, tenantKey, sessionKey, ManagedSessionStore.sessionScopeKey("other-tenant", id)}); + sessions.add(new Object[] {id, tenantKey, sessionKey, com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("other-tenant", id)}); heads.add(new Object[] {tenantKey, sessionKey, id, id}); bindings.add(new Object[] {id, ManagedSchemaKeys.key(id), ManagedSchemaKeys.key("scope", id), alias}); operations.add(new Object[] {ManagedSchemaKeys.key("operation", id), ManagedSchemaKeys.key("other-tenant"), id, @@ -170,9 +170,9 @@ class WorkspaceQueryScaleMySqlIT { workspaces.add(new Object[] {child.getBytes(java.nio.charset.StandardCharsets.UTF_8), child.getBytes(java.nio.charset.StandardCharsets.UTF_8), session.getBytes(java.nio.charset.StandardCharsets.UTF_8), rawRun.getBytes(java.nio.charset.StandardCharsets.UTF_8)}); String resourceId = "history-resource-" + index; - String resourceKey = ManagedSessionStore.resourceKey(scope, resourceId); + String resourceKey = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, resourceId); resources.add(new Object[] {resourceKey, scope, session.getBytes(java.nio.charset.StandardCharsets.UTF_8), - resourceId.getBytes(java.nio.charset.StandardCharsets.UTF_8), WorkspaceRecoveryStore.hash("{}".getBytes(java.nio.charset.StandardCharsets.UTF_8))}); + resourceId.getBytes(java.nio.charset.StandardCharsets.UTF_8), com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash("{}".getBytes(java.nio.charset.StandardCharsets.UTF_8))}); references.add(new Object[] {scope, ManagedSchemaKeys.key(resourceId), resourceKey, session.getBytes(java.nio.charset.StandardCharsets.UTF_8), resourceId.getBytes(java.nio.charset.StandardCharsets.UTF_8)}); } @@ -191,7 +191,7 @@ class WorkspaceQueryScaleMySqlIT { var plan = ExecutedQueryPlan.explain(jdbc, query.sql(), query.arguments()); assertThat(plan.tableScan()).as(plan.text()).isFalse(); if ("PRIMARY".equals(index) && plan.text().contains("Rows fetched before execution")) { - var table = WorkspaceRecoveryStore.parse(jdbc.queryForObject("EXPLAIN FORMAT=JSON " + query.sql(), String.class, query.arguments())) + var table = com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.parse(jdbc.queryForObject("EXPLAIN FORMAT=JSON " + query.sql(), String.class, query.arguments())) .path("query_block").path("table"); assertThat(table.path("access_type").asText()).isEqualTo("const"); assertThat(table.path("key").asText()).isEqualTo("PRIMARY"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReaderAssertions.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReaderAssertions.java similarity index 88% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReaderAssertions.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReaderAssertions.java index 8b75dd32566..05667142c2f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReaderAssertions.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryReaderAssertions.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.springframework.jdbc.core.JdbcTemplate; @@ -15,25 +15,27 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; /** Shares the real original-publication fixture without widening the maintenance API. */ public final class WorkspaceRecoveryReaderAssertions { + private static final com.fasterxml.jackson.databind.ObjectMapper JSON = new com.fasterxml.jackson.databind.ObjectMapper(); + private WorkspaceRecoveryReaderAssertions() { } public static void verifyOriginalPublication(JdbcTemplate jdbc, ToolPublicationObjectStore objects, JsonNode key, JsonNode admission, JsonNode manifest, long sequence, byte[] expectedSegment) { var before = snapshot(jdbc); - ObjectNode source = WorkspaceRecoveryStore.JSON.createObjectNode(); + ObjectNode source = JSON.createObjectNode(); ObjectNode head = source.putObject("head"); key.fields().forEachRemaining(field -> head.set(field.getKey(), field.getValue())); head.put("journalRevision", jdbc.queryForObject("SELECT journal_revision FROM ma_session_head", Long.class)); head.put("committedSequence", sequence); var reader = new WorkspaceRecoveryReader(jdbc, objects); - ObjectNode request = WorkspaceRecoveryStore.JSON.createObjectNode().put("executionCallId", "execution-1") + ObjectNode request = JSON.createObjectNode().put("executionCallId", "execution-1") .put("sequence", sequence); request.set("outcomeRef", admission); request.set("manifestRef", manifest); JsonNode receipt = reader.publicationReceipt(source, request); assertThat(receipt.path("publicationId").asText()).isEqualTo("pub-1"); - JsonNode manifestBody = WorkspaceRecoveryStore.parse(Base64.getDecoder().decode( + JsonNode manifestBody = com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.parse(Base64.getDecoder().decode( reader.resource(source, manifest).path("bytesBase64").asText())); JsonNode contentRef = manifestBody.path("contents").get(0).path("body").path("ref"); assertThat(receipt.path("seals")).hasSize(contentRef.isObject() ? 1 : 2); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStoreTest.java similarity index 90% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStoreTest.java index 385e9eb547c..dc20ac50945 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceRecoveryStoreTest.java @@ -1,10 +1,12 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.managedagent.catalog.AgentDefinitionExtensions; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount; +import com.alibaba.qwen.code.managedagent.contracts.catalog.AgentDefinitionExtensions; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.RequestDigests; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; @@ -39,6 +41,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; class WorkspaceRecoveryStoreTest { + private static final com.fasterxml.jackson.databind.ObjectMapper JSON = new com.fasterxml.jackson.databind.ObjectMapper(); + @TempDir Path temporary; private JdbcTemplate jdbc; private DataSourceTransactionManager manager; @@ -60,10 +64,10 @@ class WorkspaceRecoveryStoreTest { properties.getHarness().setCapabilityDigest("a".repeat(64)); properties.getRuntimeBroker().setVerifiedWorkspaceRecoveryEnabled(true); properties.getRuntimeBroker().setWorkspaceMounts(List.of(new WorkspaceMount("tenant", "storage", root.toString()))); - guard = new WorkspaceStorageGuard(jdbc, manager, properties, path -> new WorkspaceStorageGuard.Identity( + guard = new WorkspaceStorageGuard(jdbc, manager, properties, path -> new StorageIdentityReader.Identity( path.toRealPath().toString(), "test-host", "test-device", "test-inode", "2026-10-01T00:00:00Z")); - sessions = new ManagedAgentStore(jdbc, WorkspaceRecoveryStore.JSON, Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + sessions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, JSON, Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); for (String workspace : List.of("workspace-a", "workspace-b")) { jdbc.update("INSERT INTO ma_workspace (tenant_id, workspace_id, workspace_generation," + " storage_id, display_name, config_ref, policy_ref, state) VALUES ('tenant', ?, 1, 'storage'," @@ -73,7 +77,7 @@ class WorkspaceRecoveryStoreTest { } guard.register("tenant", "storage", UUID.randomUUID().toString()); String fence = UUID.randomUUID().toString(); - request = WorkspaceRecoveryStore.JSON.createObjectNode().put("version", 1) + request = JSON.createObjectNode().put("version", 1) .put("operationId", UUID.randomUUID().toString()).put("tenantId", "tenant").put("storageId", "storage") .put("fenceOperationId", fence).put("mountRevision", 1).put("sourceRoot", root.toString()) .put("bundleRoot", bundle.toString()).put("fileHistoryRoot", temporary.resolve("history").toString()) @@ -101,10 +105,10 @@ class WorkspaceRecoveryStoreTest { @Test void registrationRejectsAPrivateClosureResourceDeleteIntent() { String session = session("workspace-a"); - String scope = ManagedSessionStore.sessionScopeKey("tenant", session); - String key = ManagedSessionStore.resourceKey(scope, "checkpoint"); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session); + String key = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(scope, "checkpoint"); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, state, deletion_operation_key, gmt_create) VALUES (?, ?, 'tenant', 'private-key', ?, 'checkpoint', 'managed-checkpoint', 1, 0, ?, 'MYSQL_INLINE', ?, 'REFERENCED', ?, " + ManagedSchemaTime.SQL_NOW + ")", - key, scope, session, WorkspaceRecoveryStore.hash(new byte[0]), new byte[0], WorkspaceOperations.hash("gc")); + key, scope, session, com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(new byte[0]), new byte[0], WorkspaceOperations.hash("gc")); jdbc.update("INSERT INTO ma_resource_ref (session_scope_key, owner_kind, owner_key, owner_revision, resource_key, tenant_id, workspace_id, session_id, resource_id, journal_revision, gmt_create) VALUES (?, 'JOURNAL', ?, 1, ?, 'tenant', 'private-key', ?, 'checkpoint', 1, " + ManagedSchemaTime.SQL_NOW + ")", scope, scope, key, session); assertThatThrownBy(this::capture).hasMessageContaining("source_drift"); assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ma_operation WHERE operation_kind = 'WORKSPACE_RECOVERY'", Long.class)).isZero(); @@ -176,7 +180,7 @@ class WorkspaceRecoveryStoreTest { capture.call("sessionComplete", object().put("sessionId", session).set("summary", object())); Path path = Files.createDirectories(bundle.resolve(".w1-recovery")).resolve("manifest.json"); Files.writeString(path, manifest(capture).toString()); - String manifestDigest = WorkspaceRecoveryStore.hash(Files.readAllBytes(path)); + String manifestDigest = com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(Files.readAllBytes(path)); var params = object().put("manifestDigest", manifestDigest); params.set("result", object().put("contentVerified", true).put("activation", false)); capture.call("finish", params); @@ -199,10 +203,10 @@ class WorkspaceRecoveryStoreTest { String session = committedAndRetiredSession(); jdbc.update("UPDATE ma_session_head SET compacted_through_revision = journal_revision WHERE session_id = ?", session); assertThatThrownBy(this::capture).hasMessageContaining("source_drift"); - String scope = ManagedSessionStore.sessionScopeKey("tenant", session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session); String id = "session-resources-" + scope; - String key = com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.operationKey( - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind.SESSION_RESOURCE_GC, scope, scope, id); + String key = com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.operationKey( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind.SESSION_RESOURCE_GC, scope, scope, id); jdbc.update("INSERT INTO ma_operation (operation_key, scope_key, session_scope_key, operation_kind, operation_id, tenant_id, session_id," + " target_kind, target_key, request_digest, request_json, phase, state, gmt_create, gmt_modified)" + " VALUES (?, ?, ?, 'SESSION_RESOURCE_GC', ?, 'tenant', ?, 'SESSION', ?, 'digest', '{}', 'COLLECTING', 'UNKNOWN', 1, 1)", @@ -224,7 +228,7 @@ class WorkspaceRecoveryStoreTest { request.put("mountRevision", revision); } long committedRevision = revision; - var journal = new ManagedSessionStore(jdbc); + var journal = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String token = "a".repeat(64); String records = "{\"subtype\":\"session_execution_engine\"}\n{\"subtype\":\"managed_session_header_v1\"}\n"; byte[] bytes = records.getBytes(StandardCharsets.UTF_8); @@ -232,8 +236,8 @@ class WorkspaceRecoveryStoreTest { tx.execute(status -> journal.acquireWriter("tenant", session, token, new ManagedSessionStoreModels.AcquireWriterRequest("workspace-a", "original", 30_000L))); tx.execute(status -> journal.commit("tenant", session, token, new ManagedSessionStoreModels.CommitTransactionRequest( - "workspace-a", "original", 1, 0, 0, "genesis", "session.create", "genesis-command", WorkspaceRecoveryStore.hash(bytes), - 0, 0, 0, null, null, null, 0, null, 2, java.util.Base64.getEncoder().encodeToString(bytes), WorkspaceRecoveryStore.hash(bytes), List.of()))); + "workspace-a", "original", 1, 0, 0, "genesis", "session.create", "genesis-command", com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(bytes), + 0, 0, 0, null, null, null, 0, null, 2, java.util.Base64.getEncoder().encodeToString(bytes), com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(bytes), List.of()))); tx.execute(status -> journal.sealWriter("tenant", session, token, new ManagedSessionStoreModels.SealWriterRequest("workspace-a", "original", 1))); guard.fence("tenant", "storage", committedRevision, request.path("fenceOperationId").asText()); retireSession(session); @@ -243,7 +247,7 @@ class WorkspaceRecoveryStoreTest { @Test void recoveryAdmissionIgnoresOtherOperationKindsButBlocksUnresolvedSessionEffects() { String session = session("workspace-a"); - String scope = ManagedSessionStore.sessionScopeKey("tenant", session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session); jdbc.update("INSERT INTO ma_operation (operation_key, scope_key, session_scope_key, operation_kind, operation_id, tenant_id, session_id," + " target_kind, target_key, request_digest, request_json, state, phase, gmt_create, gmt_modified)" + " VALUES (?, ?, ?, 'OBJECT_PUT', 'original-put', 'tenant', ?, 'RESOURCE', ?, 'digest', '{}', 'RETURNED', 'PUT', 1, 1)", @@ -295,11 +299,11 @@ class WorkspaceRecoveryStoreTest { String session = session("workspace-a"); head(session); byte[] bytes = "{\"proof\":true}".getBytes(StandardCharsets.UTF_8); - String digest = WorkspaceRecoveryStore.hash(bytes); + String digest = com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(bytes); // The shape the stream-capture collector leaves: identity kept, state COLLECTED, bytes NULL. jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create) VALUES (?, ?, 'tenant', 'private-key', ?, 'resource', 'managed-definition', 1, ?, ?, 'MYSQL_INLINE', NULL, 'command', 'COLLECTED', " + ManagedSchemaTime.SQL_NOW + ")", - ManagedSessionStore.resourceKey(ManagedSessionStore.sessionScopeKey("tenant", session), "resource"), - ManagedSessionStore.sessionScopeKey("tenant", session), session, bytes.length, digest); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), "resource"), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), session, bytes.length, digest); var capture = capture(); ObjectNode params = object().put("sessionId", session); params.set("ref", object().put("resourceId", "resource").put("kind", "managed-definition") @@ -315,15 +319,15 @@ class WorkspaceRecoveryStoreTest { head(session); // The shape the publication collector leaves: the publication stays REFERENCED while // the object row is state COLLECTED with its bytes permanently freed. - String sessionScope = ManagedSessionStore.sessionScopeKey("tenant", session); + String sessionScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session); String scope = ToolPublicationDataStore.scope("tenant", "private-key", session); jdbc.update("INSERT INTO ma_output_capture (scope_key, tenant_key, tenant_id, workspace_id, session_id," + " publication_id, execution_key, capture_id, binding_json, binding_digest, token_hash, state," + " capture_bytes, producer_bytes, admission_bytes, producer_phase, write_evidence, accepted_complete," + " receipt_sequence, receipt_revision) VALUES (?, ?, 'tenant', 'private-key', ?, 'pub-1', ?," + " 'capture-1', '{}', ?, ?, 'FENCED', 0, 0, 0, 'REFERENCED', TRUE, TRUE, 1, 1)", - scope, WorkspaceRecoveryStore.hash("tenant"), session, scope, scope, scope); - jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, scope_key, publication_id, slot_key, resource_id, kind, byte_length, sha256, inline_bytes, capture_state, operation_id, tenant_id, workspace_id, session_id, schema_version, storage_kind, state, gmt_create) VALUES (?, ?, ?, 'pub-1', 'terminal', 'resource-1', 'managed-tool-terminal', 64, ?, NULL, 'COLLECTED', 'op-1', 'tenant', 'private-key', ?, 1, 'MYSQL_INLINE', 'COLLECTED', " + ManagedSchemaTime.SQL_NOW + ")", ManagedSessionStore.resourceKey(sessionScope, "resource-1"), sessionScope, scope, "a".repeat(64), session); + scope, com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash("tenant"), session, scope, scope, scope); + jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, scope_key, publication_id, slot_key, resource_id, kind, byte_length, sha256, inline_bytes, capture_state, operation_id, tenant_id, workspace_id, session_id, schema_version, storage_kind, state, gmt_create) VALUES (?, ?, ?, 'pub-1', 'terminal', 'resource-1', 'managed-tool-terminal', 64, ?, NULL, 'COLLECTED', 'op-1', 'tenant', 'private-key', ?, 1, 'MYSQL_INLINE', 'COLLECTED', " + ManagedSchemaTime.SQL_NOW + ")", com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(sessionScope, "resource-1"), sessionScope, scope, "a".repeat(64), session); var capture = capture(); assertThatThrownBy(() -> capture.call("publicationObject", object().put("sessionId", session).put("publicationId", "pub-1").put("slotKey", "terminal"))) @@ -427,11 +431,11 @@ class WorkspaceRecoveryStoreTest { head(pinned); jdbc.update("UPDATE ma_session_head SET workspace_id = X'776f726b73706163652d62'" + " WHERE session_id = ?", pinned); - var instructions = new ManagedSessionStore(jdbc).publishAgentInstructions( + var instructions = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).publishAgentInstructions( "tenant", "workspace-b", pinned, "Keep the agent instructions. 保留指令。"); var skillCatalog = List.>of(Map.of( "name", "review", "description", "Review", "body", "Check the diff. 检查差异。")); - var skills = new ManagedSessionStore(jdbc).publishAgentSkills( + var skills = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).publishAgentSkills( "tenant", "workspace-b", pinned, skillCatalog); JsonNode sources = call(capture(), "sessions").path("sessions"); assertThat(sources).hasSize(3); @@ -498,7 +502,7 @@ class WorkspaceRecoveryStoreTest { .resolve(".w1-recovery")).resolve("manifest.json"); Files.writeString(manifest, manifest(capture).toString()); ObjectNode finish = object().put("manifestDigest", - WorkspaceRecoveryStore.hash(Files.readAllBytes(manifest))); + com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(Files.readAllBytes(manifest))); finish.set("result", object().put("contentVerified", true) .put("activation", false)); assertThat(capture.call("finish", finish).path("result") @@ -614,12 +618,12 @@ class WorkspaceRecoveryStoreTest { var digests = new RequestDigests(); var skills = List.>of(Map.of("name", "review", "description", "Review", "body", "ORIGINAL")); - new ManagedSessionSkills(jdbc, WorkspaceRecoveryStore.JSON, digests, + new ManagedSessionSkills(jdbc, JSON, digests, new ManagedAgentDefinitionStore(jdbc, digests)).save("tenant", session, skills); var capture = capture(); String value = jdbc.queryForObject("SELECT result_json FROM ma_request_receipt WHERE tenant_id = ?" + " AND session_id = ? AND namespace = 'SESSION_CREATE_ACTOR'", String.class, "tenant", session); - ObjectNode snapshot = (ObjectNode) WorkspaceRecoveryStore.JSON.readTree(value); + ObjectNode snapshot = (ObjectNode) JSON.readTree(value); if (badVersion) { snapshot.put("version", 2); } else { @@ -628,7 +632,7 @@ class WorkspaceRecoveryStoreTest { jdbc.update("UPDATE ma_request_receipt SET result_json = ? WHERE tenant_id = ? AND session_id = ?" + " AND namespace = 'SESSION_CREATE_ACTOR'", snapshot.toString(), "tenant", session); assertThatThrownBy(() -> capture.call("sessionComplete", object().put("sessionId", session).set("summary", object()))) - .isInstanceOf(WorkspaceRecoveryStore.RecoveryFailure.class).hasMessageContaining("source_drift"); + .isInstanceOf(com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.RecoveryFailure.class).hasMessageContaining("source_drift"); assertThat(capture.inspect().path("state").asText()).isEqualTo("INVALIDATED"); assertThat(capture.inspect().path("lastErrorCode").asText()).isEqualTo("source_drift"); assertThat(jdbc.queryForObject("SELECT state FROM ma_operation_work WHERE work_kind = 'SESSION' AND session_id = ?", @@ -659,13 +663,13 @@ class WorkspaceRecoveryStoreTest { String session = session("workspace-a"); head(session); byte[] bytes = "{\"proof\":true}".getBytes(StandardCharsets.UTF_8); - String digest = WorkspaceRecoveryStore.hash(bytes); + String digest = com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(bytes); jdbc.update("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create) VALUES (?, ?, 'tenant', 'private-key', ?, 'resource', 'managed-definition', 1, ?, ?, 'MYSQL_INLINE', ?, 'command', 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ")", - ManagedSessionStore.resourceKey(ManagedSessionStore.sessionScopeKey("tenant", session), "resource"), - ManagedSessionStore.sessionScopeKey("tenant", session), session, bytes.length, digest, bytes); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), "resource"), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), session, bytes.length, digest, bytes); jdbc.update("INSERT INTO ma_resource_ref (session_scope_key, owner_kind, owner_key, owner_revision, resource_key, tenant_id, workspace_id, session_id, journal_revision, resource_id, gmt_create) VALUES (?, 'JOURNAL', ?, 1, ?, 'tenant', 'private-key', ?, 1, 'resource', " + ManagedSchemaTime.SQL_NOW + ")", - ManagedSessionStore.sessionScopeKey("tenant", session), ManagedSessionStore.sessionScopeKey("tenant", session), - ManagedSessionStore.resourceKey(ManagedSessionStore.sessionScopeKey("tenant", session), "resource"), session); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey(com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), "resource"), session); var capture = capture(); JsonNode source = call(capture, "sessions").path("sessions").get(0).path("source"); assertThat(source.path("head").path("workspaceId").asText()).isEqualTo("private-key"); @@ -716,7 +720,7 @@ class WorkspaceRecoveryStoreTest { capture.call("sessionComplete", done); Path manifest = Files.createDirectories(bundle.resolve(".w1-recovery")).resolve("manifest.json"); Files.writeString(manifest, manifest(capture).toString()); - String digest = WorkspaceRecoveryStore.hash(Files.readAllBytes(manifest)); + String digest = com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(Files.readAllBytes(manifest)); ObjectNode finish = object().put("manifestDigest", "a".repeat(64)); finish.set("result", object().put("contentVerified", true).put("activation", false)); assertThatThrownBy(() -> capture.call("finish", finish)).hasMessageContaining("manifest_digest_conflict"); @@ -776,7 +780,7 @@ class WorkspaceRecoveryStoreTest { "actor".getBytes(StandardCharsets.UTF_8), "actor".getBytes(StandardCharsets.UTF_8)); Path manifest = Files.createDirectories(bundle.resolve(".w1-recovery")).resolve("manifest.json"); Files.writeString(manifest, manifest(capture).toString()); - ObjectNode params = object().put("manifestDigest", WorkspaceRecoveryStore.hash(Files.readAllBytes(manifest))); + ObjectNode params = object().put("manifestDigest", com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(Files.readAllBytes(manifest))); params.set("result", object().put("contentVerified", true).put("activation", false)); assertThatThrownBy(() -> capture.call("finish", params)).hasMessageContaining("source_drift"); assertThat(capture.inspect().path("state").asText()).isEqualTo("INVALIDATED"); @@ -788,7 +792,7 @@ class WorkspaceRecoveryStoreTest { var original = capture(); Path manifest = Files.createDirectories(bundle.resolve(".w1-recovery")).resolve("manifest.json"); Files.writeString(manifest, manifest(original).toString()); - ObjectNode params = object().put("manifestDigest", WorkspaceRecoveryStore.hash(Files.readAllBytes(manifest))); + ObjectNode params = object().put("manifestDigest", com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceRecoveryProtocol.hash(Files.readAllBytes(manifest))); params.set("result", object().put("contentVerified", true).put("activation", false)); try (var executor = Executors.newSingleThreadExecutor()) { var racingJdbc = new ManagedSchemaJdbcTemplate(jdbc.getDataSource()) { @@ -847,11 +851,11 @@ class WorkspaceRecoveryStoreTest { private String retireSession(String session) { String operation = UUID.randomUUID().toString(); - String scope = ManagedSessionStore.sessionScopeKey("tenant", session); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session); jdbc.update("UPDATE ma_session SET status = 'DELETING' WHERE session_id = ?", session); jdbc.update("INSERT INTO ma_operation (operation_key, scope_key, session_scope_key, target_kind, target_key, tenant_id, session_id, operation_id, operation_kind, actor_digest, idempotency_key, request_digest, request_json, state, phase, admission_stage, delivery_state, session_status_before, claim_owner, claim_until, claim_generation, available_at, gmt_create, gmt_modified) VALUES (?, ?, ?, 'SESSION', ?, 'tenant', ?, ?, 'DELETE', '', 'delete', 'digest', '{}', 'RUNNING', 'LEASED', 'JAVA_DURABLE', 'LEASED', 'CLOSED', 'worker', (" + ManagedSchemaTime.SQL_NOW + " + 60000), 1, 0, 0, 0)", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.operationKey( - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract.OperationKind.DELETE, + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.operationKey( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract.OperationKind.DELETE, scope, WorkspaceOperations.hash(session), operation), scope, scope, WorkspaceOperations.hash(session), session, operation); jdbc.update("UPDATE ma_session_head SET latest_checkpoint_resource_id = X'72657461696e6564' WHERE session_id = ?", session); assertThat(new TransactionTemplate(manager).execute(status -> @@ -877,7 +881,7 @@ class WorkspaceRecoveryStoreTest { } private static ObjectNode object() { - return WorkspaceRecoveryStore.JSON.createObjectNode(); + return JSON.createObjectNode(); } private static JsonNode call(WorkspaceRecoveryStore store, String method) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceSchemaConsolidationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceSchemaConsolidationTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceSchemaConsolidationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceSchemaConsolidationTest.java index 2aaaa6faa6b..2d3bb77a260 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceSchemaConsolidationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceSchemaConsolidationTest.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.managedagent.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.WorkspaceCsiRegistration; import com.alibaba.qwen.code.managedagent.model.StoreModels; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; @@ -126,7 +127,7 @@ class WorkspaceSchemaConsolidationTest { var binding = new ContextBinding("tenant", "root", 1, "storage", ".", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1); var child = new StoreModels.SessionRecord("tenant", "child", "qwen-code", "1", "Child", "ACTIVE", null, null, 0, 0, 0, now, now, null, 0, binding, "yolo", "hosted-workspace-files/1"); - var authority = new WorkspaceExecutionStore(jdbc, manager); + var authority = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, manager)); authority.authorize(child); jdbc.update("DELETE FROM ma_workspace_access WHERE tenant_id = ? AND workspace_id = ?", "tenant", "root"); jdbc.update("INSERT INTO ma_workspace_access (tenant_id, workspace_id, actor_id, role) VALUES ('tenant', 'root', ?, 'OWNER')", @@ -138,7 +139,7 @@ class WorkspaceSchemaConsolidationTest { void expiredMaintenanceClaimCannotReleaseOrReplaceTheOriginalPhysicalHolder() { root("root"); var store = new ChildWorkspaceStore(jdbc, manager); - var authority = new WorkspaceExecutionStore(jdbc, manager); + var authority = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, manager)); var storage = new ContextBinding("tenant", "root", 1, "storage", ".", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1); long now = System.currentTimeMillis(); var admitted = store.admit("tenant", "parent", "run", storage, now); @@ -168,7 +169,7 @@ class WorkspaceSchemaConsolidationTest { void eachPhysicalStepKeepsItsOwnIdentityEvenWithinOneClaim() { root("root"); var children = new ChildWorkspaceStore(jdbc, manager); - var authority = new WorkspaceExecutionStore(jdbc, manager); + var authority = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, manager)); var binding = new ContextBinding("tenant", "root", 1, "storage", ".", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1); long now = System.currentTimeMillis(); var admitted = children.admit("tenant", "parent", "run", binding, now); @@ -196,7 +197,7 @@ class WorkspaceSchemaConsolidationTest { @Test void privateRecoveryInventoryExcludesPublicOnlyBodiesAtScale() throws Exception { - String scope = ManagedSessionStore.sessionScopeKey("tenant", "session"); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", "session"); byte[] body = "{}".getBytes(StandardCharsets.UTF_8); java.util.List resources = new java.util.ArrayList<>(); java.util.List roots = new java.util.ArrayList<>(); @@ -211,8 +212,8 @@ class WorkspaceSchemaConsolidationTest { jdbc.batchUpdate("INSERT INTO ma_resource (resource_key, session_scope_key, tenant_id, workspace_id, session_id, resource_id, kind, schema_version, byte_length, sha256, storage_kind, inline_bytes, publish_command_id, state, gmt_create) VALUES (?, ?, 'tenant', 'root', 'session', ?, ?, 1, 2, ?, 'MYSQL_INLINE', ?, 'fixture', 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ")", resources); jdbc.batchUpdate("INSERT INTO ma_resource_ref (session_scope_key, owner_kind, owner_key, owner_revision, resource_key, tenant_id, workspace_id, session_id, journal_revision, resource_id, gmt_create) VALUES (?, ?, ?, 1, ?, 'tenant', 'root', 'session', ?, ?, " + ManagedSchemaTime.SQL_NOW + ")", roots); var catalog = new WorkspaceCsiCheckpointSnapshotStore(jdbc.getDataSource(), - new com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository(jdbc.getDataSource(), - new com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector("test", new byte[32])), new ObjectMapper()); + new com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository(jdbc.getDataSource(), + new com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector("test", new byte[32])), new ObjectMapper()); var key = new ObjectMapper().createObjectNode().put("tenantId", "tenant").put("workspaceId", "root").put("sessionId", "session"); try (var connection = jdbc.getDataSource().getConnection()) { connection.setTransactionIsolation(java.sql.Connection.TRANSACTION_REPEATABLE_READ); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuardTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuardTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuardTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuardTest.java index 5316181f721..a13d4c7c8a1 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuardTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceStorageGuardTest.java @@ -5,15 +5,16 @@ import com.alibaba.qwen.code.daemon.HostedHarnessCapabilities; import com.alibaba.qwen.code.daemon.HostedHarnessClient; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.StorageIdentityReader; import com.alibaba.qwen.code.managedagent.harness.QwenHostedHarnessConnector; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -82,7 +83,7 @@ class WorkspaceStorageGuardTest { root = Files.createDirectory(temp.resolve("empty-native-root")).toRealPath(); properties.getRuntimeBroker().setWorkspaceMounts(List.of( new WorkspaceMount("tenant", "storage", root.toString()))); - var guard = new WorkspaceStorageGuard(jdbc, new DataSourceTransactionManager(dataSource), properties); + var guard = new WorkspaceStorageGuard(jdbc, new DataSourceTransactionManager(dataSource), properties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()); guard.register("tenant", "storage", UUID.randomUUID().toString()); Process probe = new ProcessBuilder("node", "-e", "console.log(require('node:fs').lstatSync(process.argv[1],{bigint:true}).birthtimeNs.toString())", @@ -109,10 +110,10 @@ class WorkspaceStorageGuardTest { Files.setLastModifiedTime(root, FileTime.fromMillis(1)); } var manager = new DataSourceTransactionManager(dataSource); - var first = new WorkspaceStorageGuard(jdbc, manager, properties); + var first = new WorkspaceStorageGuard(jdbc, manager, properties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()); String operation = UUID.randomUUID().toString(); first.register("tenant", "storage", operation); - var restarted = new WorkspaceStorageGuard(jdbc, manager, properties); + var restarted = new WorkspaceStorageGuard(jdbc, manager, properties, new com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader()); if ("Linux".equals(System.getProperty("os.name"))) { FileTime birth = Files.readAttributes(root, BasicFileAttributes.class).creationTime(); Files.setLastModifiedTime(root, birth); @@ -225,7 +226,7 @@ class WorkspaceStorageGuardTest { WorkspaceStorageGuard stable = fixture.guard(); stable.register("tenant", "storage", UUID.randomUUID().toString()); var attributes = Files.readAttributes(history, BasicFileAttributes.class); - String identity = new ObjectMapper().writeValueAsString(new WorkspaceStorageGuard.Identity( + String identity = new ObjectMapper().writeValueAsString(new StorageIdentityReader.Identity( history.toString(), "test-host", "test-device", attributes.fileKey().toString(), attributes.creationTime().toInstant().toString())); String migrationId = UUID.randomUUID().toString(); @@ -265,7 +266,7 @@ class WorkspaceStorageGuardTest { @Test void rejectsDeletedAndRestoredRootWhenInodeIsReusedButBirthTimeChanges() throws Exception { AtomicReference birth = new AtomicReference<>("2026-09-30T00:00:00.123456789Z"); - WorkspaceStorageGuard.IdentityReader reader = path -> new WorkspaceStorageGuard.Identity( + StorageIdentityReader reader = path -> new StorageIdentityReader.Identity( path.toString(), "test-host", "same-device", "reused-inode", birth.get()); var manager = new DataSourceTransactionManager(dataSource); var original = new WorkspaceStorageGuard(jdbc, manager, properties, reader); @@ -320,7 +321,7 @@ class WorkspaceStorageGuardTest { if (unreadable.get() != 0) { throw new IOException("momentary mount failure"); } - return new WorkspaceStorageGuard.Identity(path.toString(), "host", "device", "inode", + return new StorageIdentityReader.Identity(path.toString(), "host", "device", "inode", "2026-10-07T00:00:00Z"); }); guard.register("tenant", "storage", UUID.randomUUID().toString()); @@ -328,7 +329,7 @@ class WorkspaceStorageGuardTest { var session = new SessionRecord("tenant", sessionId, "qwen-code", null, null, "ACTIVE", null, null, 0, 0, 0, 1, 1, null, 1, binding, "default", "hosted-workspace-files/1"); - var execution = spy(new WorkspaceExecutionStore(jdbc, manager, guard)); + var execution = spy(com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, manager, guard))); doNothing().when(execution).authorizeActionResponse(session); doNothing().when(execution).authorizePassiveAttachment(session); var sessions = mock(AgentStateStore.class); @@ -374,12 +375,12 @@ class WorkspaceStorageGuardTest { void actionResponseRetriesARevokedGrantWhileKeepingStructuralRefusalsTerminal() { var manager = new DataSourceTransactionManager(dataSource); var guard = new WorkspaceStorageGuard(jdbc, manager, properties, path -> - new WorkspaceStorageGuard.Identity(path.toString(), "host", "device", "inode", + new StorageIdentityReader.Identity(path.toString(), "host", "device", "inode", "2026-10-07T00:00:00Z")); guard.register("tenant", "storage", UUID.randomUUID().toString()); // The action-response authority is derived from the real grant rows, // not injected. - var execution = spy(new WorkspaceExecutionStore(jdbc, manager, guard)); + var execution = spy(com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, manager, guard))); var bound = new ContextBinding("tenant", "workspace", 1, "storage", "child", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1); String sessionId = UUID.randomUUID().toString(); @@ -499,7 +500,7 @@ class WorkspaceStorageGuardTest { void nativeBirthEvidenceRequiresTheSameRootAndFullNanosecondBirthTime() throws Exception { FileTime birth = FileTime.from(Instant.parse("2026-09-30T00:00:00.123456789Z")); String evidence = "7|42|2026-09-30 00:00:00.123456789 +0000"; - assertThat(WorkspaceStorageGuard.verifiedNativeBirthTime(evidence, "7", "42", birth)) + assertThat(com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader.verifiedNativeBirthTime(evidence, "7", "42", birth)) .isEqualTo(birth.toInstant().toString()); for (String invalid : List.of( "8|42|2026-09-30 00:00:00.123456789 +0000", @@ -509,7 +510,7 @@ class WorkspaceStorageGuardTest { "7|42|1970-01-01 00:00:00.000000000 +0000", "7|42|2026-02-30 00:00:00.123456789 +0000", evidence + "|extra")) { - assertThatThrownBy(() -> WorkspaceStorageGuard.verifiedNativeBirthTime(invalid, "7", "42", birth)) + assertThatThrownBy(() -> com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader.verifiedNativeBirthTime(invalid, "7", "42", birth)) .isInstanceOf(IOException.class); } } @@ -517,13 +518,13 @@ class WorkspaceStorageGuardTest { @Test void rejectsUnavailableAndAmbiguousBirthTimeWithoutUsingDirectoryMtimeAsIdentity() throws Exception { FileTime birth = FileTime.from(Instant.parse("2026-09-30T00:00:00.123456789Z")); - assertThatThrownBy(() -> WorkspaceStorageGuard.verifiedBirthTime(birth, birth)) + assertThatThrownBy(() -> com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader.verifiedBirthTime(birth, birth)) .isInstanceOf(java.io.IOException.class); - assertThatThrownBy(() -> WorkspaceStorageGuard.verifiedBirthTime(FileTime.fromMillis(0), birth)) + assertThatThrownBy(() -> com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader.verifiedBirthTime(FileTime.fromMillis(0), birth)) .isInstanceOf(java.io.IOException.class); - assertThat(WorkspaceStorageGuard.verifiedBirthTime(birth, FileTime.fromMillis(1))) + assertThat(com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader.verifiedBirthTime(birth, FileTime.fromMillis(1))) .isEqualTo("2026-09-30T00:00:00.123456789Z"); - assertThat(WorkspaceStorageGuard.verifiedBirthTime(birth, FileTime.fromMillis(2))) + assertThat(com.alibaba.qwen.code.runtimebroker.provisioning.NativeStorageIdentityReader.verifiedBirthTime(birth, FileTime.fromMillis(2))) .isEqualTo("2026-09-30T00:00:00.123456789Z"); } @@ -716,7 +717,7 @@ class WorkspaceStorageGuardTest { if (!attributes.isDirectory() || !path.equals(path.toRealPath())) { throw new java.io.IOException("Root changed"); } - return new WorkspaceStorageGuard.Identity(path.toString(), "test-host", + return new StorageIdentityReader.Identity(path.toString(), "test-host", "test-device", attributes.fileKey().toString(), attributes.creationTime().toInstant().toString()); }); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceTestStorage.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceTestStorage.java similarity index 89% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceTestStorage.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceTestStorage.java index e30111edde4..ba05d48a0c8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceTestStorage.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/persistence/WorkspaceTestStorage.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.persistence; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.springframework.jdbc.core.JdbcTemplate; /** Explicit storage model for offline SQL tests; native mount tests use the real guard. */ diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ExternalRuntimeConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ExternalRuntimeConfigurationTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ExternalRuntimeConfigurationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ExternalRuntimeConfigurationTest.java index f11187ed25e..e9883e9dad7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ExternalRuntimeConfigurationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ExternalRuntimeConfigurationTest.java @@ -158,9 +158,9 @@ class ExternalRuntimeConfigurationTest { @Test void bindsTheExternalWorkspaceKindWithoutAdmittingHostedWorkspaceFeatures() { var properties = properties( - com.alibaba.qwen.code.runtimebroker.provisioning.ExternalWorkspaceProvisioner.KIND); + com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE); assertEquals( - com.alibaba.qwen.code.runtimebroker.provisioning.ExternalWorkspaceProvisioner.KIND, + com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE, properties.getRuntimeBroker().getProvisioner()); assertEquals(ManagedAgentPreflight.Status.PASS, ManagedAgentPreflight.startupValidationCheck(properties).status()); @@ -176,7 +176,7 @@ class ExternalRuntimeConfigurationTest { @Test void theOperatorVisibleTextOfBothExternalKindsIsPinned() { var workspace = brokerCheck(properties( - com.alibaba.qwen.code.runtimebroker.provisioning.ExternalWorkspaceProvisioner.KIND)); + com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE)); assertEquals("external-workspace configuration accepted; provider reachability," + " worker attestation and storage registration are checked on first use", workspace.detail()); @@ -186,7 +186,7 @@ class ExternalRuntimeConfigurationTest { scratch.detail()); // Configurable is not usable: the prerequisite WARN must say so. var warn = ManagedAgentPreflight.brokerChecks(properties( - com.alibaba.qwen.code.runtimebroker.provisioning.ExternalWorkspaceProvisioner.KIND)) + com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE)) .stream().filter(check -> check.name().equals("external-prerequisite")) .findFirst().orElseThrow(); assertEquals(ManagedAgentPreflight.Status.WARN, warn.status()); @@ -217,6 +217,6 @@ class ExternalRuntimeConfigurationTest { } private static ManagedAgentProperties scratchProperties() { - return properties(ExternalRuntimeProvisioner.KIND); + return properties(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/preflight/HarnessBrokerUrlTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/preflight/HarnessBrokerUrlTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/preflight/HarnessBrokerUrlTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/preflight/HarnessBrokerUrlTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ManagedAgentPreflightTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ManagedAgentPreflightTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ManagedAgentPreflightTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ManagedAgentPreflightTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ResolvedConfigurationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ResolvedConfigurationTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ResolvedConfigurationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ResolvedConfigurationTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ServiceAccountTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ServiceAccountTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ServiceAccountTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/preflight/ServiceAccountTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinatorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinatorTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinatorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinatorTest.java index a2eb2765842..e6881cdb301 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinatorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ActionResponseCoordinatorTest.java @@ -1,6 +1,7 @@ package com.alibaba.qwen.code.managedagent.session; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.harness.UnavailableHarnessConnector; import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore; import org.junit.jupiter.api.Test; @@ -34,7 +35,7 @@ class ActionResponseCoordinatorTest { } @org.junit.jupiter.api.Test void disabledHarnessDoesNotScanRenameDeliveries() { - var store = org.mockito.Mockito.mock(com.alibaba.qwen.code.managedagent.session.AgentStateStore.class); + var store = org.mockito.Mockito.mock(com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.class); var executor = org.mockito.Mockito.mock(java.util.concurrent.ExecutorService.class); var coordinator = new SessionRenameCoordinator(store, new com.alibaba.qwen.code.managedagent.harness.UnavailableHarnessConnector(), diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/AdmittedTurnRetryTerminalStateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/AdmittedTurnRetryTerminalStateTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/AdmittedTurnRetryTerminalStateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/AdmittedTurnRetryTerminalStateTest.java index 70064ce5b3e..508be53ab9d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/AdmittedTurnRetryTerminalStateTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/AdmittedTurnRetryTerminalStateTest.java @@ -1,18 +1,18 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.daemon.DaemonProtocolException; -import com.alibaba.qwen.code.daemon.HarnessSessionRefusedException; -import com.alibaba.qwen.code.daemon.HostedHarnessGenerationException; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.Attachment; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.SourceEvent; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.SourceStream; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.Attachment; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.SourceEvent; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.SourceStream; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessGenerationFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessProtocolFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessSessionRefusedFailure; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnRecord; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -49,7 +49,7 @@ class AdmittedTurnRetryTerminalStateTest { @ValueSource(ints = {10, 42}) void admittedWorkspaceTurnParksAfterThePostAdmissionRetryBudgetIsSpent( int retryCount) { - DaemonHttpException unavailable = mock(DaemonHttpException.class); + HarnessHttpFailure unavailable = mock(HarnessHttpFailure.class); when(unavailable.getStatusCode()).thenReturn(503); Dispatched dispatched = dispatchTransientFailure(retryCount, new ManagedAgentProperties(), unavailable, "epoch-1"); @@ -69,7 +69,7 @@ class AdmittedTurnRetryTerminalStateTest { @ValueSource(ints = {10, 42}) void aLostAdmissionDoesNotPermitASessionScopedCancel( int retryCount) { - DaemonHttpException unavailable = mock(DaemonHttpException.class); + HarnessHttpFailure unavailable = mock(HarnessHttpFailure.class); when(unavailable.getStatusCode()).thenReturn(503); Dispatched dispatched = dispatchTransientFailure(retryCount, new ManagedAgentProperties(), unavailable); @@ -97,7 +97,7 @@ class AdmittedTurnRetryTerminalStateTest { "hosted_prompt_recovery_required"}) void namedRecoveryRefusalsKeepTheirCodeWhileAwaitingNativeEvidence( String refusalCode) { - DaemonHttpException refusal = mock(DaemonHttpException.class); + HarnessHttpFailure refusal = mock(HarnessHttpFailure.class); when(refusal.getStatusCode()).thenReturn(409); when(refusal.getErrorCode()).thenReturn(refusalCode); Dispatched dispatched = dispatchTransientFailure(5, @@ -153,7 +153,7 @@ class AdmittedTurnRetryTerminalStateTest { "storage-a", ".", "config-a", 1), "yolo", "hosted-workspace-files/1")); when(harness.isWorkspaceFilesAvailable()).thenReturn(true); - DaemonHttpException unavailable = mock(DaemonHttpException.class); + HarnessHttpFailure unavailable = mock(HarnessHttpFailure.class); when(unavailable.getStatusCode()).thenReturn(503); when(harness.recoverManagedRuntime("tenant", "session", false)) .thenThrow(unavailable); @@ -220,7 +220,7 @@ class AdmittedTurnRetryTerminalStateTest { boolean expectFailure) { AgentStateStore store = dispatchTransientFailure(retryCount, new ManagedAgentProperties(), - WorkspaceExecutionStore.unavailable()).store(); + com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).store(); if (expectFailure) { verifyParked(store, "workspace_unavailable"); @@ -239,8 +239,8 @@ class AdmittedTurnRetryTerminalStateTest { @CsvSource({"10, true", "9, false"}) void namedLoadRefusalsHonourThePostAdmissionBudget(int retryCount, boolean expectFailure) { - HarnessSessionRefusedException refusal = mock( - HarnessSessionRefusedException.class); + HarnessSessionRefusedFailure refusal = mock( + HarnessSessionRefusedFailure.class); when(refusal.getCode()).thenReturn("managed_session_open_failed"); AgentStateStore store = dispatchTransientFailure(retryCount, new ManagedAgentProperties(), refusal).store(); @@ -262,7 +262,7 @@ class AdmittedTurnRetryTerminalStateTest { @ValueSource(ints = {10, 42}) void aLeaseBoundedWriterConflictStillRetriesPastThePostAdmissionBudget( int retryCount) { - DaemonHttpException conflict = mock(DaemonHttpException.class); + HarnessHttpFailure conflict = mock(HarnessHttpFailure.class); when(conflict.getStatusCode()).thenReturn(409); when(conflict.getErrorCode()) .thenReturn("managed_session_writer_conflict"); @@ -287,7 +287,7 @@ class AdmittedTurnRetryTerminalStateTest { int failures) { Dispatched dispatched = dispatchTransientFailure(failures, failures, new ManagedAgentProperties(), - mock(HostedHarnessGenerationException.class), "epoch-1"); + mock(HarnessGenerationFailure.class), "epoch-1"); verify(dispatched.store()).scheduleTurnRetry(eq("tenant"), eq("session"), eq("turn"), anyString(), anyLong(), eq(true)); @@ -307,7 +307,7 @@ class AdmittedTurnRetryTerminalStateTest { void aGenerationChurnPastTheCapParksTheAdmittedTurn(int failures) { Dispatched dispatched = dispatchTransientFailure(failures, failures, new ManagedAgentProperties(), - mock(HostedHarnessGenerationException.class), "epoch-1"); + mock(HarnessGenerationFailure.class), "epoch-1"); AgentStateStore store = dispatched.store(); verifyParked(store, "hosted_harness_unavailable_after_admission"); @@ -329,7 +329,7 @@ class AdmittedTurnRetryTerminalStateTest { properties.getDispatch().setMaxPostAdmissionRetries(0); properties.getDispatch().setMaxOperationRetries(0); Dispatched dispatched = dispatchTransientFailure(failures, failures, - properties, mock(HostedHarnessGenerationException.class), + properties, mock(HarnessGenerationFailure.class), "epoch-1"); AgentStateStore store = dispatched.store(); @@ -472,8 +472,8 @@ class AdmittedTurnRetryTerminalStateTest { anyString(), eq("boot-1"))).thenReturn(true); when(store.findTurn("tenant", "session", "turn")) .thenReturn(Optional.of(claimed)); - DaemonProtocolException protocolError = mock( - DaemonProtocolException.class); + HarnessProtocolFailure protocolError = mock( + HarnessProtocolFailure.class); TurnRecord attempted = new TurnRecord("tenant", "session", "turn", promptId, claimed.input(), claimed.payloadDigest(), "RUNNING", true, null, null, "previous-owner", Long.MAX_VALUE, 0, 0, 0, null, null, null, 1, 1, null, 1); @@ -516,7 +516,7 @@ class AdmittedTurnRetryTerminalStateTest { } private static Dispatched dispatchTransientFailure(int retryCount) { - DaemonHttpException unavailable = mock(DaemonHttpException.class); + HarnessHttpFailure unavailable = mock(HarnessHttpFailure.class); when(unavailable.getStatusCode()).thenReturn(503); return dispatchTransientFailure(retryCount, new ManagedAgentProperties(), unavailable); @@ -524,7 +524,7 @@ class AdmittedTurnRetryTerminalStateTest { private static Dispatched dispatchTransientFailure(int retryCount, ManagedAgentProperties properties) { - DaemonHttpException unavailable = mock(DaemonHttpException.class); + HarnessHttpFailure unavailable = mock(HarnessHttpFailure.class); when(unavailable.getStatusCode()).thenReturn(503); return dispatchTransientFailure(retryCount, properties, unavailable); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ChildWorktreeGitVersionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ChildWorktreeGitVersionTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ChildWorktreeGitVersionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ChildWorktreeGitVersionTest.java index 80f7f38380d..8d3991eea1f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ChildWorktreeGitVersionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ChildWorktreeGitVersionTest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.managedagent.workspace.ChildWorktreeGit; +import com.alibaba.qwen.code.managedagent.adapters.workspace.ChildWorktreeGit; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinatorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinatorTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinatorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinatorTest.java index 7e5dc817a00..43c5e40a6e1 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinatorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ContextCompressionCoordinatorTest.java @@ -1,7 +1,8 @@ package com.alibaba.qwen.code.managedagent.session; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; @@ -84,7 +85,7 @@ class ContextCompressionCoordinatorTest { .thenReturn(Optional.empty()); when(fixture.store.markContextCompressionSubmitted(eq(fixture.operation), anyString())).thenReturn(true); when(fixture.store.contextCompressionRequest("tenant", "session", "operation")).thenReturn(Map.of()); - var refusal = mock(com.alibaba.qwen.code.daemon.DaemonHttpException.class); + var refusal = mock(com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure.class); when(refusal.getStatusCode()).thenReturn(409); when(refusal.getErrorCode()).thenReturn("session_context_busy"); when(fixture.harness.runContextCompression(eq("tenant"), eq("session"), any())).thenThrow(refusal); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/CoordinatorTestSupport.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/CoordinatorTestSupport.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/CoordinatorTestSupport.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/CoordinatorTestSupport.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/EventStreams.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/EventStreams.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/EventStreams.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/EventStreams.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinatorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinatorTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinatorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinatorTest.java index 76b53891f92..8a8527193cb 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinatorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/HarnessCoordinatorTest.java @@ -3,30 +3,30 @@ package com.alibaba.qwen.code.managedagent.session; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.daemon.DaemonProtocolException; -import com.alibaba.qwen.code.daemon.DaemonTransportException; -import com.alibaba.qwen.code.daemon.HarnessRuntimeRecovery; -import com.alibaba.qwen.code.daemon.HarnessSessionRefusedException; -import com.alibaba.qwen.code.daemon.HostedHarnessGenerationException; import com.alibaba.qwen.code.daemon.HostedHarnessStreamIdleException; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.Admission; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.Attachment; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.SourceEvent; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.SourceStream; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessDisabledException; +import com.alibaba.qwen.code.managedagent.contracts.harness.HostedHarnessRecoveryDeclinedException; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.Admission; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.Attachment; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.SourceEvent; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.SourceStream; -import com.alibaba.qwen.code.managedagent.harness.HarnessDisabledException; -import com.alibaba.qwen.code.managedagent.harness.HostedHarnessRecoveryDeclinedException; import com.alibaba.qwen.code.managedagent.harness.UnavailableHarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessGenerationFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessProtocolFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRuntimeRecovery; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessSessionRefusedFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessTransportFailure; import com.alibaba.qwen.code.managedagent.model.StoreModels.DispatchTarget; import com.alibaba.qwen.code.managedagent.model.StoreModels.HarnessEvent; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnRecord; import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import org.junit.jupiter.api.Test; @@ -35,7 +35,6 @@ import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.InOrder; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; import java.time.Clock; import java.time.Duration; @@ -442,7 +441,7 @@ class HarnessCoordinatorTest { } // Before submission, the only RuntimeBrokerException that reaches the - // coordinator is WorkspaceExecutionStore.unavailable() (409, + // coordinator is com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable() (409, // workspace_unavailable, not retryable), raised by the connector's // authorization in createOrLoad. Broker lease contention (workspace_busy) // is raised inside the Broker's tool-execution transport and is consumed @@ -450,7 +449,7 @@ class HarnessCoordinatorTest { @ParameterizedTest @ValueSource(ints = {0, 5}) void failsOnWorkspaceAuthorizationRefusalBeforeSubmission(int retryCount) { - RuntimeBrokerException refusal = WorkspaceExecutionStore.unavailable(); + RuntimeBrokerException refusal = com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); AgentStateStore store = dispatchWithCreateOrLoadFailure(refusal, false, retryCount, boundSessionRecord()); @@ -544,7 +543,7 @@ class HarnessCoordinatorTest { @Test void retriesWorkspaceAuthorizationRefusalAfterARecordedSubmission() { AgentStateStore store = dispatchWithCreateOrLoadFailure( - WorkspaceExecutionStore.unavailable(), true, 5, + com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(), true, 5, boundSessionRecord()); verify(store).scheduleTurnRetry(eq("tenant"), eq("session"), eq("turn"), anyString(), anyLong(), eq(false)); @@ -569,14 +568,14 @@ class HarnessCoordinatorTest { anyString(), anyString(), anyString(), anyString()); } - // Pins the 409 cell of the DaemonHttpException arm: a conflict the Harness + // Pins the 409 cell of the HarnessHttpFailure arm: a conflict the Harness // itself reports (hosted_turn_active, hosted_prompt_conflict, // hosted_event_epoch_mismatch, ...) is transient, so it is retried rather // than failed. A create-time 409 never reaches here: the connector answers // it by loading the existing authority. @Test void retriesAConflictReportedByTheHarness() { - DaemonHttpException conflict = mock(DaemonHttpException.class); + HarnessHttpFailure conflict = mock(HarnessHttpFailure.class); when(conflict.getStatusCode()).thenReturn(409); AgentStateStore store = dispatchWithCreateOrLoadFailure(conflict, false, 0, @@ -591,7 +590,7 @@ class HarnessCoordinatorTest { // that the original native input cannot continue executing. @Test void parksAPermanentHarnessRejectionAfterSubmission() { - DaemonHttpException rejected = mock(DaemonHttpException.class); + HarnessHttpFailure rejected = mock(HarnessHttpFailure.class); when(rejected.getStatusCode()).thenReturn(400); AgentStateStore store = dispatchWithCreateOrLoadFailure(rejected, true, 5, @@ -636,7 +635,7 @@ class HarnessCoordinatorTest { // submission may have been admitted is retried, not failed. @Test void retriesATransientHarnessFailureAfterSubmission() { - DaemonHttpException unavailable = mock(DaemonHttpException.class); + HarnessHttpFailure unavailable = mock(HarnessHttpFailure.class); when(unavailable.getStatusCode()).thenReturn(503); AgentStateStore store = dispatchWithCreateOrLoadFailure(unavailable, true, 5, @@ -652,7 +651,7 @@ class HarnessCoordinatorTest { "409,harness_placement_changed", "409,harness_placement_quiescing", "409,harness_placement_released"}) void poolCapacityAndOwnershipWaitsOutliveBothAdmissionBudgets(int status, String code) { for (boolean submitted : List.of(false, true)) { - var failure = new ManagedAgentException(HttpStatus.valueOf(status), code, "Waiting for a Harness owner"); + var failure = new ManagedAgentException(status, code, "Waiting for a Harness owner"); AgentStateStore store = dispatchWithCreateOrLoadFailure(failure, submitted, 100, unboundSessionRecord()); verify(store).scheduleTurnRetry(eq("tenant"), eq("session"), eq("turn"), anyString(), anyLong(), eq(true)); @@ -664,7 +663,7 @@ class HarnessCoordinatorTest { @Test void hostedDrainCutoffOutlivesBothAdmissionBudgets() { for (boolean submitted : List.of(false, true)) { - var failure = mock(DaemonHttpException.class); + var failure = mock(HarnessHttpFailure.class); when(failure.getStatusCode()).thenReturn(409); when(failure.getErrorCode()).thenReturn("hosted_harness_quiescing"); AgentStateStore store = dispatchWithCreateOrLoadFailure(failure, submitted, 100, unboundSessionRecord()); @@ -679,7 +678,7 @@ class HarnessCoordinatorTest { @CsvSource({"503,other_unavailable", "403,writer_credential_invalid", "409,harness_session_scope_ambiguous", "409,harness_placement_not_admitted", "409,harness_placement_epoch_exhausted", "400,harness_pool_unavailable"}) void poolConfigurationAndUnclassifiedFailuresStillMeetTheAdmissionBudget(int status, String code) { - var failure = new ManagedAgentException(HttpStatus.valueOf(status), code, "Cannot admit this Harness owner"); + var failure = new ManagedAgentException(status, code, "Cannot admit this Harness owner"); AgentStateStore store = dispatchWithCreateOrLoadFailure(failure, false, 100, unboundSessionRecord()); verify(store).failTurn(eq("tenant"), eq("session"), eq("turn"), anyString(), eq("hosted_harness_unavailable"), anyString()); @@ -725,7 +724,7 @@ class HarnessCoordinatorTest { // one the wait names no predecessor's lease and meets the budget. @Test void unboundSessionMeetsRetryBudgetOnLeaseBounded409() { - DaemonHttpException conflict = mock(DaemonHttpException.class); + HarnessHttpFailure conflict = mock(HarnessHttpFailure.class); when(conflict.getStatusCode()).thenReturn(409); when(conflict.getErrorCode()) .thenReturn("hosted_turn_recovery_required"); @@ -745,8 +744,8 @@ class HarnessCoordinatorTest { // the pre-admission budget runs out. @Test void retriesANamedLoadRefusalBeforeTheBudgetRunsOut() { - HarnessSessionRefusedException refusal = mock( - HarnessSessionRefusedException.class); + HarnessSessionRefusedFailure refusal = mock( + HarnessSessionRefusedFailure.class); when(refusal.getCode()).thenReturn("managed_session_open_failed"); AgentStateStore store = dispatchWithCreateOrLoadFailure(refusal, false, 0, @@ -759,8 +758,8 @@ class HarnessCoordinatorTest { @Test void recordsTheLoadRefusalCodeWhenRetriesRunOut() { - HarnessSessionRefusedException refusal = mock( - HarnessSessionRefusedException.class); + HarnessSessionRefusedFailure refusal = mock( + HarnessSessionRefusedFailure.class); when(refusal.getCode()).thenReturn("managed_session_open_failed"); AgentStateStore store = dispatchWithCreateOrLoadFailure(refusal, false, 5, @@ -787,7 +786,7 @@ class HarnessCoordinatorTest { when(store.requireSession("tenant", "session")).thenReturn( new SessionRecord("tenant", "session", "qwen-code", null, "ACTIVE", "boot-old", null, 0, 0, 1, 1, null, 1)); - DaemonHttpException conflict = mock(DaemonHttpException.class); + HarnessHttpFailure conflict = mock(HarnessHttpFailure.class); when(conflict.getStatusCode()).thenReturn(409); when(conflict.getErrorCode()) .thenReturn("hosted_tool_profile_conflict"); @@ -826,7 +825,7 @@ class HarnessCoordinatorTest { when(store.requireSession("tenant", "session")).thenReturn( new SessionRecord("tenant", "session", "qwen-code", null, "ACTIVE", "boot-old", null, 0, 0, 1, 1, null, 1)); - DaemonHttpException attached = mock(DaemonHttpException.class); + HarnessHttpFailure attached = mock(HarnessHttpFailure.class); when(attached.getStatusCode()).thenReturn(409); when(attached.getErrorCode()) .thenReturn("hosted_session_already_attached"); @@ -1182,7 +1181,7 @@ class HarnessCoordinatorTest { when(store.requireSession("tenant", "session")).thenReturn( new SessionRecord("tenant", "session", "qwen-code", null, "ACTIVE", "boot-old", null, 0, 0, 1, 1, null, 1)); - DaemonHttpException refused = mock(DaemonHttpException.class); + HarnessHttpFailure refused = mock(HarnessHttpFailure.class); when(refused.getStatusCode()).thenReturn(409); when(refused.getErrorCode()) .thenReturn("hosted_turn_recovery_required"); @@ -1235,7 +1234,7 @@ class HarnessCoordinatorTest { "ACTIVE", "boot-old", null, 0, 0, 1, 1, null, 1)); when(store.findTurn("tenant", "session", "turn")) .thenReturn(Optional.of(claimed)); - DaemonHttpException refused = mock(DaemonHttpException.class); + HarnessHttpFailure refused = mock(HarnessHttpFailure.class); when(refused.getStatusCode()).thenReturn(409); when(refused.getErrorCode()) .thenReturn("hosted_turn_recovery_required"); @@ -1337,7 +1336,7 @@ class HarnessCoordinatorTest { when(store.requireSession("tenant", "session")).thenReturn( new SessionRecord("tenant", "session", "qwen-code", null, "ACTIVE", "boot-old", null, 0, 0, 1, 1, null, 1)); - DaemonHttpException refused = mock(DaemonHttpException.class); + HarnessHttpFailure refused = mock(HarnessHttpFailure.class); when(refused.getStatusCode()).thenReturn(409); when(refused.getErrorCode()) .thenReturn("hosted_turn_recovery_required"); @@ -1488,7 +1487,7 @@ class HarnessCoordinatorTest { when(store.markTurnRecoveryBlocked(anyString(), anyString(), anyString(), anyString(), anyString(), anyString())).thenReturn(false); } else if ("transport-error".equals(mode)) { - when(stream.recoveryBlocked(1)).thenThrow(mock(DaemonTransportException.class)); + when(stream.recoveryBlocked(1)).thenThrow(mock(HarnessTransportFailure.class)); } when(stream.next()).thenReturn(new SourceEvent(2L, "turn_complete", Map.of("stopReason", "end_turn"), "prompt", Map.of()), null); @@ -1543,7 +1542,7 @@ class HarnessCoordinatorTest { when(store.requireSession("tenant", "session")).thenReturn( new SessionRecord("tenant", "session", "qwen-code", null, "ACTIVE", "boot-old", null, 0, 0, 1, 1, null, 1)); - DaemonHttpException conflict = mock(DaemonHttpException.class); + HarnessHttpFailure conflict = mock(HarnessHttpFailure.class); when(conflict.getStatusCode()).thenReturn(409); when(conflict.getErrorCode()) .thenReturn("managed_session_writer_conflict"); @@ -1582,7 +1581,7 @@ class HarnessCoordinatorTest { when(store.requireSession("tenant", "session")).thenReturn( new SessionRecord("tenant", "session", "qwen-code", null, "ACTIVE", "boot-old", null, 0, 0, 1, 1, null, 1)); - DaemonHttpException durable = mock(DaemonHttpException.class); + HarnessHttpFailure durable = mock(HarnessHttpFailure.class); when(durable.getStatusCode()).thenReturn(409); when(durable.getErrorCode()) .thenReturn("hosted_turn_recovery_required"); @@ -1628,7 +1627,7 @@ class HarnessCoordinatorTest { anyString(), anyString())).thenReturn(true); when(store.findTurn("tenant", "session", "turn")) .thenReturn(Optional.of(claimed)); - DaemonHttpException window = mock(DaemonHttpException.class); + HarnessHttpFailure window = mock(HarnessHttpFailure.class); when(window.getStatusCode()).thenReturn(409); when(window.getErrorCode()) .thenReturn("hosted_turn_recovery_in_progress"); @@ -1666,7 +1665,7 @@ class HarnessCoordinatorTest { when(store.requireSession("tenant", "session")).thenReturn( new SessionRecord("tenant", "session", "qwen-code", null, "ACTIVE", "boot-old", null, 0, 0, 1, 1, null, 1)); - DaemonHttpException badGateway = mock(DaemonHttpException.class); + HarnessHttpFailure badGateway = mock(HarnessHttpFailure.class); when(badGateway.getStatusCode()).thenReturn(503); when(badGateway.getErrorCode()).thenReturn(null); when(harness.recoverManagedRuntime("tenant", "session", false)) @@ -1962,7 +1961,7 @@ class HarnessCoordinatorTest { when(harness.recoverManagedRuntime("tenant", "session", true)) .thenReturn(new Attachment("boot-new", null, 5L, "epoch-new")); - DaemonHttpException noLiveTurn = mock(DaemonHttpException.class); + HarnessHttpFailure noLiveTurn = mock(HarnessHttpFailure.class); when(noLiveTurn.getStatusCode()).thenReturn(409); when(noLiveTurn.getErrorCode()) .thenReturn("hosted_turn_recovery_required"); @@ -2014,7 +2013,7 @@ class HarnessCoordinatorTest { "ACTIVE", "boot-new", null, 0, 0, 1, 1, null, 1)); when(store.bindHarness(eq("tenant"), eq("session"), eq("turn"), anyString(), eq("boot-new"))).thenReturn(true); - DaemonHttpException refused = mock(DaemonHttpException.class); + HarnessHttpFailure refused = mock(HarnessHttpFailure.class); when(refused.getStatusCode()).thenReturn(409); when(refused.getErrorCode()) .thenReturn("hosted_turn_recovery_required"); @@ -2059,7 +2058,7 @@ class HarnessCoordinatorTest { "ACTIVE", "boot-new", null, 0, 0, 1, 1, null, 1)); when(store.bindHarness(eq("tenant"), eq("session"), eq("turn"), anyString(), eq("boot-new"))).thenReturn(true); - DaemonHttpException refused = mock(DaemonHttpException.class); + HarnessHttpFailure refused = mock(HarnessHttpFailure.class); when(refused.getStatusCode()).thenReturn(409); when(refused.getErrorCode()) .thenReturn("hosted_turn_recovery_required"); @@ -2115,7 +2114,7 @@ class HarnessCoordinatorTest { "ACTIVE", "boot-new", null, 0, 0, 1, 1, null, 1)); when(store.bindHarness(eq("tenant"), eq("session"), eq("turn"), anyString(), eq("boot-new"))).thenReturn(true); - DaemonHttpException refused = mock(DaemonHttpException.class); + HarnessHttpFailure refused = mock(HarnessHttpFailure.class); when(refused.getStatusCode()).thenReturn(409); when(refused.getErrorCode()) .thenReturn("hosted_turn_recovery_required"); @@ -2164,7 +2163,7 @@ class HarnessCoordinatorTest { "ACTIVE", "boot-new", null, 0, 0, 1, 1, null, 1)); when(store.bindHarness(eq("tenant"), eq("session"), eq("turn"), anyString(), eq("boot-new"))).thenReturn(true); - DaemonHttpException refused = mock(DaemonHttpException.class); + HarnessHttpFailure refused = mock(HarnessHttpFailure.class); when(refused.getStatusCode()).thenReturn(409); when(refused.getErrorCode()) .thenReturn("hosted_prompt_recovery_required"); @@ -2218,7 +2217,7 @@ class HarnessCoordinatorTest { anyString(), eq("boot-new"))).thenReturn(false, true); when(store.withdrawSubmissionAttempted(eq("tenant"), eq("session"), eq("turn"), anyString())).thenReturn(true); - DaemonHttpException duplicate = mock(DaemonHttpException.class); + HarnessHttpFailure duplicate = mock(HarnessHttpFailure.class); when(duplicate.getStatusCode()).thenReturn(409); when(duplicate.getErrorCode()) .thenReturn("hosted_prompt_recovery_required"); @@ -2287,7 +2286,7 @@ class HarnessCoordinatorTest { anyString(), eq("boot-new"))).thenReturn(true); when(store.findTurn("tenant", "session", "turn")) .thenReturn(Optional.of(claimed)); - DaemonHttpException duplicate = mock(DaemonHttpException.class); + HarnessHttpFailure duplicate = mock(HarnessHttpFailure.class); when(duplicate.getStatusCode()).thenReturn(409); when(duplicate.getErrorCode()) .thenReturn("hosted_prompt_recovery_required"); @@ -2341,7 +2340,7 @@ class HarnessCoordinatorTest { anyString(), eq("boot-new"))).thenReturn(true); when(store.findTurn("tenant", "session", "turn")) .thenReturn(Optional.of(claimed)); - DaemonHttpException busy = mock(DaemonHttpException.class); + HarnessHttpFailure busy = mock(HarnessHttpFailure.class); when(busy.getStatusCode()).thenReturn(409); when(busy.getErrorCode()) .thenReturn("hosted_turn_recovery_required"); @@ -2435,7 +2434,7 @@ class HarnessCoordinatorTest { new SessionRecord("tenant", "session", "qwen-code", null, "ACTIVE", null, null, 0, 0, 1, 1, null, 1)); when(harness.createOrLoad("tenant", "session", false)).thenThrow( - mock(HostedHarnessGenerationException.class)); + mock(HarnessGenerationFailure.class)); HarnessCoordinator coordinator = new HarnessCoordinator(store, harness, new HarnessEventProjector(), runtimeWarmer, directExecutor(), Clock.systemUTC(), @@ -2561,8 +2560,8 @@ class HarnessCoordinatorTest { when(store.requireSession("tenant", "session")).thenReturn( new SessionRecord("tenant", "session", "qwen-code", null, "ACTIVE", "boot-old", null, 0, 0, 1, 1, null, 1)); - HostedHarnessGenerationException mismatch = - mock(HostedHarnessGenerationException.class); + HarnessGenerationFailure mismatch = + mock(HarnessGenerationFailure.class); when(harness.recoverManagedRuntime("tenant", "session", false)) .thenThrow(mismatch); HarnessCoordinator coordinator = new HarnessCoordinator(store, harness, @@ -2620,7 +2619,7 @@ class HarnessCoordinatorTest { new SessionRecord("tenant", "session", "qwen-code", null, "ACTIVE", "boot-old", null, 0, 0, 1, 1, null, 1)); when(harness.recoverManagedRuntime("tenant", "session", false)) - .thenThrow(mock(HostedHarnessGenerationException.class)); + .thenThrow(mock(HarnessGenerationFailure.class)); HarnessCoordinator coordinator = new HarnessCoordinator(store, harness, new HarnessEventProjector(), runtimeWarmer, directExecutor(), Clock.systemUTC(), properties); @@ -2712,7 +2711,7 @@ class HarnessCoordinatorTest { if (admitted) { when(store.bindHarness(eq("tenant"), eq("session"), eq("legacy-turn"), anyString(), eq("boot-old"))).thenReturn(true); - DaemonHttpException parked = mock(DaemonHttpException.class); + HarnessHttpFailure parked = mock(HarnessHttpFailure.class); when(parked.getStatusCode()).thenReturn(409); when(parked.getErrorCode()).thenReturn("hosted_turn_recovery_required"); doThrow(parked).when(harness).cancel("tenant", "session"); @@ -2821,7 +2820,7 @@ class HarnessCoordinatorTest { } else if (!missingAdmission) { when(store.bindHarness(eq("tenant"), eq("session"), eq("turn"), anyString(), eq("boot-old"))).thenReturn(true); - DaemonHttpException parked = mock(DaemonHttpException.class); + HarnessHttpFailure parked = mock(HarnessHttpFailure.class); when(parked.getStatusCode()).thenReturn(409); when(parked.getErrorCode()).thenReturn("hosted_turn_recovery_required"); doThrow(parked).when(harness).cancelOriginalTurn("tenant", "session", "prompt"); @@ -2948,7 +2947,7 @@ class HarnessCoordinatorTest { when(harness.recoverManagedRuntime("tenant", "session", false)) .thenThrow(new HostedHarnessRecoveryDeclinedException( "model_start")); - DaemonHttpException parked = mock(DaemonHttpException.class); + HarnessHttpFailure parked = mock(HarnessHttpFailure.class); when(parked.getStatusCode()).thenReturn(409); when(parked.getErrorCode()) .thenReturn("hosted_turn_recovery_required"); @@ -3021,7 +3020,7 @@ class HarnessCoordinatorTest { .thenReturn(new Attachment("boot-new", blocked, 4L, "epoch-new")); when(store.bindHarness(eq("tenant"), eq("session"), eq("turn"), anyString(), eq("boot-old"))).thenReturn(true); - DaemonHttpException parked = mock(DaemonHttpException.class); + HarnessHttpFailure parked = mock(HarnessHttpFailure.class); when(parked.getStatusCode()).thenReturn(409); when(parked.getErrorCode()).thenReturn("hosted_turn_recovery_required"); doThrow(parked).when(harness).cancelOriginalTurn("tenant", "session", "original-prompt"); @@ -3647,7 +3646,7 @@ class HarnessCoordinatorTest { assertThat(terminalQueued.await(5, TimeUnit.SECONDS)).isTrue(); } if ("transport-error".equals(mode)) { - throw mock(DaemonTransportException.class); + throw mock(HarnessTransportFailure.class); } return true; }); @@ -4176,7 +4175,7 @@ class HarnessCoordinatorTest { .thenReturn(new Attachment("boot-new", null, null, null)); when(harness.submit(eq(tenantId), eq(sessionId), eq(promptId), any(), anyString())).thenThrow( - workspaceRefusal ? WorkspaceExecutionStore.unavailable() + workspaceRefusal ? com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable() : new IllegalStateException("response lost")); when(runtimeWarmer.isEnabled()).thenReturn(false); @@ -4727,7 +4726,7 @@ class HarnessCoordinatorTest { when(store.bindHarness(eq("tenant"), eq("session"), eq("turn"), anyString(), eq("boot-old"))).thenReturn(true); when(harness.recoverManagedRuntime("tenant", "session", false)) - .thenThrow(mock(DaemonProtocolException.class)); + .thenThrow(mock(HarnessProtocolFailure.class)); HarnessCoordinator coordinator = new HarnessCoordinator(store, harness, new HarnessEventProjector(), runtimeWarmer, directExecutor(), Clock.systemUTC(), diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinatorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinatorTest.java similarity index 88% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinatorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinatorTest.java index 5a3ecd0ae2e..1e3d06a27e4 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinatorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/HostedCleanupRetryCoordinatorTest.java @@ -1,20 +1,19 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.daemon.DaemonTransportException; -import com.alibaba.qwen.code.daemon.HostedHarnessGenerationException; -import com.alibaba.qwen.code.daemon.MutationOutcomeUnknownException; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HostedHarnessRecoveryDeclinedException; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.harness.HostedHarnessRecoveryDeclinedException; -import com.alibaba.qwen.code.managedagent.persistence.HostedCleanupDebtStore; -import com.alibaba.qwen.code.managedagent.persistence.HostedCleanupDebtStore.CleanupDebt; -import com.alibaba.qwen.code.managedagent.persistence.HostedCleanupDebtStore.ScanPage; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessGenerationFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessMutationUnknownFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessTransportFailure; +import com.alibaba.qwen.code.managedagent.repository.HostedCleanupDebtStore; +import com.alibaba.qwen.code.managedagent.repository.HostedCleanupDebtStore.CleanupDebt; +import com.alibaba.qwen.code.managedagent.repository.HostedCleanupDebtStore.ScanPage; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.springframework.http.HttpStatus; import java.time.Clock; import java.time.Instant; @@ -92,7 +91,7 @@ class HostedCleanupRetryCoordinatorTest { when(store.findOwed(eq(20), anyLong(), anyString())).thenReturn(page(debt(SESSION, OWNER, PROMPT))); when(harness.isAvailable()).thenReturn(true); when(harness.recheckRecoveryCleanup(TENANT, SESSION, PROMPT)) - .thenThrow(mock(DaemonTransportException.class)) + .thenThrow(mock(HarnessTransportFailure.class)) .thenReturn(true); coordinator.retryOwedCleanup(); @@ -126,19 +125,19 @@ class HostedCleanupRetryCoordinatorTest { static Stream attachmentCases() { return Stream.of( - Arguments.of("transport", mock(DaemonTransportException.class), true), - Arguments.of("generation", mock(HostedHarnessGenerationException.class), true), - Arguments.of("unknown-outcome", mock(MutationOutcomeUnknownException.class), true), + Arguments.of("transport", mock(HarnessTransportFailure.class), true), + Arguments.of("generation", mock(HarnessGenerationFailure.class), true), + Arguments.of("unknown-outcome", mock(HarnessMutationUnknownFailure.class), true), Arguments.of("http-404", httpStatus(404), true), Arguments.of("http-503", httpStatus(503), true), Arguments.of("http-409", httpStatus(409), false), Arguments.of("declined", new HostedHarnessRecoveryDeclinedException("await_action"), false), Arguments.of("pool-unavailable", new ManagedAgentException( - HttpStatus.SERVICE_UNAVAILABLE, "harness_pool_unavailable", "no owner"), false)); + 503, "harness_pool_unavailable", "no owner"), false)); } - private static DaemonHttpException httpStatus(int status) { - DaemonHttpException error = mock(DaemonHttpException.class); + private static HarnessHttpFailure httpStatus(int status) { + HarnessHttpFailure error = mock(HarnessHttpFailure.class); when(error.getStatusCode()).thenReturn(status); return error; } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/Issue13181QueryBudgetTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/Issue13181QueryBudgetTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/Issue13181QueryBudgetTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/Issue13181QueryBudgetTest.java index 109aa8b6d89..530d7c6622a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/Issue13181QueryBudgetTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/Issue13181QueryBudgetTest.java @@ -1,21 +1,21 @@ package com.alibaba.qwen.code.managedagent.session; import com.alibaba.qwen.code.managedagent.PublicationJournalFixture; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationVerifier; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -454,15 +454,15 @@ class Issue13181QueryBudgetTest { bound, boundIds.get(3)); String closeSession = boundIds.get(5); String closeId = "op_" + UUID.randomUUID(); - String closeScope = ManagedSessionStore.sessionScopeKey(bound, closeSession); + String closeScope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(bound, closeSession); fixture.jdbc.update("INSERT INTO ma_operation (operation_key, scope_key, session_scope_key, target_kind," + " target_key, request_json, phase, tenant_id, session_id, operation_id, operation_kind, actor_digest," + " idempotency_key, request_digest, state, admission_stage, delivery_state, session_status_before," + " receipt_id, available_at, gmt_create, gmt_modified, completed_at)" + " VALUES (?, ?, ?, 'SESSION', ?, '{}', 'COMPLETED', ?, ?, ?, 'CLOSE', '', 'close-key'," + " 'digest', 'COMPLETED', 'JAVA_DURABLE', 'CONFIRMED', 'CLOSED', 'rcpt', 0, 0, 0, 0)", - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(closeScope, "CLOSE", closeId), - closeScope, closeScope, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(closeSession), + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(closeScope, "CLOSE", closeId), + closeScope, closeScope, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(closeSession), bound, closeSession, closeId); fixture.ledger.reset(); var boundPublic = fixture.service.listPublicSessions(bound, "actor", @@ -1536,11 +1536,11 @@ class Issue13181QueryBudgetTest { properties.setModelCatalog(List.of(option)); when(harness.supportsModelSelection()).thenReturn(true); } - store = new ManagedAgentStore(jdbc, JSON, clock, hub::publish, - registry, properties); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, JSON, clock, hub::publish, + registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); service = new ManagedAgentService(store, new RequestDigests(), mock(HarnessCoordinator.class), - harness, registry); + harness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); } List createCwdSessions(String tenant, int count) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentReplayOutageTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentReplayOutageTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentReplayOutageTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentReplayOutageTest.java index a921f1015c6..4e4882cba24 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentReplayOutageTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentReplayOutageTest.java @@ -40,17 +40,17 @@ class ManagedAgentReplayOutageTest { + ";MODE=MySQL;DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE" + ";LOCK_TIMEOUT=10000"); ManagedSchema.initialize(source); - ManagedAgentStore store = new ManagedAgentStore( + ManagedAgentStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore( new ManagedSchemaJdbcTemplate(source), new ObjectMapper(), Clock.systemUTC(), events -> {}, mock(ManagedWorkspaceRegistry.class), - new ManagedAgentProperties()); + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); HarnessCoordinator coordinator = mock(HarnessCoordinator.class); UnavailableHarnessConnector harness = new UnavailableHarnessConnector(); RequestDigests digests = new RequestDigests(); ManagedAgentService service = new ManagedAgentService(store, digests, - coordinator, harness, mock(ManagedWorkspaceRegistry.class)); + coordinator, harness, mock(ManagedWorkspaceRegistry.class), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); String tenant = "tenant-replay-outage"; List> input = diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceChildWorkspaceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceChildWorkspaceTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceChildWorkspaceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceChildWorkspaceTest.java index d58d68cf9eb..cebb5408b6d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceChildWorkspaceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceChildWorkspaceTest.java @@ -1,7 +1,8 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; @@ -49,7 +50,7 @@ class ManagedAgentServiceChildWorkspaceTest { when(store.insertChildSessionCommand(anyString(), anyString(), anyString(), anyString(), anyString(), anyList(), any(), any(StoreModels.SessionLineage.class), anyString())) .thenReturn(new StoreModels.Admission("isolated-child", null, false, true)); - service = new ManagedAgentService(store, new RequestDigests(), null, harness, null); + service = new ManagedAgentService(store, new RequestDigests(), null, harness, null, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); } @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorkflowChildTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorkflowChildTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorkflowChildTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorkflowChildTest.java index 988828cba26..09e274c72e6 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorkflowChildTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorkflowChildTest.java @@ -1,7 +1,8 @@ package com.alibaba.qwen.code.managedagent.session; import com.alibaba.qwen.code.daemon.SubmitHarnessTurn; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.ApiModels; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels; @@ -53,7 +54,7 @@ class ManagedAgentServiceWorkflowChildTest { when(store.insertChildSessionCommand(anyString(), anyString(), anyString(), anyString(), anyString(), anyList(), any(), any(StoreModels.SessionLineage.class))) .thenReturn(new StoreModels.Admission("workflow-child", null, false, true)); - service = new ManagedAgentService(store, new RequestDigests(), null, harness, null); + service = new ManagedAgentService(store, new RequestDigests(), null, harness, null, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); } @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorktreeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorktreeTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorktreeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorktreeTest.java index c9f426af1fe..d56d033b0db 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorktreeTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentServiceWorktreeTest.java @@ -1,13 +1,14 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.Admission; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore; import com.alibaba.qwen.code.managedagent.workspace.SessionWorktreeService; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -36,7 +37,7 @@ class ManagedAgentServiceWorktreeTest { private final WorkspaceSelection selection = new WorkspaceSelection("workspace", "."); private final SessionWorktreeService.Request request = new SessionWorktreeService.Request("main"); private final ManagedAgentService service = new ManagedAgentService(store, new RequestDigests(), - coordinator, harness, mock(ManagedWorkspaceRegistry.class)); + coordinator, harness, mock(ManagedWorkspaceRegistry.class), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); @ParameterizedTest @ValueSource(booleans = {false, true}) diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentSessionCapabilitiesTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentSessionCapabilitiesTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentSessionCapabilitiesTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentSessionCapabilitiesTest.java index a1ecfac959d..3b43a4a0a3a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentSessionCapabilitiesTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentSessionCapabilitiesTest.java @@ -1,6 +1,9 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.model.ApiModels.SessionRecovery; import com.alibaba.qwen.code.managedagent.model.ApiModels.SessionRecoveryActions; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellLifecycleOperation; @@ -9,8 +12,6 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionLineage; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionPage; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceToolProfiles; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; @@ -42,7 +43,7 @@ class ManagedAgentSessionCapabilitiesTest { var harness = mock(HarnessConnector.class); var warmer = mock(RuntimeWarmer.class); var service = new ManagedAgentService(store, new RequestDigests(), - mock(HarnessCoordinator.class), harness, workspaces); + mock(HarnessCoordinator.class), harness, workspaces, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); service.setRuntimeWarmer(warmer); var workspace = new ContextBinding("tenant", "workspace", 1, "storage", ".", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1); @@ -88,7 +89,7 @@ class ManagedAgentSessionCapabilitiesTest { void recoveryProjectionKeepsAnUnsettledLifecycleOperationOnSingleSessionReads() { var store = mock(AgentStateStore.class); var service = new ManagedAgentService(store, new RequestDigests(), - mock(HarnessCoordinator.class), mock(HarnessConnector.class), mock(ManagedWorkspaceRegistry.class)); + mock(HarnessCoordinator.class), mock(HarnessConnector.class), mock(ManagedWorkspaceRegistry.class), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); var session = new SessionRecord("tenant", "session", "qwen-code", "title", "CLOSING", null, null, 0, 0, 1, 1, null, 1); var operation = new WebShellLifecycleOperation("op-close", "close", "running", null); @@ -116,7 +117,7 @@ class ManagedAgentSessionCapabilitiesTest { var harness = mock(HarnessConnector.class); var warmer = mock(RuntimeWarmer.class); var service = new ManagedAgentService(store, new RequestDigests(), - mock(HarnessCoordinator.class), harness, workspaces); + mock(HarnessCoordinator.class), harness, workspaces, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); service.setRuntimeWarmer(warmer); var workspace = new ContextBinding("tenant", "workspace", 1, "storage", ".", WorkspaceExecutionProfile.CONTEXT_CONFIG_REF, 1); @@ -170,7 +171,7 @@ class ManagedAgentSessionCapabilitiesTest { var harness = mock(HarnessConnector.class); var warmer = mock(RuntimeWarmer.class); var service = new ManagedAgentService(store, new RequestDigests(), - mock(HarnessCoordinator.class), harness, workspaces); + mock(HarnessCoordinator.class), harness, workspaces, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); service.setRuntimeWarmer(warmer); when(store.workspaceFilesEnabled()).thenReturn(true); when(warmer.supportsWorkspaceClose()).thenReturn(globalSupport); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentWorkspaceReplayTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentWorkspaceReplayTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentWorkspaceReplayTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentWorkspaceReplayTest.java index e53ae65f4f2..1f0437ee8f6 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentWorkspaceReplayTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedAgentWorkspaceReplayTest.java @@ -1,8 +1,9 @@ package com.alibaba.qwen.code.managedagent.session; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.Attachment; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.Attachment; import com.alibaba.qwen.code.managedagent.harness.UnavailableHarnessConnector; import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; import com.alibaba.qwen.code.managedagent.model.RequestDigests; @@ -11,13 +12,11 @@ import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnRecord; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedArtifactReader; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; import org.assertj.core.api.ThrowableAssert; import org.h2.jdbcx.JdbcDataSource; @@ -123,7 +122,7 @@ class ManagedAgentWorkspaceReplayTest { }; coordinator = mock(HarnessCoordinator.class); return new ManagedAgentService(store, new RequestDigests(), - coordinator, harness, registry); + coordinator, harness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); } private String boundSession(String tenant) { @@ -163,8 +162,8 @@ class ManagedAgentWorkspaceReplayTest { registry = new ManagedWorkspaceRegistry(jdbc); properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - store = new ManagedAgentStore(jdbc, new ObjectMapper(), - Clock.systemUTC(), events -> {}, registry, properties); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), + Clock.systemUTC(), events -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } // The recorded submit must answer through a Workspace-files outage, like @@ -236,8 +235,8 @@ class ManagedAgentWorkspaceReplayTest { // A non-yolo approval mode keeps the actions capability live before // the delete; the store snapshots it at construction. properties.getHarness().setApprovalMode("default"); - store = new ManagedAgentStore(jdbc, new ObjectMapper(), - Clock.systemUTC(), events -> {}, registry, properties); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), + Clock.systemUTC(), events -> {}, registry, properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); String tenant = "tenant-" + UUID.randomUUID(); String sessionId = boundSession(tenant); ManagedAgentService service = service(new AtomicBoolean(true)); @@ -385,7 +384,7 @@ class ManagedAgentWorkspaceReplayTest { transaction.executeWithoutResult(status -> assertThatThrownBy(call) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { assertThat(error.getStatus()) - .isEqualTo(HttpStatus.NOT_FOUND); + .isEqualTo(HttpStatus.NOT_FOUND.value()); assertThat(error.getCode()).isEqualTo("session_not_found"); })); } @@ -880,7 +879,7 @@ class ManagedAgentWorkspaceReplayTest { // then the Session row as a tombstone. private void recordDelete(String tenant, String sessionId, String statusBefore) { - String scope = ManagedSessionStore.sessionScopeKey(tenant, sessionId); + String scope = com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey(tenant, sessionId); String target = ManagedSchemaKeys.key(sessionId); jdbc.update("INSERT INTO ma_operation (operation_key, scope_key," + " session_scope_key, operation_id, operation_kind," diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedEventReplayTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedEventReplayTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedEventReplayTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedEventReplayTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamServiceTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamServiceTest.java index bf3908c2cdd..9df67588e0a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedEventStreamServiceTest.java @@ -1,6 +1,7 @@ package com.alibaba.qwen.code.managedagent.session; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.ApiModels.WebShellEvent; import com.alibaba.qwen.code.managedagent.model.StoreModels.EventRecord; @@ -11,7 +12,6 @@ import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import org.springframework.http.HttpStatus; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.io.IOException; @@ -41,10 +41,10 @@ class ManagedEventStreamServiceTest { void rejectsNegativeReconciliationCursorBeforeReadingEvents() { AgentStateStore store = mock(AgentStateStore.class); ManagedAgentService agentService = new ManagedAgentService(store, - null, null, null, mock(ManagedWorkspaceRegistry.class)); + null, null, null, mock(ManagedWorkspaceRegistry.class), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); assertThatThrownBy(() -> agentService.streamEvents(SESSION, -1)) .isInstanceOfSatisfying(ManagedAgentException.class, error -> { - assertThat(error.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThat(error.getStatus()).isEqualTo(400); assertThat(error.getCode()).isEqualTo("invalid_event_cursor"); }); verifyNoInteractions(store); @@ -58,7 +58,7 @@ class ManagedEventStreamServiceTest { AgentStateStore store = mock(AgentStateStore.class); ManagedWorkspaceRegistry registry = mock(ManagedWorkspaceRegistry.class); ManagedAgentService agentService = new ManagedAgentService(store, - null, null, null, registry); + null, null, null, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); SessionRecord session = new SessionRecord("tenant", "session", "qwen-code", null, null, "ACTIVE", null, null, 0, 2, 0, 1, 1, null, 1, new ContextBinding("tenant", "ws-a", 1, "storage-a", ".", "config-a", 1), "yolo", @@ -129,7 +129,7 @@ class ManagedEventStreamServiceTest { AgentStateStore store = mock(AgentStateStore.class); ManagedWorkspaceRegistry registry = mock(ManagedWorkspaceRegistry.class); ManagedAgentService agentService = new ManagedAgentService(store, - null, null, null, registry); + null, null, null, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); SessionRecord session = new SessionRecord("tenant", "session", "qwen-code", null, null, "ACTIVE", null, null, 0, 2, 0, 1, 1, null, 1, new ContextBinding("tenant", "ws-a", 1, "storage-a", ".", "config-a", 1), "yolo", null); @@ -207,7 +207,7 @@ class ManagedEventStreamServiceTest { AgentStateStore store = mock(AgentStateStore.class); ManagedWorkspaceRegistry registry = mock(ManagedWorkspaceRegistry.class); ManagedAgentService agentService = new ManagedAgentService(store, - null, null, null, registry); + null, null, null, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); SessionRecord session = new SessionRecord("tenant", "session", "qwen-code", null, null, "ACTIVE", null, null, 0, 2, 0, 1, 1, null, 1, new ContextBinding("tenant", "ws-a", 1, "storage-a", ".", "config-a", 1), "yolo", null); @@ -290,7 +290,7 @@ class ManagedEventStreamServiceTest { AgentStateStore store = mock(AgentStateStore.class); ManagedWorkspaceRegistry registry = mock(ManagedWorkspaceRegistry.class); ManagedAgentService agentService = new ManagedAgentService(store, - null, null, null, registry); + null, null, null, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); SessionRecord session = new SessionRecord("tenant", "session", "qwen-code", null, null, "ACTIVE", null, null, 0, 2, 0, 1, 1, null, 1, new ContextBinding("tenant", "ws-a", 1, "storage-a", ".", "config-a", 1), "yolo", null); @@ -369,7 +369,7 @@ class ManagedEventStreamServiceTest { AgentStateStore store = mock(AgentStateStore.class); ManagedWorkspaceRegistry registry = mock(ManagedWorkspaceRegistry.class); ManagedAgentService agentService = new ManagedAgentService(store, - null, null, null, registry); + null, null, null, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); SessionRecord session = new SessionRecord("tenant", "session", "qwen-code", null, null, "ACTIVE", null, null, 0, 2, 0, 1, 1, null, 1, new ContextBinding("tenant", "ws-a", 1, "storage-a", ".", "config-a", 1), "yolo", null); @@ -444,7 +444,7 @@ class ManagedEventStreamServiceTest { AgentStateStore store = mock(AgentStateStore.class); ManagedWorkspaceRegistry registry = mock(ManagedWorkspaceRegistry.class); ManagedAgentService agentService = new ManagedAgentService(store, - null, null, null, registry); + null, null, null, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); SessionRecord session = new SessionRecord("tenant", "session", "qwen-code", null, null, "ACTIVE", null, null, 0, 2, 0, 1, 1, null, 1, new ContextBinding("tenant", "ws-a", 1, "storage-a", ".", "config-a", 1), "yolo", null); @@ -510,7 +510,7 @@ class ManagedEventStreamServiceTest { boolean reconcile) throws Exception { AgentStateStore store = mock(AgentStateStore.class); ManagedAgentService agentService = new ManagedAgentService(store, - null, null, null, mock(ManagedWorkspaceRegistry.class)); + null, null, null, mock(ManagedWorkspaceRegistry.class), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); when(store.requireSession("tenant", "session")).thenReturn(SESSION); List records = List.of(event(1, false), new EventRecord("tenant", "session", 2, "event-2", "turn", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachmentTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachmentTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachmentTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachmentTest.java index 65d07e2bee2..2cc4036e13c 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachmentTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ManagedInputAttachmentTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.managedagent.session; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; @@ -42,11 +42,11 @@ class ManagedInputAttachmentTest { + ";MODE=MySQL;DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE"); ManagedSchema.initialize(source); var registry = mock(ManagedWorkspaceRegistry.class); - store = new ManagedAgentStore(new ManagedSchemaJdbcTemplate(source), new ObjectMapper(), - Clock.systemUTC(), events -> {}, registry, new ManagedAgentProperties()); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(new ManagedSchemaJdbcTemplate(source), new ObjectMapper(), + Clock.systemUTC(), events -> {}, registry, new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); var harness = mock(HarnessConnector.class); when(harness.isAvailable()).thenReturn(true); - service = new ManagedAgentService(store, new RequestDigests(), mock(HarnessCoordinator.class), harness, registry); + service = new ManagedAgentService(store, new RequestDigests(), mock(HarnessCoordinator.class), harness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); } @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/MessageProjectionGapHealTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/MessageProjectionGapHealTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/MessageProjectionGapHealTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/MessageProjectionGapHealTest.java index 4b3f19ba10f..caaa29ea9af 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/MessageProjectionGapHealTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/MessageProjectionGapHealTest.java @@ -5,9 +5,9 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.persistence.EventIdentity; import com.alibaba.qwen.code.managedagent.event.MessageMaterializer; import com.alibaba.qwen.code.managedagent.model.StoreModels; -import com.alibaba.qwen.code.managedagent.persistence.EventIdentity; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; @@ -383,7 +383,7 @@ class MessageProjectionGapHealTest { jdbc = new ManagedSchemaJdbcTemplate(dataSource); transactions = new TransactionTemplate( new DataSourceTransactionManager(dataSource)); - return new ManagedAgentStore(jdbc, new ObjectMapper(), new Clock() { + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), new Clock() { @Override public ZoneId getZone() { return ZoneOffset.UTC; @@ -398,6 +398,6 @@ class MessageProjectionGapHealTest { public Instant instant() { return Instant.ofEpochMilli(now.get()); } - }, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties()); + }, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationReconcilerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationReconcilerTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationReconcilerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationReconcilerTest.java index af23eb319d5..b264fc22853 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationReconcilerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationReconcilerTest.java @@ -1,14 +1,16 @@ package com.alibaba.qwen.code.managedagent.session; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AcceptanceEvidence; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.AcceptanceVerdict; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.ParkedOperation; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore.TaskTarget; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.TaskTarget; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -350,8 +352,8 @@ class OperationReconcilerTest { } @SuppressWarnings("unchecked") - private ObjectProvider brokers() { - ObjectProvider provider = + private ObjectProvider brokers() { + ObjectProvider provider = mock(ObjectProvider.class); when(provider.getIfAvailable()).thenReturn(broker); return provider; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationRetryTerminalStateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationRetryTerminalStateTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationRetryTerminalStateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationRetryTerminalStateTest.java index 3d823e50aaf..16e5bd0a272 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationRetryTerminalStateTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/OperationRetryTerminalStateTest.java @@ -1,21 +1,21 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.daemon.DaemonProtocolException; -import com.alibaba.qwen.code.daemon.HostedHarnessCapabilityMismatchException; -import com.alibaba.qwen.code.daemon.HostedHarnessGenerationException; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessCapabilityFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessGenerationFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessProtocolFailure; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedActionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; import com.alibaba.qwen.code.managedagent.workspace.ChildLifecycleAdmissions; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.fasterxml.jackson.databind.JsonNode; @@ -25,7 +25,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.ObjectProvider; -import org.springframework.http.HttpStatus; import java.time.Clock; import java.time.Duration; @@ -219,7 +218,7 @@ class OperationRetryTerminalStateTest { "ACTIVE", "boot-1", null, 0, 0, 1, 1, null, 1)); when(harness.isAvailable()).thenReturn(true); when(harness.closeSession(any(OperationRecord.class))).thenThrow( - mock(HostedHarnessGenerationException.class)); + mock(HarnessGenerationFailure.class)); when(sessionStore.hasLiveWriter("tenant", "session")) .thenReturn(false); @@ -276,7 +275,7 @@ class OperationRetryTerminalStateTest { "ACTIVE", "boot-1", null, 0, 0, 1, 1, null, 1)); when(harness.isAvailable()).thenReturn(true); when(harness.closeSession(any(OperationRecord.class))).thenThrow( - mock(HostedHarnessGenerationException.class)); + mock(HarnessGenerationFailure.class)); // The old boot's writer lease has not expired yet. when(sessionStore.hasLiveWriter("tenant", "session")) .thenReturn(true); @@ -329,7 +328,7 @@ class OperationRetryTerminalStateTest { "ACTIVE", "boot-1", null, 0, 0, 1, 1, null, 1)); when(harness.isAvailable()).thenReturn(true); when(harness.closeSession(any(OperationRecord.class))).thenThrow( - mock(HostedHarnessGenerationException.class)); + mock(HarnessGenerationFailure.class)); when(sessionStore.hasLiveWriter("tenant", "session")) .thenReturn(false); @@ -638,8 +637,8 @@ class OperationRetryTerminalStateTest { when(runtimeWarmer.closeWorkspace("tenant", "session")) .thenReturn(CompletableFuture.completedFuture(null)); when(harness.isAvailable()).thenReturn(true); - HostedHarnessCapabilityMismatchException mismatch = - mock(HostedHarnessCapabilityMismatchException.class); + HarnessCapabilityFailure mismatch = + mock(HarnessCapabilityFailure.class); when(mismatch.getCode()).thenReturn("managed_capability_mismatch"); when(harness.closeSession(any(OperationRecord.class))).thenThrow(mismatch); // The Harness was never asked to stop, so it keeps the writer. @@ -703,7 +702,7 @@ class OperationRetryTerminalStateTest { .thenReturn(CompletableFuture.completedFuture(null)); when(harness.isAvailable()).thenReturn(true); when(harness.closeSession(any(OperationRecord.class))) - .thenThrow(mock(DaemonProtocolException.class)); + .thenThrow(mock(HarnessProtocolFailure.class)); when(sessionStore.hasLiveWriter("tenant", "session")) .thenReturn(true); @@ -757,8 +756,8 @@ class OperationRetryTerminalStateTest { 1, BOUND_WORKSPACE, "yolo", TOOL_PROFILE)); when(runtimeWarmer.supportsWorkspaceClose(BOUND_WORKSPACE)).thenReturn(true); when(harness.isAvailable()).thenReturn(true); - HostedHarnessCapabilityMismatchException mismatch = - mock(HostedHarnessCapabilityMismatchException.class); + HarnessCapabilityFailure mismatch = + mock(HarnessCapabilityFailure.class); when(mismatch.getCode()).thenReturn("managed_capability_mismatch"); when(harness.closeSession(any(OperationRecord.class))).thenThrow(mismatch); when(sessionStore.hasLiveWriter("tenant", "session")) @@ -1014,8 +1013,7 @@ class OperationRetryTerminalStateTest { when(harness.settleLifecycle(any(OperationRecord.class))) .thenReturn(new ObjectMapper().createObjectNode() .put("settled", true)); - doThrow(com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore - .blocked("workspace_lifecycle_hooks_unsettled")) + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.blocked("workspace_lifecycle_hooks_unsettled")) .when(lifecycle).saveEffects(any(OperationRecord.class), any(JsonNode.class)); @@ -1203,7 +1201,7 @@ class OperationRetryTerminalStateTest { // No durable receipt, so the settle calls the Harness; the Harness // answered 200 but the receipt failed to decode — the client marks // it as answered. - doThrow(new com.alibaba.qwen.code.daemon.HostedHarnessSettleAnsweredException( + doThrow(new com.alibaba.qwen.code.managedagent.harness.contracts.HarnessSettleAnsweredFailure( "POST /session/:id/lifecycle contains invalid JSON", null)) .when(harness).settleLifecycle(any()); @@ -1281,7 +1279,7 @@ class OperationRetryTerminalStateTest { .put("neverInitialized", true)); // The settle's next step keeps failing (the detach endpoint is // down), which classifies as the lifecycle hooks code. - doThrow(mock(com.alibaba.qwen.code.daemon.DaemonHttpException.class)) + doThrow(mock(com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure.class)) .when(harness).detachLifecycle(any()); SessionLifecycleCoordinator coordinator = @@ -1350,7 +1348,7 @@ class OperationRetryTerminalStateTest { when(lifecycle.recoverEffects(any())).thenReturn( new ObjectMapper().createObjectNode() .put("storeSynthesized", true)); - doThrow(mock(com.alibaba.qwen.code.daemon.DaemonHttpException.class)) + doThrow(mock(com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure.class)) .when(harness).detachLifecycle(any()); SessionLifecycleCoordinator coordinator = @@ -1427,14 +1425,14 @@ class OperationRetryTerminalStateTest { when(lifecycle.recoverEffects(any())).thenReturn( new ObjectMapper().createObjectNode() .put("storeSynthesized", true)); - DaemonHttpException refusal = mock(DaemonHttpException.class); + HarnessHttpFailure refusal = mock(HarnessHttpFailure.class); when(refusal.getErrorCode()).thenReturn(refusalCode); - // The SDK wraps the daemon's refusal in MutationOutcomeUnknownException + // The SDK wraps the daemon's refusal in HarnessMutationUnknownFailure // once the non-idempotent detach may have been delivered, so the wire // code lives on the cause chain — the reason daemonRefusalCode walks it. if (wrapped) { - com.alibaba.qwen.code.daemon.MutationOutcomeUnknownException outcome = - mock(com.alibaba.qwen.code.daemon.MutationOutcomeUnknownException.class); + com.alibaba.qwen.code.managedagent.harness.contracts.HarnessMutationUnknownFailure outcome = + mock(com.alibaba.qwen.code.managedagent.harness.contracts.HarnessMutationUnknownFailure.class); when(outcome.getCause()).thenReturn(refusal); doThrow(outcome).when(harness).detachLifecycle(any()); } else { @@ -1502,8 +1500,8 @@ class OperationRetryTerminalStateTest { 1, BOUND_WORKSPACE, "yolo", TOOL_PROFILE)); when(runtimeWarmer.supportsWorkspaceClose(BOUND_WORKSPACE)).thenReturn(true); when(harness.isAvailable()).thenReturn(true); - HostedHarnessCapabilityMismatchException mismatch = - mock(HostedHarnessCapabilityMismatchException.class); + HarnessCapabilityFailure mismatch = + mock(HarnessCapabilityFailure.class); when(mismatch.getCode()).thenReturn("managed_capability_mismatch"); when(harness.closeSession(any(OperationRecord.class))).thenThrow(mismatch); when(sessionStore.hasLiveWriter("tenant", "session")) @@ -1747,7 +1745,7 @@ class OperationRetryTerminalStateTest { // The writer the retirement waits on is still live. when(sessionStore.hasLiveWriter("tenant", "session")) .thenReturn(true); - doThrow(new ManagedAgentException(HttpStatus.CONFLICT, + doThrow(new ManagedAgentException(409, "managed_session_writer_active", "Session deletion is waiting for its writer to stop.")) .when(store).completeOperation(eq("tenant"), eq("session"), @@ -2491,7 +2489,7 @@ class OperationRetryTerminalStateTest { when(actions.find("tenant", "session", "action-1")).thenReturn( Optional.of(new ManagedActionStore.Action("action-1", "requested", body, null, null))); - DaemonHttpException unavailable = mock(DaemonHttpException.class); + HarnessHttpFailure unavailable = mock(HarnessHttpFailure.class); when(unavailable.getStatusCode()).thenReturn(503); doThrow(unavailable).when(harness).resolveAction("tenant", "session", "action-1", body); @@ -2542,7 +2540,7 @@ class OperationRetryTerminalStateTest { "requested", new ObjectMapper().readTree( "{\"expiresAt\":1}"), null, null))); - DaemonHttpException unavailable = mock(DaemonHttpException.class); + HarnessHttpFailure unavailable = mock(HarnessHttpFailure.class); when(unavailable.getStatusCode()).thenReturn(503); doThrow(unavailable).when(harness).resolveAction("tenant", "session", "action-1", body); @@ -2591,8 +2589,8 @@ class OperationRetryTerminalStateTest { when(actions.find("tenant", "session", "action-1")).thenReturn( Optional.of(new ManagedActionStore.Action("action-1", "requested", body, null, null))); - HostedHarnessCapabilityMismatchException mismatch = - mock(HostedHarnessCapabilityMismatchException.class); + HarnessCapabilityFailure mismatch = + mock(HarnessCapabilityFailure.class); when(mismatch.getCode()).thenReturn("managed_capability_mismatch"); doThrow(mismatch).when(harness).resolveAction("tenant", "session", "action-1", body); @@ -2641,7 +2639,7 @@ class OperationRetryTerminalStateTest { when(actions.find("tenant", "session", "action-1")).thenReturn( Optional.of(new ManagedActionStore.Action("action-1", "requested", body, null, null))); - doThrow(WorkspaceExecutionStore.unavailable()).when(harness) + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(harness) .resolveAction("tenant", "session", "action-1", body); ActionResponseCoordinator coordinator = new ActionResponseCoordinator( @@ -2847,7 +2845,7 @@ class OperationRetryTerminalStateTest { when(actions.find("tenant", "session", "action-1")).thenReturn( Optional.of(new ManagedActionStore.Action("action-1", "requested", body, null, null))); - DaemonHttpException rejected = mock(DaemonHttpException.class); + HarnessHttpFailure rejected = mock(HarnessHttpFailure.class); when(rejected.getStatusCode()).thenReturn(400); doThrow(rejected).when(harness).resolveAction("tenant", "session", "action-1", body); @@ -2879,7 +2877,7 @@ class OperationRetryTerminalStateTest { when(actions.find("tenant", "session", "action-1")).thenReturn( Optional.of(new ManagedActionStore.Action("action-1", "decided", body, "rcpt-9", - ManagedActionStore.decisionDigest(body)))); + com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.decisionDigest(body)))); ActionResponseCoordinator coordinator = new ActionResponseCoordinator( sessions, actions, harness, @@ -2922,7 +2920,7 @@ class OperationRetryTerminalStateTest { "requested", body, null, null)), Optional.of(new ManagedActionStore.Action("action-1", "decided", body, "rcpt-9", - ManagedActionStore.decisionDigest(body)))); + com.alibaba.qwen.code.managedagent.repository.ManagedActionStore.decisionDigest(body)))); ActionResponseCoordinator coordinator = new ActionResponseCoordinator( sessions, actions, harness, diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptionsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptionsTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptionsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptionsTest.java index da421e87f27..b7bce56657b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptionsTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/OwnerSubscriptionsTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.AttachedSession; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.AttachedSession; import com.alibaba.qwen.code.managedagent.persistence.TransportConsumerStore; import org.apache.rocketmq.client.apis.ClientException; import org.apache.rocketmq.client.apis.consumer.LitePushConsumer; @@ -36,7 +36,7 @@ class OwnerSubscriptionsTest { store = mock(TransportConsumerStore.class); consumer = mock(LitePushConsumer.class); owners = new OwnerSubscriptions(harness, store); - owners.bind(consumer); + owners.bind(com.alibaba.qwen.code.managedagent.adapters.event.RocketMqMessages.subscriptions(consumer)); } @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/RecoverySettlementTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/RecoverySettlementTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/RecoverySettlementTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/RecoverySettlementTest.java index a3a380658a0..a8127ded833 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/RecoverySettlementTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/RecoverySettlementTest.java @@ -1,20 +1,21 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.daemon.DaemonProtocolException; -import com.alibaba.qwen.code.daemon.HarnessRuntimeRecovery; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.Admission; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.Attachment; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.RecoveryStatus; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.SourceEvent; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.SourceStream; -import com.alibaba.qwen.code.managedagent.harness.HostedHarnessRecoveryDeclinedException; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.Admission; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.Attachment; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.RecoveryStatus; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.SourceEvent; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.SourceStream; +import com.alibaba.qwen.code.managedagent.contracts.harness.HostedHarnessRecoveryDeclinedException; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessProtocolFailure; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessRuntimeRecovery; import com.alibaba.qwen.code.managedagent.model.StoreModels.HarnessEvent; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnRecord; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -51,7 +52,7 @@ class RecoverySettlementTest { @ValueSource(ints = {10, 42}) void cancellationAdmissionAloneCannotFinishAnAdmittedWorkspaceTurn(int retries) { Fixture fixture = new Fixture(retries, "RUNNING", false); - DaemonHttpException unavailable = mock(DaemonHttpException.class); + HarnessHttpFailure unavailable = mock(HarnessHttpFailure.class); when(unavailable.getStatusCode()).thenReturn(503); fixture.failRecovery(unavailable); @@ -70,7 +71,7 @@ class RecoverySettlementTest { void terminalFailureAlsoWaitsForTheOriginalNativeInput(String kind) { Fixture fixture = new Fixture(0, "RUNNING", false); fixture.failRecovery("protocol".equals(kind) - ? mock(DaemonProtocolException.class) + ? mock(HarnessProtocolFailure.class) : new HostedHarnessRecoveryDeclinedException("model_snapshot_missing")); fixture.dispatch(); @@ -90,7 +91,7 @@ class RecoverySettlementTest { @Test void aLiveResidentWithoutTakeoverWorkStillReceivesCancellation() { Fixture fixture = new Fixture(0, "RUNNING", false); - fixture.failRecovery(mock(DaemonProtocolException.class)); + fixture.failRecovery(mock(HarnessProtocolFailure.class)); when(fixture.harness.recoverManagedFailureCancellation(eq("tenant"), eq("session"), eq("turn"), anyString(), eq("original-prompt"))).thenReturn(new Attachment("boot-old", null, 4L, "epoch-old")); @@ -108,7 +109,7 @@ class RecoverySettlementTest { @Test void aLostIntentWriteCannotIssueCancellation() { Fixture fixture = new Fixture(0, "RUNNING", false); - fixture.failRecovery(mock(DaemonProtocolException.class)); + fixture.failRecovery(mock(HarnessProtocolFailure.class)); doReturn(false).when(fixture.store).markTurnFailureRecovery(anyString(), anyString(), anyString(), anyString(), anyString()); @@ -144,7 +145,7 @@ class RecoverySettlementTest { void partialOrForeignProofCannotFinishTheTurn(boolean settled, boolean continuation, String proofPrompt) { Fixture fixture = new Fixture(0, "RUNNING", false); - fixture.failRecovery(mock(DaemonProtocolException.class)); + fixture.failRecovery(mock(HarnessProtocolFailure.class)); fixture.proof.set(new RecoveryStatus("awaiting_evidence", "unconfirmed", proofPrompt, settled, continuation, false)); @@ -156,7 +157,7 @@ class RecoverySettlementTest { @Test void matchingProofProjectsTheNativeTailBeforeCompletingTheTurn() { Fixture fixture = new Fixture(0, "RUNNING", false); - fixture.failRecovery(mock(DaemonProtocolException.class)); + fixture.failRecovery(mock(HarnessProtocolFailure.class)); doAnswer(invocation -> { fixture.proof.set(ready()); return new Admission(5L, "epoch-new"); @@ -183,7 +184,7 @@ class RecoverySettlementTest { @Test void alreadyCommittedProofDoesNotStopTheOriginalExecutionAgain() { Fixture fixture = new Fixture(0, "RUNNING", false); - fixture.failRecovery(mock(DaemonProtocolException.class)); + fixture.failRecovery(mock(HarnessProtocolFailure.class)); fixture.proof.set(ready()); fixture.dispatch(); @@ -201,7 +202,7 @@ class RecoverySettlementTest { @Test void matchingNativeProofCanSettleALostAdmissionResponse() { Fixture fixture = new Fixture(0, "RUNNING", false, false); - fixture.failRecovery(mock(DaemonProtocolException.class)); + fixture.failRecovery(mock(HarnessProtocolFailure.class)); fixture.proof.set(ready()); fixture.dispatch(); @@ -217,7 +218,7 @@ class RecoverySettlementTest { @Test void missingReadinessFieldsFromAnOldHarnessRemainBlocked() { Fixture fixture = new Fixture(0, "RUNNING", false); - fixture.failRecovery(mock(DaemonProtocolException.class)); + fixture.failRecovery(mock(HarnessProtocolFailure.class)); fixture.proof.set(null); fixture.dispatch(); @@ -228,7 +229,7 @@ class RecoverySettlementTest { @Test void unreadableNativeEvidenceCannotBeConvertedIntoTerminalFailure() { Fixture fixture = new Fixture(0, "RUNNING", false); - fixture.failRecovery(mock(DaemonProtocolException.class)); + fixture.failRecovery(mock(HarnessProtocolFailure.class)); when(fixture.harness.inspectRecovery("tenant", "session", "original-prompt")) .thenThrow(new IllegalStateException("evidence store unavailable")); @@ -256,7 +257,7 @@ class RecoverySettlementTest { @Test void aTailReadFailureParksAndRetriesFromTheLastConsumedCursor() { Fixture fixture = new Fixture(0, "RUNNING", false); - fixture.failRecovery(mock(DaemonProtocolException.class)); + fixture.failRecovery(mock(HarnessProtocolFailure.class)); fixture.proof.set(ready()); fixture.failAfterChunk.set(true); @@ -274,7 +275,7 @@ class RecoverySettlementTest { @Test void aSafeInitialCancellationProjectsItsTerminalWithoutAContinuationCheckpoint() { Fixture fixture = new Fixture(0, "RUNNING", false, false); - fixture.failRecovery(mock(DaemonProtocolException.class)); + fixture.failRecovery(mock(HarnessProtocolFailure.class)); fixture.proof.set(ready()); fixture.tail = List.of(new SourceEvent(1L, "turn_complete", Map.of("stopReason", "cancelled"), "original-prompt", Map.of())); @@ -288,7 +289,7 @@ class RecoverySettlementTest { @Test void aUserCancellationProjectsItsActualTerminalInsteadOfAFailure() { Fixture fixture = new Fixture(0, "CANCELLING", false); - fixture.failRecovery(mock(DaemonProtocolException.class)); + fixture.failRecovery(mock(HarnessProtocolFailure.class)); fixture.proof.set(ready()); fixture.tail = List.of(new SourceEvent(5L, "turn_complete", Map.of("stopReason", "cancelled"), "original-prompt", Map.of())); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancerBacklogTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancerBacklogTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancerBacklogTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancerBacklogTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancerTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancerTest.java index c45dae57649..f1d39aaa7df 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorAdvancerTest.java @@ -1,6 +1,7 @@ package com.alibaba.qwen.code.managedagent.session; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.model.StoreModels.ReplayFloorTarget; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorRetractionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorRetractionTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorRetractionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorRetractionTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorSchedulerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorSchedulerTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorSchedulerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ReplayFloorSchedulerTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ResidentAcceptanceSnapshot.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ResidentAcceptanceSnapshot.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/ResidentAcceptanceSnapshot.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/ResidentAcceptanceSnapshot.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionEventHubPinningTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionEventHubPinningTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionEventHubPinningTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionEventHubPinningTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionEventHubTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionEventHubTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionEventHubTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionEventHubTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinatorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinatorTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinatorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinatorTest.java index bbdc92e2b1a..2b7e6aec3ed 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinatorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleCoordinatorTest.java @@ -5,7 +5,10 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.harness.UnavailableHarnessConnector; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels; @@ -16,13 +19,11 @@ import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.workspace.ChildLifecycleAdmissions; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; @@ -67,7 +68,7 @@ class SessionLifecycleCoordinatorTest { var harness = Mockito.mock(HarnessConnector.class); var warmer = Mockito.mock(RuntimeWarmer.class); Mockito.when(warmer.supportsWorkspaceClose(session.workspace())).thenReturn(true); - var authority = com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore.authority(operation); + var authority = com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation); Mockito.when(warmer.prepareWorkspaceResources("tenant", "session", authority)).thenReturn(CompletableFuture.completedFuture(null)); Mockito.when(warmer.drainWorkspaceResources("tenant", "session", authority)).thenReturn(CompletableFuture.completedFuture(null)); Mockito.when(warmer.closeWorkspace("tenant", "session")).thenReturn(CompletableFuture.completedFuture(null)); @@ -110,7 +111,7 @@ class SessionLifecycleCoordinatorTest { "hosted-workspace-mcp/1,runtime_admission_closed,false" }) void onlyMcpIdentityFailureCanRecoverOriginalStoppedResources(String profile, String code, boolean recovery) throws Exception { - var store = Mockito.mock(com.alibaba.qwen.code.managedagent.session.AgentStateStore.class); + var store = Mockito.mock(com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.class); var session = Mockito.mock(StoreModels.SessionRecord.class); Mockito.when(session.workspace()).thenReturn(Mockito.mock(com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding.class)); Mockito.when(session.toolProfile()).thenReturn(profile); @@ -120,13 +121,13 @@ class SessionLifecycleCoordinatorTest { "digest", "DELIVERING", "ACCEPTED", "DRAINING", "ACTIVE", "receipt", "server", 1, 1, null, 2); Mockito.when(lifecycle.recoverEffects(operation)).thenReturn(new ObjectMapper().createObjectNode().put("neverInitialized", false)); var harness = Mockito.mock(HarnessConnector.class); - var failure = Mockito.mock(com.alibaba.qwen.code.daemon.DaemonHttpException.class); + var failure = Mockito.mock(com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure.class); Mockito.when(failure.getErrorCode()).thenReturn(code); Mockito.doThrow(failure).when(harness).prepareLifecycleResources(operation); var warmer = Mockito.mock(RuntimeWarmer.class); Mockito.when(warmer.supportsWorkspaceClose()).thenReturn(true); Mockito.when(warmer.supportsWorkspaceClose(session.workspace())).thenReturn(true); - var authority = com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore.authority(operation); + var authority = com.alibaba.qwen.code.managedagent.repository.WorkspaceLifecycleStore.authority(operation); Mockito.when(warmer.recoverStoppedWorkspaceResources("tenant", "session", authority)).thenReturn(CompletableFuture.completedFuture(null)); Mockito.when(warmer.closeWorkspace("tenant", "session")).thenReturn(CompletableFuture.completedFuture(null)); var relay = Mockito.mock(ChildResultRelayStore.class); @@ -163,7 +164,7 @@ class SessionLifecycleCoordinatorTest { @org.junit.jupiter.params.ParameterizedTest @org.junit.jupiter.params.provider.CsvSource({"0,true", "0,false", "1,true", "1,false"}) void settlesOnlyWhenTheSelectedBindingSupportsClose(int protocol, boolean supported) throws Exception { - var store = Mockito.mock(com.alibaba.qwen.code.managedagent.session.AgentStateStore.class); + var store = Mockito.mock(com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.class); var session = Mockito.mock(StoreModels.SessionRecord.class); var workspace = new com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding( "tenant", "workspace", 1, "storage", ".", "config", 1); @@ -220,7 +221,7 @@ class SessionLifecycleCoordinatorTest { } private static ChildLifecycleAdmissions admissions( - com.alibaba.qwen.code.managedagent.session.AgentStateStore store, + com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore store, RuntimeWarmer warmer) { return new ChildLifecycleAdmissions(store, new RequestDigests(), warmer); @@ -241,8 +242,8 @@ class SessionLifecycleCoordinatorTest { var jdbc = new ManagedSchemaJdbcTemplate(source); var properties = new ManagedAgentProperties(); properties.getHarness().setWorkspaceFilesEnabled(true); - var store = new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, - new ManagedWorkspaceRegistry(jdbc), properties); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), Clock.systemUTC(), ignored -> {}, + new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); jdbc.update("INSERT INTO ma_workspace (tenant_id, workspace_id, workspace_generation, storage_id," + " display_name, config_ref, policy_ref, state) VALUES ('tenant', 'workspace', 1, 'storage', 'Workspace', ?, ?, 'ACTIVE')", WorkspaceExecutionProfile.CONFIG_REF, WorkspaceExecutionProfile.POLICY_REF); @@ -259,7 +260,7 @@ class SessionLifecycleCoordinatorTest { public CompletionStage drain(String id) { throw new AssertionError("Accepted bound close must keep its original scope"); } }; try (var executor = Executors.newSingleThreadExecutor()) { - var coordinator = new SessionLifecycleCoordinator(store, new ManagedSessionStore(jdbc), + var coordinator = new SessionLifecycleCoordinator(store, com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)), new UnavailableHarnessConnector(), unsupported, new ChildResultRelayStore(jdbc), new ObjectMapper(), admissions(store, unsupported), noTurnCancels(), brokerProvider(null), executor, Clock.systemUTC(), properties); @@ -279,7 +280,7 @@ class SessionLifecycleCoordinatorTest { @Test void dispatchAfterShutdownDoesNotScheduleARenewal() { - var store = Mockito.mock(com.alibaba.qwen.code.managedagent.session.AgentStateStore.class); + var store = Mockito.mock(com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.class); var operation = new StoreModels.OperationRecord("tenant", "session", "operation", OperationKind.CLOSE, "digest", "DELIVERING", "ACCEPTED", "DRAINING", "ACTIVE", "receipt", "server", 1, 1, null, 2); Mockito.when(store.claimOperation(Mockito.eq("tenant"), Mockito.eq("session"), Mockito.eq("operation"), @@ -367,8 +368,8 @@ class SessionLifecycleCoordinatorTest { properties.getHarness().setWorkspaceFilesEnabled(true); properties.getDispatch().setLeaseDuration(Duration.ofMillis(200)); properties.getDispatch().setRetryInitialDelay(Duration.ofMillis(50)); - var store = new ManagedAgentStore(jdbc, new ObjectMapper(), - Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), + Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); jdbc.update("INSERT INTO ma_workspace (tenant_id," + " workspace_id, workspace_generation, storage_id," + " display_name, config_ref, policy_ref, state)" @@ -485,8 +486,8 @@ class SessionLifecycleCoordinatorTest { + " 'workspace', ?, 'child_run', 'run-1', 'h', 1," + " 'res-run-1', '" + taskKind + "', 'running', 'session'," + " 'planned', 1)", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey("tenant", session), - com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection.recordKey(session, "child_run", "run-1"), session); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), + com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.recordKey(session, "child_run", "run-1"), session); byte[] bytes = resourceBody.getBytes(StandardCharsets.UTF_8); jdbc.update("INSERT INTO ma_resource" + " (resource_key, session_scope_key, tenant_id, workspace_id," @@ -496,9 +497,9 @@ class SessionLifecycleCoordinatorTest { + " VALUES (?, ?, 'tenant', 'workspace', ?," + " 'res-run-1', 'managed-input', 1, ?," + " '" + "b".repeat(64) + "', 'MYSQL_INLINE', ?, 'command', 'REFERENCED', " + ManagedSchemaTime.SQL_NOW + ")", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.resourceKey( - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey("tenant", session), "res-run-1"), - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey("tenant", session), session, bytes.length, bytes); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.resourceKey( + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), "res-run-1"), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", session), session, bytes.length, bytes); } private String childCloseOperation(World world) { @@ -588,7 +589,7 @@ class SessionLifecycleCoordinatorTest { } @SuppressWarnings("unchecked") - private static ObjectProvider brokerProvider( + private static ObjectProvider brokerProvider( RuntimeBindingRecord binding) { RuntimeBrokerService broker = Mockito.mock(RuntimeBrokerService.class); if (binding != null) { @@ -599,7 +600,7 @@ class SessionLifecycleCoordinatorTest { Mockito.anyString(), Mockito.anyString())) .thenReturn(binding); } - ObjectProvider provider = + ObjectProvider provider = Mockito.mock(ObjectProvider.class); Mockito.when(provider.getIfAvailable()).thenAnswer(ignored -> broker); return provider; @@ -661,7 +662,7 @@ class SessionLifecycleCoordinatorTest { var harness = new CascadingHarness(true, false); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(false, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(false, false)), @@ -699,7 +700,7 @@ class SessionLifecycleCoordinatorTest { var harness = new CascadingHarness(true, true); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), noTurnCancels(), @@ -754,12 +755,12 @@ class SessionLifecycleCoordinatorTest { + " VALUES (?, ?, 'tenant'," + " 'workspace', ?, 'team_state', 'team-1', 'h', 2," + " 'res-team-1', 1)", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey("tenant", world.session), - com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection.recordKey(world.session, "team_state", "team-1"), world.session); + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", world.session), + com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.recordKey(world.session, "team_state", "team-1"), world.session); var harness = new CascadingHarness(true, false); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), noTurnCancels(), @@ -830,7 +831,7 @@ class SessionLifecycleCoordinatorTest { }; try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), noTurnCancels(), @@ -895,7 +896,7 @@ class SessionLifecycleCoordinatorTest { var harness = new CascadingHarness(false, false); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, true), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, true)), noTurnCancels(), @@ -944,7 +945,7 @@ class SessionLifecycleCoordinatorTest { var harness = new CascadingHarness(true, false); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), noTurnCancels(), @@ -983,7 +984,7 @@ class SessionLifecycleCoordinatorTest { var harness = new CascadingHarness(true, false); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), noTurnCancels(), @@ -1044,14 +1045,14 @@ class SessionLifecycleCoordinatorTest { Mockito.when(broker.findLatestBindingByHarnessSessionAnyState( Mockito.anyString(), Mockito.anyString())) .thenReturn(retired); - ObjectProvider provider = + ObjectProvider provider = Mockito.mock(ObjectProvider.class); Mockito.when(provider.getIfAvailable()).thenAnswer( ignored -> broker); var harness = new CascadingHarness(true, false); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), noTurnCancels(), @@ -1094,7 +1095,7 @@ class SessionLifecycleCoordinatorTest { var harness = new CascadingHarness(true, false); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), noTurnCancels(), @@ -1138,7 +1139,7 @@ class SessionLifecycleCoordinatorTest { var harness = new CascadingHarness(true, false); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), noTurnCancels(), @@ -1180,7 +1181,7 @@ class SessionLifecycleCoordinatorTest { var harness = new CascadingHarness(true, true); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), noTurnCancels(), @@ -1233,7 +1234,7 @@ class SessionLifecycleCoordinatorTest { var harness = new CascadingHarness(true, false); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), noTurnCancels(), @@ -1278,7 +1279,7 @@ class SessionLifecycleCoordinatorTest { try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator( lifecycleWorld.store, - new ManagedSessionStore(lifecycleWorld.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(lifecycleWorld.jdbc)), harness, warmer(true, false), lifecycleWorld.relayStore, new ObjectMapper(), admissions(lifecycleWorld.store, warmer(true, false)), @@ -1327,7 +1328,7 @@ class SessionLifecycleCoordinatorTest { var harness = new CascadingHarness(true, false); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), noTurnCancels(), @@ -1393,7 +1394,7 @@ class SessionLifecycleCoordinatorTest { world.child, "run-1", "turn-1"); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), sessions, @@ -1481,7 +1482,7 @@ class SessionLifecycleCoordinatorTest { world.child, "run-1", "turn-1"); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), sessions, @@ -1553,7 +1554,7 @@ class SessionLifecycleCoordinatorTest { world.child, "run-1", "turn-1"); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), sessions, @@ -1606,7 +1607,7 @@ class SessionLifecycleCoordinatorTest { ManagedAgentService sessions = Mockito.mock(ManagedAgentService.class); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), sessions, @@ -1648,7 +1649,7 @@ class SessionLifecycleCoordinatorTest { pendingSessionMessage(world); var harness = new CascadingHarness(true, false); var refusal = Mockito.mock( - com.alibaba.qwen.code.daemon.DaemonHttpException.class); + com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure.class); Mockito.when(refusal.getErrorCode()) .thenReturn("hosted_session_closing"); harness.messageFailure = refusal; @@ -1667,7 +1668,7 @@ class SessionLifecycleCoordinatorTest { world.child, "run-1", "turn-1"); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), sessions, @@ -1733,7 +1734,7 @@ class SessionLifecycleCoordinatorTest { world.child, "run-1", "turn-1"); try (var executor = Executors.newSingleThreadExecutor()) { var coordinator = new SessionLifecycleCoordinator(world.store, - new ManagedSessionStore(world.jdbc), harness, + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(world.jdbc)), harness, warmer(true, false), world.relayStore, new ObjectMapper(), admissions(world.store, warmer(true, false)), sessions, @@ -1777,8 +1778,8 @@ class SessionLifecycleCoordinatorTest { + " VALUES (?, ?, 'tenant', 'workspace', ?," + " 'session_message', 'msg_1:message', 'h', 1," + " 'res-msg-1', 1)", - com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore.sessionScopeKey("tenant", world.child), - com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionProjection.recordKey(world.child, "session_message", "msg_1:message"), + com.alibaba.qwen.code.managedagent.repository.ManagedSessionStore.sessionScopeKey("tenant", world.child), + com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedExtensionProjection.recordKey(world.child, "session_message", "msg_1:message"), world.child); byte[] journal = ("{\"subtype\":\"managed_session_event_v1\"," + "\"managedSession\":{\"kind\":\"input.accepted\"," diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleProtocolChoiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleProtocolChoiceTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleProtocolChoiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleProtocolChoiceTest.java index f62fb0df730..4d8485a7efb 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleProtocolChoiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleProtocolChoiceTest.java @@ -1,11 +1,12 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationAdmission; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleServiceResumeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleServiceResumeTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleServiceResumeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleServiceResumeTest.java index 798ffb6f524..57b541a0161 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleServiceResumeTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionLifecycleServiceResumeTest.java @@ -1,6 +1,8 @@ package com.alibaba.qwen.code.managedagent.session; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; @@ -9,11 +11,11 @@ import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceLifecycleStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.h2.jdbcx.JdbcDataSource; @@ -185,7 +187,8 @@ class SessionLifecycleServiceResumeTest { ObjectProvider bindings = mock(ObjectProvider.class); when(bindings.getIfAvailable()) - .thenReturn(mock(RuntimeBindingRepository.class)); + .thenReturn(new JdbcRuntimeBindingRepository(dataSource, + new AesGcmSecretProtector("test", new byte[32]))); WorkspaceLifecycleStore lifecycle = new WorkspaceLifecycleStore(jdbc, new ObjectMapper(), bindings); store.setWorkspaceLifecycleStore(lifecycle); @@ -292,7 +295,7 @@ class SessionLifecycleServiceResumeTest { + ";MODE=MySQL;DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE"); ManagedSchema.initialize(dataSource); jdbc = new ManagedSchemaJdbcTemplate(dataSource); - return new ManagedAgentStore(jdbc, new ObjectMapper(), new Clock() { + return com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), new Clock() { @Override public ZoneId getZone() { return ZoneOffset.UTC; @@ -307,6 +310,6 @@ class SessionLifecycleServiceResumeTest { public Instant instant() { return Instant.ofEpochMilli(now.get()); } - }, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + }, ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelayTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelayTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelayTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelayTest.java index c588e61f06e..e06b3af0631 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelayTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionMessageRelayTest.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.managedagent.session; -import com.alibaba.qwen.code.daemon.DaemonHttpException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore; import com.alibaba.qwen.code.managedagent.persistence.SessionMessageRelayStore; -import com.alibaba.qwen.code.managedagent.persistence.SessionMessageRelayStore.Lineage; -import com.alibaba.qwen.code.managedagent.persistence.SessionMessageRelayStore.MessageRow; -import com.alibaba.qwen.code.managedagent.persistence.SessionMessageRelayStore.PendingMessage; +import com.alibaba.qwen.code.managedagent.repository.SessionMessageRelayStore.Lineage; +import com.alibaba.qwen.code.managedagent.repository.SessionMessageRelayStore.MessageRow; +import com.alibaba.qwen.code.managedagent.repository.SessionMessageRelayStore.PendingMessage; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; @@ -112,16 +112,10 @@ class SessionMessageRelayTest { return reply == null ? Map.of() : Map.of("reply", reply); } - static DaemonHttpException refusal(String code) { - try { - var ctor = DaemonHttpException.class.getDeclaredConstructor( - String.class, int.class, String.class); - ctor.setAccessible(true); - return ctor.newInstance("runMessageOperation", 409, - "{\"code\":\"" + code + "\"}"); - } catch (Exception error) { - throw new IllegalStateException(error); - } + static HarnessHttpFailure refusal(String code) { + String body = "{\"code\":\"" + code + "\"}"; + return new HarnessHttpFailure("runMessageOperation failed with HTTP 409: " + body, null, 409, + body, Map.of("code", code)); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryInitialStateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryInitialStateTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryInitialStateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryInitialStateTest.java index 0fce3817270..8a605ca1477 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryInitialStateTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryInitialStateTest.java @@ -1,15 +1,16 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.AcquireWriterRequest; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitResource; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.model.ApiModels.InputBlock; import com.alibaba.qwen.code.managedagent.model.RequestDigests; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.AcquireWriterRequest; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitResource; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels.CommitTransactionRequest; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.fasterxml.jackson.databind.JsonNode; @@ -79,12 +80,12 @@ class SessionRecoveryInitialStateTest { ManagedSchema.initialize(source); jdbc = new ManagedSchemaJdbcTemplate(source); var registry = mock(ManagedWorkspaceRegistry.class); - store = new ManagedAgentStore(jdbc, json, Clock.systemUTC(), events -> {}, registry, - new ManagedAgentProperties()); - journals = new ManagedSessionStore(jdbc); + store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, json, Clock.systemUTC(), events -> {}, registry, + new ManagedAgentProperties(), new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); + journals = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); when(harness.isAvailable()).thenReturn(true); when(harness.supportsFencedTitles()).thenReturn(true); - sessions = new ManagedAgentService(store, digests, coordinator, harness, registry); + sessions = new ManagedAgentService(store, digests, coordinator, harness, registry, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules()); sessionId = sessions.createSession(TENANT, "actor", "create", "qwen-code", null, "Untouched", null, List.of()).sessionId(); } @@ -317,11 +318,11 @@ class SessionRecoveryInitialStateTest { return result; } }; - assertThat(recovery(new ManagedSessionStore(moving)).query(TENANT, "actor", sessionId).nextTurnAllowed()).isFalse(); + assertThat(recovery(com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(moving))).query(TENANT, "actor", sessionId).nextTurnAllowed()).isFalse(); } private SessionRecoveryService recovery(ManagedSessionStore nativeStore) { - return new SessionRecoveryService(sessions, store, coordinator, harness, jdbc, nativeStore); + return new SessionRecoveryService(sessions, store, coordinator, harness, new com.alibaba.qwen.code.managedagent.persistence.JdbcSessionRecoveryRepository(jdbc), nativeStore); } private String cancelBeforeSubmission(String key) { @@ -343,10 +344,10 @@ class SessionRecoveryInitialStateTest { } private void prepareGenesis() throws Exception { - Path root = Path.of("").toAbsolutePath().getParent().getParent(); + Path root = VerificationFixtures.repositoryRoot(); Path output = directory.resolve("initial-journal.json"); var process = new ProcessBuilder("node", root.resolve( - "packages/managed-agent-server/src/test/resources/recovery-initial-journal.mjs").toString(), sessionId) + "packages/managed-agent-server/verification/src/test/resources/recovery-initial-journal.mjs").toString(), sessionId) .directory(root.toFile()).redirectErrorStream(true).redirectOutput(output.toFile()).start(); try { assertThat(process.waitFor(30, TimeUnit.SECONDS)).as("Core initial journal generator completed").isTrue(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryServiceTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryServiceTest.java index b9fb8eb6492..96b8c35146f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/SessionRecoveryServiceTest.java @@ -1,8 +1,9 @@ package com.alibaba.qwen.code.managedagent.session; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector.RecoveryStatus; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector.RecoveryStatus; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.TurnSummary; @@ -13,7 +14,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import java.util.List; @@ -56,7 +56,7 @@ class SessionRecoveryServiceTest { "tenant", "session", "MANAGED_SESSION", "READY", null); when(sessions.requireReadableSession("tenant", "actor", "session")).thenReturn(session); when(store.findLatestTurns("tenant", List.of("session"))).thenReturn(Map.of()); - service = new SessionRecoveryService(sessions, store, coordinator, harness, jdbc, mock(ManagedSessionStore.class)); + service = new SessionRecoveryService(sessions, store, coordinator, harness, new com.alibaba.qwen.code.managedagent.persistence.JdbcSessionRecoveryRepository(jdbc), mock(ManagedSessionStore.class)); } @Test @@ -233,7 +233,7 @@ class SessionRecoveryServiceTest { @Test void readerCanInspectButCannotRecheckOrCancel() { turn("RUNNING", AgentStateStore.RUNTIME_RECOVERY_BLOCKED); - doThrow(new ManagedAgentException(HttpStatus.FORBIDDEN, + doThrow(new ManagedAgentException(403, "session_operation_forbidden", "Operator required")) .when(sessions).requireRecoveryOperator(session, "actor"); var recovery = service.query("tenant", "actor", "session"); @@ -391,7 +391,7 @@ class SessionRecoveryServiceTest { @Test void foreignOrInvisibleSessionsNeverReachTheirEvidence() { when(sessions.requireReadableSession("foreign", "actor", "session")) - .thenThrow(new ManagedAgentException(HttpStatus.NOT_FOUND, + .thenThrow(new ManagedAgentException(404, "session_not_found", "Not found")); assertThatThrownBy(() -> service.query("foreign", "actor", "session")) .isInstanceOf(ManagedAgentException.class); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinatorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinatorTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinatorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinatorTest.java index c98dc2b5ec0..7cd38889be1 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinatorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/TaskCancelCoordinatorTest.java @@ -4,16 +4,16 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore.TaskCancelOutcome; import com.alibaba.qwen.code.managedagent.model.StoreModels.AcceptanceVerdict; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationKind; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.model.StoreModels.ParkedOperation; import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedExtensionRecordStore.TaskTarget; -import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore.TaskCancelOutcome; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; +import com.alibaba.qwen.code.managedagent.repository.ManagedExtensionRecordStore.TaskTarget; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -753,7 +753,7 @@ class TaskCancelCoordinatorTest { @Test void aDeliveryCancelTheRouteRefusesParksAndSettlesFromTheRecord() { when(harness.runChannelOperation(eq(TENANT), eq(SESSION), any())) - .thenThrow(WorkspaceExecutionStore.unavailable()); + .thenThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()); target.set(delivery("pending", false)); coordinator.dispatch(TENANT, SESSION, OPERATION); verify(store).retryOperation(eq(TENANT), eq(SESSION), eq(OPERATION), @@ -843,8 +843,8 @@ class TaskCancelCoordinatorTest { } @SuppressWarnings("unchecked") - private ObjectProvider brokers() { - ObjectProvider provider = + private ObjectProvider brokers() { + ObjectProvider provider = mock(ObjectProvider.class); when(provider.getIfAvailable()).thenReturn(broker); return provider; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/WakeRecoveryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/WakeRecoveryTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/WakeRecoveryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/WakeRecoveryTest.java index de13e1d90a2..3e0412cd0c0 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/WakeRecoveryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/WakeRecoveryTest.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.managedagent.session; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore; import com.alibaba.qwen.code.managedagent.persistence.WakeRecoveryStore; -import com.alibaba.qwen.code.managedagent.persistence.WakeRecoveryStore.Candidate; +import com.alibaba.qwen.code.managedagent.repository.WakeRecoveryStore.Candidate; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/WorkspaceRecoveryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/WorkspaceRecoveryTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/session/WorkspaceRecoveryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/session/WorkspaceRecoveryTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelayTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelayTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelayTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelayTest.java index 9d28426cf11..af2aed935a7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelayTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildResultRelayTest.java @@ -1,18 +1,18 @@ package com.alibaba.qwen.code.managedagent.workspace; -import com.alibaba.qwen.code.daemon.DaemonHttpException; +import com.alibaba.qwen.code.managedagent.contracts.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; -import com.alibaba.qwen.code.managedagent.harness.HarnessConnector; +import com.alibaba.qwen.code.managedagent.harness.contracts.HarnessHttpFailure; import com.alibaba.qwen.code.managedagent.model.ApiModels.CommandAdmission; import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore; -import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore.PendingChild; -import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore.RelayRow; -import com.alibaba.qwen.code.managedagent.persistence.ChildResultRelayStore.TurnLine; import com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; +import com.alibaba.qwen.code.managedagent.repository.ChildResultRelayStore.PendingChild; +import com.alibaba.qwen.code.managedagent.repository.ChildResultRelayStore.RelayRow; +import com.alibaba.qwen.code.managedagent.repository.ChildResultRelayStore.TurnLine; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -134,23 +134,16 @@ class ChildResultRelayTest { } /** The route's `409 child_operation_record` — the parent's own - * committed veto, never a transient shape: its constructor is - * package-private inside qwencode, so the double reaches it - * reflectively. */ - private static DaemonHttpException recordRefusal() { + * committed veto, never a transient shape, represented by the + * Harness contract boundary. */ + private static HarnessHttpFailure recordRefusal() { return refusal("child_operation_record"); } - static DaemonHttpException refusal(String code) { - try { - var ctor = DaemonHttpException.class.getDeclaredConstructor( - String.class, int.class, String.class); - ctor.setAccessible(true); - return ctor.newInstance("runChildOperation", 409, - "{\"code\":\"" + code + "\"}"); - } catch (Exception error) { - throw new IllegalStateException(error); - } + static HarnessHttpFailure refusal(String code) { + String body = "{\"code\":\"" + code + "\"}"; + return new HarnessHttpFailure("runChildOperation failed with HTTP 409: " + body, null, 409, + body, Map.of("code", code)); } } @@ -159,7 +152,7 @@ class ChildResultRelayTest { store = mock(ChildResultRelayStore.class); sessions = mock(ManagedAgentService.class); broker = mock(RuntimeBrokerService.class); - ObjectProvider provider = + ObjectProvider provider = Mockito.mock(ObjectProvider.class); when(provider.getIfAvailable()).thenAnswer(ignored -> broker); harness = new RecordingHarness(); @@ -1839,7 +1832,7 @@ class ChildResultRelayTest { } private static ManagedAgentException refusal(String code) { - return new ManagedAgentException(org.springframework.http.HttpStatus.CONFLICT, + return new ManagedAgentException(409, code, code); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceServiceTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceServiceTest.java index b3bc7e31319..40c20431f32 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/ChildWorkspaceServiceTest.java @@ -1,13 +1,19 @@ package com.alibaba.qwen.code.managedagent.workspace; +import com.alibaba.qwen.code.managedagent.adapters.workspace.ChildWorktreeGit; +import com.alibaba.qwen.code.managedagent.adapters.workspace.ChildWorktreeGitTest; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorkspaceException; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorkspaceProvider; +import com.alibaba.qwen.code.managedagent.contracts.workspace.RuntimeWarmer; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceToolProfiles; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.StoreModels; import com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore; -import com.alibaba.qwen.code.managedagent.persistence.ChildWorkspaceStore.Row; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; +import com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.Row; import com.alibaba.qwen.code.managedagent.session.ManagedAgentService; -import com.alibaba.qwen.code.managedagent.workspace.runtime.RuntimeWarmer; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; @@ -40,7 +46,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import static com.alibaba.qwen.code.managedagent.workspace.ChildWorktreeGitTest.plain; +import static com.alibaba.qwen.code.managedagent.adapters.workspace.ChildWorktreeGitTest.plain; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -129,8 +135,8 @@ class ChildWorkspaceServiceTest { assertThat(ready.state()).isEqualTo(ChildWorkspaceStore.READY); assertThat(ready.repositoryRelative()).isEqualTo("project"); - assertThat(ready.childCwdRelative()).isEqualTo(ChildWorktreeGit.childCwd(ready.childWorkspaceId(), ".")); - assertThat(ready.childWorkspaceId()).isEqualTo(ChildWorkspaceStore.childWorkspaceId( + assertThat(ready.childCwdRelative()).isEqualTo(com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorktreeGit.childCwd(ready.childWorkspaceId(), ".")); + assertThat(ready.childWorkspaceId()).isEqualTo(com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.childWorkspaceId( parent.tenantId(), parent.sessionId(), "run-1")); Path child = root.resolve(ready.childCwdRelative()); assertThat(plain(child, "rev-parse", "HEAD")).isEqualTo(ready.baseCommit()); @@ -168,7 +174,7 @@ class ChildWorkspaceServiceTest { assertThat(merged.lastError()).isNull(); assertThat(project.resolve("f.txt")).hasContent("PARENT\nb\nc\nd\nCHILD"); assertThat(project.resolve("new.txt")).hasContent("new"); - assertThat(root.resolve(ChildWorktreeGit.childDirectory(ready.childWorkspaceId()))).doesNotExist(); + assertThat(root.resolve(com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorktreeGit.childDirectory(ready.childWorkspaceId()))).doesNotExist(); assertThat(jdbc.queryForList("SELECT holder_key FROM ma_storage_owner" + " WHERE storage_key = ?", String.class, storageKey(parent.workspace()))).containsOnlyNulls(); assertPinsGone(ready); @@ -253,7 +259,7 @@ class ChildWorkspaceServiceTest { assertThat(merged.state()).isEqualTo(ChildWorkspaceStore.MERGED); assertThat(project.resolve("gen/x.txt")).hasContent("generated"); - assertThat(root.resolve(ChildWorktreeGit.childDirectory(ready.childWorkspaceId()))).doesNotExist(); + assertThat(root.resolve(com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorktreeGit.childDirectory(ready.childWorkspaceId()))).doesNotExist(); } @Test @@ -461,7 +467,7 @@ class ChildWorkspaceServiceTest { var parent = createSession("project"); Row ready = service.prepare(parent.tenantId(), parent.sessionId(), "run-1"); store.requestFinish(parent.tenantId(), parent.sessionId(), "run-1", ChildWorkspaceStore.MERGE, clock.get()); - Path worktree = root.resolve(ChildWorktreeGit.childDirectory(ready.childWorkspaceId())); + Path worktree = root.resolve(com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorktreeGit.childDirectory(ready.childWorkspaceId())); Path gone = switch (removed) { case "worktree" -> worktree; case "worktree-git" -> worktree.resolve(".git"); @@ -517,7 +523,7 @@ class ChildWorkspaceServiceTest { Row discarded = service.find(parent.tenantId(), parent.sessionId(), "run-1"); assertThat(discarded.state()).isEqualTo(ChildWorkspaceStore.DISCARDED); assertThat(discarded.resultCommit()).isNull(); - assertThat(root.resolve(ChildWorktreeGit.childDirectory(ready.childWorkspaceId()))).doesNotExist(); + assertThat(root.resolve(com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorktreeGit.childDirectory(ready.childWorkspaceId()))).doesNotExist(); assertPinsGone(discarded); } @@ -612,7 +618,7 @@ class ChildWorkspaceServiceTest { assertThat(discarded.resultCommit()).isNull(); assertThat(project.resolve("stray.txt")).doesNotExist(); assertThat(project.resolve("f.txt")).hasContent("uncommitted"); - assertThat(root.resolve(ChildWorktreeGit.childDirectory(ready.childWorkspaceId()))).doesNotExist(); + assertThat(root.resolve(com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorktreeGit.childDirectory(ready.childWorkspaceId()))).doesNotExist(); assertPinsGone(ready); } @@ -642,7 +648,7 @@ class ChildWorkspaceServiceTest { assertThat(discarded.outcomeCode()).isEqualTo("discarded"); assertThat(discarded.resultCommit()).isNull(); assertThat(project.resolve("stray.txt")).doesNotExist(); - assertThat(root.resolve(ChildWorktreeGit.childDirectory(ready.childWorkspaceId()))).doesNotExist(); + assertThat(root.resolve(com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorktreeGit.childDirectory(ready.childWorkspaceId()))).doesNotExist(); assertPinsGone(ready); } @@ -766,7 +772,7 @@ class ChildWorkspaceServiceTest { .isEqualTo(ChildWorkspaceStore.READY); assertThat(store.transition(stalled, "worker-a", ChildWorkspaceStore.FAILED, - ChildWorkspaceStore.columns(), clock.get())).isFalse(); + com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.columns(), clock.get())).isFalse(); // Same state, newer claim: only the claim generation tells them apart. Row second = store.admit(parent.tenantId(), parent.sessionId(), "run-2", parent.workspace(), clock.get()); @@ -775,9 +781,9 @@ class ChildWorkspaceServiceTest { Row taken = store.claim(second, "worker-a", clock.get(), ChildWorkspaceService.LEASE_MS); assertThat(taken.state()).isEqualTo(first.state()); assertThat(store.transition(first, "worker-a", ChildWorkspaceStore.FAILED, - ChildWorkspaceStore.columns(), clock.get())).isFalse(); + com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.columns(), clock.get())).isFalse(); assertThat(store.transition(taken, "worker-a", ChildWorkspaceStore.FAILED, - ChildWorkspaceStore.columns(), clock.get())).isTrue(); + com.alibaba.qwen.code.managedagent.repository.ChildWorkspaceStore.columns(), clock.get())).isTrue(); assertBroker(() -> leases.holdForMaintenance(parent.workspace(), stalled.childWorkspaceId(), stalled.claimGeneration()), "workspace_unavailable"); } @@ -942,7 +948,7 @@ class ChildWorkspaceServiceTest { warmer.provider = new ChildWorkspaceProvider() { @Override public Path storageRoot(ContextBinding binding) { - throw WorkspaceExecutionStore.unavailable(); + throw com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable(); } @Override @@ -1235,7 +1241,7 @@ class ChildWorkspaceServiceTest { String id = "0".repeat(32); String offset = "d".repeat(200) + "/" + "e".repeat(200) + "/" + "f".repeat(200) + "/" + "g".repeat(200) + "/" + "h".repeat(200); - assertThat(ChildWorkspaceService.childCwd(id, ".")).isEqualTo(ChildWorktreeGit.childDirectory(id)); + assertThat(ChildWorkspaceService.childCwd(id, ".")).isEqualTo(com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorktreeGit.childDirectory(id)); assertThatThrownBy(() -> ChildWorkspaceService.childCwd(id, offset)) .isInstanceOfSatisfying(ChildWorkspaceException.class, error -> assertThat(error.code()).isEqualTo(ChildWorkspaceException.LAYOUT)); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeGitTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeGitTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeGitTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeGitTest.java index 69e71996174..5043de01c03 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeGitTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeGitTest.java @@ -1,14 +1,18 @@ package com.alibaba.qwen.code.managedagent.workspace; +import com.alibaba.qwen.code.managedagent.adapters.workspace.ChildWorktreeGit; +import com.alibaba.qwen.code.managedagent.adapters.workspace.SessionWorktreeGit; import com.alibaba.qwen.code.managedagent.api.SessionWorktreeController; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.ChildWorkspaceException; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.exception.ManagedAgentException; import com.alibaba.qwen.code.managedagent.model.TenantContext; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry.ResolvedBinding; import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore; -import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore.Plan; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; +import com.alibaba.qwen.code.managedagent.repository.ManagedWorkspaceRegistry.ResolvedBinding; +import com.alibaba.qwen.code.managedagent.repository.SessionWorktreeStore.Plan; import com.alibaba.qwen.code.managedagent.session.HarnessCoordinator; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.fasterxml.jackson.databind.ObjectMapper; @@ -122,8 +126,8 @@ class SessionWorktreeGitTest { broker.setDurableLocalProcess(true); broker.setProvisioner("local-process"); broker.setBoundSessionIsolationClass("session"); - var service = new SessionWorktreeService(store, mock(WorkspaceExecutionStore.class), properties, - mock(ManagedAgentStore.class), mock(HarnessCoordinator.class)); + var service = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new SessionWorktreeService(store, mock(WorkspaceExecutionStore.class), properties, + mock(ManagedAgentStore.class), mock(HarnessCoordinator.class))); try { for (String ref : List.of("topic", "start", commit, "missing", "../invalid")) { var options = service.options("tenant", "actor", selection, ref); @@ -172,9 +176,9 @@ class SessionWorktreeGitTest { plan.worktreeId(), "PREPARING", plan, null, "operation", null, 1, null, "PREPARING", 0, 1, null, "actor"); when(store.claim(eq(row), anyString(), anyLong())).thenReturn(row); - var service = new SessionWorktreeService(store, execution, + var service = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new SessionWorktreeService(store, execution, new ManagedAgentProperties(), - mock(ManagedAgentStore.class), coordinator); + mock(ManagedAgentStore.class), coordinator)); try { service.drive(row); verify(store).complete(eq(row), anyString(), diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeServiceTest.java similarity index 88% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeServiceTest.java index ead7e776e62..2458d5daf08 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SessionWorktreeServiceTest.java @@ -1,7 +1,12 @@ package com.alibaba.qwen.code.managedagent.workspace; +import com.alibaba.qwen.code.managedagent.adapters.workspace.ChildWorktreeGit; +import com.alibaba.qwen.code.managedagent.adapters.workspace.LocalWorktreeFiles; +import com.alibaba.qwen.code.managedagent.adapters.workspace.SessionWorktreeGit; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedArtifactConfiguration; +import com.alibaba.qwen.code.managedagent.contracts.workspace.SessionWorktreeOperations; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorktreeFiles; import com.alibaba.qwen.code.managedagent.event.MessageMaterializer; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.SessionWorktreeStore; @@ -37,8 +42,8 @@ class SessionWorktreeServiceTest { private final SessionWorktreeStore store = mock(SessionWorktreeStore.class); private final WorkspaceExecutionStore execution = mock(WorkspaceExecutionStore.class); private final ManagedAgentStore sessions = mock(ManagedAgentStore.class); - private final SessionWorktreeService service = new SessionWorktreeService(store, execution, new ManagedAgentProperties(), - sessions, mock(HarnessCoordinator.class)); + private final SessionWorktreeService service = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new SessionWorktreeService(store, execution, new ManagedAgentProperties(), + sessions, mock(HarnessCoordinator.class))); @AfterEach void stop() { @@ -66,6 +71,9 @@ class SessionWorktreeServiceTest { ManagedArtifactConfiguration.class); context.registerBean(ManagedAgentProperties.class, ManagedAgentProperties::new); context.registerBean(MessageMaterializer.class, () -> mock(MessageMaterializer.class)); + context.registerBean(WorktreeFiles.class, LocalWorktreeFiles::new); + context.registerBean(SessionWorktreeOperations.Factory.class, + () -> (git, timeout) -> new SessionWorktreeGit(new ChildWorktreeGit(git, timeout))); context.registerBean("worktrees", SessionWorktreeService.class, () -> service); try { context.refresh(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceServiceTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceServiceTest.java index 3cad4987142..d2b681d944b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/SshWorkspaceServiceTest.java @@ -70,7 +70,7 @@ class SshWorkspaceServiceTest { assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isFalse(); Map request = invocation.getArgument(1); if ("register-storage".equals(request.get("operation"))) { - marker.set(SshWorkspaceStore.object(request, "marker")); + marker.set(com.alibaba.qwen.code.managedagent.repository.SshWorkspaceStore.object(request, "marker")); return Map.of("identity", identity, "marker", marker.get()); } var result = new LinkedHashMap(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/HostedWorkspaceRecoveryWorkerIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/HostedWorkspaceRecoveryWorkerIT.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/HostedWorkspaceRecoveryWorkerIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/HostedWorkspaceRecoveryWorkerIT.java index 68acb8905e6..65d31e0a7b1 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/HostedWorkspaceRecoveryWorkerIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/HostedWorkspaceRecoveryWorkerIT.java @@ -1,16 +1,17 @@ package com.alibaba.qwen.code.managedagent.workspace.runtime; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; import com.alibaba.qwen.code.runtimebroker.*; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeRecoveryEvidence; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import com.alibaba.qwen.code.runtimebroker.provisioning.LocalRebootTestSupport; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.io.TempDir; @@ -53,7 +54,8 @@ class HostedWorkspaceRecoveryWorkerIT { @Test @Timeout(120) void realWorkerAndSpringSqlRecoverSavedHolderAfterSyntheticReboot() throws Exception { - Path bundle = Path.of(System.getProperty("qwen.runtime.worker.bundle", "../../dist/cli.js")) + Path bundle = Path.of(System.getProperty("qwen.runtime.worker.bundle", + VerificationFixtures.repositoryRoot().resolve("dist/cli.js").toString())) .toAbsolutePath().normalize(); assertThat(bundle).isRegularFile(); temporary = temporary.toRealPath(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentHostedRuntimeFixture.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentHostedRuntimeFixture.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentHostedRuntimeFixture.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentHostedRuntimeFixture.java index fb71be64ebe..203455cba03 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentHostedRuntimeFixture.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/ResidentHostedRuntimeFixture.java @@ -1,13 +1,12 @@ package com.alibaba.qwen.code.managedagent.workspace.runtime; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.session.AgentStateStore; import com.alibaba.qwen.code.managedagent.persistence.ResidentWorkspaces; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceCsiReservationStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.session.AgentStateStore; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.ResidentWorkspaceProfile; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; @@ -21,8 +20,9 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer; @@ -135,7 +135,7 @@ public final class ResidentHostedRuntimeFixture implements RuntimeProvisioner { var fixture = new ResidentHostedRuntimeFixture(properties, jdbc, manager, bindings, command, processDirectory, retainedPlacement); factory.registerSingleton("residentHostedFixture", fixture); - return new EmbeddedRuntimeBroker(factory.getBean(AgentStateStore.class), properties, bindings, + return com.alibaba.qwen.code.managedagent.config.runtime.EmbeddedBrokerFixture.create(factory.getBean(AgentStateStore.class), properties, bindings, factory.getBean(RuntimeSessionRepository.class), factory.getBean(ToolExecutionRepository.class), factory.getBean(WorkspaceExecutionStore.class), factory.getBeanProvider(ToolPublicationStore.class).getIfAvailable(), factory.getBeanProvider(ToolPublicationDataStore.class).getIfAvailable(), jdbc, manager, fixture); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinatorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinatorTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinatorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinatorTest.java index 8b178802997..d39ede4d2c8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinatorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRecoveryCoordinatorTest.java @@ -2,7 +2,7 @@ package com.alibaba.qwen.code.managedagent.workspace.runtime; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.Test; import java.util.ArrayList; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJobTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJobTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJobTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJobTest.java index 28c7e9f2bce..44920d50548 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJobTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/RuntimeRetentionJobTest.java @@ -2,7 +2,7 @@ package com.alibaba.qwen.code.managedagent.workspace.runtime; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.RuntimeRetentionConfiguration; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeRetention; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeRetention; import org.junit.jupiter.api.Test; import org.springframework.boot.task.ThreadPoolTaskSchedulerBuilder; import org.springframework.context.annotation.AnnotationConfigApplicationContext; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRecoveryContract.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRecoveryContract.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRecoveryContract.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRecoveryContract.java index ae1c612f924..27c5bef7aaa 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRecoveryContract.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRecoveryContract.java @@ -1,14 +1,11 @@ package com.alibaba.qwen.code.managedagent.workspace.runtime; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceOperatorRecoveryStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.*; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; @@ -20,7 +17,10 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import org.springframework.jdbc.core.JdbcTemplate; import javax.sql.DataSource; @@ -51,7 +51,7 @@ public final class WorkspaceRecoveryContract { var created = transaction.execute(status -> store.insertWorkspaceSessionCommand(fixture.tenant, "actor", "neighbor", "sha256:" + "b".repeat(64), "qwen-code", null, null, List.of(), null, new WorkspaceSelection("workspace", "."))); var second = fixture.runtime("second", created.sessionId()); - var other = new WorkspaceExecutionStore(jdbc, new org.springframework.jdbc.datasource.DataSourceTransactionManager(source)); + var other = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, new org.springframework.jdbc.datasource.DataSourceTransactionManager(source))); var start = new java.util.concurrent.CyclicBarrier(2); try (var pool = Executors.newFixedThreadPool(2)) { var a = pool.submit(() -> { @@ -123,7 +123,7 @@ public final class WorkspaceRecoveryContract { connection.setAutoCommit(false); try (var lock = connection.prepareStatement("SELECT tenant_id FROM ma_tenant_guard" + " WHERE tenant_key = ? AND guard_domain = 'runtime_placement' FOR UPDATE")) { - lock.setString(1, com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key(fixture.tenant)); + lock.setString(1, com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key(fixture.tenant)); try (var rows = lock.executeQuery()) { assertThat(rows.next()).isTrue(); } @@ -363,14 +363,14 @@ public final class WorkspaceRecoveryContract { } } - static final class Fixture { - final JdbcRuntimeBindingRepository bindings; - final JdbcRuntimeSessionRepository sessions; - final JdbcToolExecutionRepository executions; - final SessionRecord session; - final String tenant = "recovery-" + UUID.randomUUID(); + public static final class Fixture { + public final JdbcRuntimeBindingRepository bindings; + public final JdbcRuntimeSessionRepository sessions; + public final JdbcToolExecutionRepository executions; + public final SessionRecord session; + public final String tenant = "recovery-" + UUID.randomUUID(); - Fixture(DataSource source, JdbcTemplate jdbc, ManagedAgentStore store) { + public Fixture(DataSource source, JdbcTemplate jdbc, ManagedAgentStore store) { bindings = new JdbcRuntimeBindingRepository(source, new AesGcmSecretProtector("test", new byte[32])); sessions = new JdbcRuntimeSessionRepository(source); executions = new JdbcToolExecutionRepository(source); @@ -388,11 +388,11 @@ public final class WorkspaceRecoveryContract { session = store.findSessionById(created.sessionId()).orElseThrow(); } - Runtime runtime(String name) { + public Runtime runtime(String name) { return runtime(name, session.sessionId()); } - Runtime runtime(String name, String harnessSessionId) { + public Runtime runtime(String name, String harnessSessionId) { var scope = new RuntimeScope(tenant, "workspace", "1", "/original-storage", WorkspaceExecutionProfile.CAPABILITY_DIGEST, "session"); var request = new RuntimeProvisionRequest(scope, tenant + name, "local-process", "storage"); @@ -410,19 +410,19 @@ public final class WorkspaceRecoveryContract { acquiring.withState(RuntimeSessionRecord.State.READY, Instant.now()))); } - RuntimeBindingRecord lose(RuntimeBindingRecord binding) { + public RuntimeBindingRecord lose(RuntimeBindingRecord binding) { return bindings.compareAndSet(binding, binding.withRecoveryEvidence( evidence(binding, RuntimeRecoveryEvidence.Fact.JOURNAL_LOST), evidence(binding, RuntimeRecoveryEvidence.Fact.WRITERS_STOPPED), Instant.now())); } } - static RuntimeRecoveryEvidence evidence(RuntimeBindingRecord binding, RuntimeRecoveryEvidence.Fact fact) { + public static RuntimeRecoveryEvidence evidence(RuntimeBindingRecord binding, RuntimeRecoveryEvidence.Fact fact) { var seed = binding.getProvisionSeed(); return new RuntimeRecoveryEvidence(seed.getProvisionRequestId() + fact, fact, "test-reboot", Instant.now(), "original-test-host-boot", seed.getProvisionRequestId(), seed.getProvisionalRuntimeId(), seed.getGatewayIncarnation(), seed.getLeaseId(), seed.getEpoch(), binding.getResourceHandle()); } - record Runtime(RuntimeBindingRecord binding, RuntimeSessionRecord session) { } + public record Runtime(RuntimeBindingRecord binding, RuntimeSessionRecord session) { } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeInstallProbeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeInstallProbeTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeInstallProbeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeInstallProbeTest.java index 2cc4ced3a79..e02dd0062fc 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeInstallProbeTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeInstallProbeTest.java @@ -25,7 +25,7 @@ import static org.mockito.Mockito.when; * acquisition accepts and refuse every substitution early, while claiming * nothing and contacting no worker. */ -class WorkspaceRuntimeInstallProbeTest { +public class WorkspaceRuntimeInstallProbeTest { private static final String TENANT = "probe-tenant"; private static final String STORAGE = "probe-storage"; @@ -33,8 +33,7 @@ class WorkspaceRuntimeInstallProbeTest { private Path temporary; @Test - void acceptsNestedAndRefusesMissingFileAndAliasedDirectories() - throws Exception { + public void acceptsNestedAndRefusesMissingFileAndAliasedDirectories() throws Exception { Path root = Files.createDirectory(temporary.resolve("mount")) .toRealPath(); Files.createDirectories(root.resolve("services/api")); @@ -68,7 +67,7 @@ class WorkspaceRuntimeInstallProbeTest { } @Test - void refusesUnknownStorageAndAMountWhoseIdentityMoved() throws Exception { + public void refusesUnknownStorageAndAMountWhoseIdentityMoved() throws Exception { Path root = Files.createDirectory(temporary.resolve("mount")) .toRealPath(); Files.createDirectories(root.resolve("services/api")); @@ -98,7 +97,7 @@ class WorkspaceRuntimeInstallProbeTest { // target directory differs from the current one here, so no current- // binding call can hide behind it. @Test - void aGuardedMountVerifiesTheProbeTarget() throws Exception { + public void aGuardedMountVerifiesTheProbeTarget() throws Exception { Path root = Files.createDirectory(temporary.resolve("mount")) .toRealPath(); Files.createDirectories(root.resolve("services/api")); @@ -109,9 +108,9 @@ class WorkspaceRuntimeInstallProbeTest { "jdbc:h2:mem:probe-guarded;MODE=MySQL;DB_CLOSE_DELAY=-1", "sa", ""); WorkspaceRuntimeResolver guarded = new WorkspaceRuntimeResolver( - null, new WorkspaceExecutionStore(new JdbcTemplate( + null, com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(new JdbcTemplate( dataSource), - new DataSourceTransactionManager(dataSource), guard), + new DataSourceTransactionManager(dataSource), guard)), mountProperties(root)); ContextBinding bound = binding("services/api"); @@ -124,7 +123,7 @@ class WorkspaceRuntimeInstallProbeTest { // against the current binding (would permanently refuse the escape). org.mockito.Mockito.verifyNoMoreInteractions(guard); - org.mockito.Mockito.doThrow(WorkspaceExecutionStore.unavailable()) + org.mockito.Mockito.doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()) .when(guard).verifyProbe( org.mockito.ArgumentMatchers.any( ContextBinding.class)); @@ -159,7 +158,7 @@ class WorkspaceRuntimeInstallProbeTest { // line at the origin class and let a vanished mount wedge the // Session against session_context_busy with unbounded retries. @Test - void aVanishedTargetOrMountRootRefusesTerminally() throws Exception { + public void aVanishedTargetOrMountRootRefusesTerminally() throws Exception { Path root = Files.createDirectory(temporary.resolve("mount")) .toRealPath(); Files.createDirectories(root.resolve("gone")); @@ -180,7 +179,7 @@ class WorkspaceRuntimeInstallProbeTest { // POSIX. Collapsing the catch(IOException) arm into unavailable() // turns this red. @Test - void aMomentaryIoFailureClassifiesRetryable() throws Exception { + public void aMomentaryIoFailureClassifiesRetryable() throws Exception { org.junit.jupiter.api.Assumptions.assumeTrue(java.nio.file .FileSystems.getDefault().supportedFileAttributeViews() .contains("posix")); @@ -203,7 +202,7 @@ class WorkspaceRuntimeInstallProbeTest { // Session whose present directory is already gone must still be // movable — that is the escape this feature exists for. @Test - void theGuardVerifiesTheCandidateBindingNotTheDestroyedCurrentOne() + public void theGuardVerifiesTheCandidateBindingNotTheDestroyedCurrentOne() throws Exception { Path root = Files.createDirectory(temporary.resolve("mount")) .toRealPath(); @@ -216,9 +215,9 @@ class WorkspaceRuntimeInstallProbeTest { "jdbc:h2:mem:probe-candidate;MODE=MySQL;DB_CLOSE_DELAY=-1", "sa", ""); WorkspaceRuntimeResolver guarded = new WorkspaceRuntimeResolver( - null, new WorkspaceExecutionStore(new JdbcTemplate( + null, com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(new JdbcTemplate( dataSource), - new DataSourceTransactionManager(dataSource), guard), + new DataSourceTransactionManager(dataSource), guard)), mountProperties(root)); assertThatCode(() -> guarded.verifyInstallable(binding("gone"), @@ -237,7 +236,7 @@ class WorkspaceRuntimeInstallProbeTest { // accepted. uid-0 hosts bypass POSIX checks, so the gate measures the // seal's own effect instead of trusting attribute-view support. @Test - void theProbeRefusesAnUnreadableOrUnsearchableTarget() throws Exception { + public void theProbeRefusesAnUnreadableOrUnsearchableTarget() throws Exception { org.junit.jupiter.api.Assumptions.assumeTrue(java.nio.file .FileSystems.getDefault().supportedFileAttributeViews() .contains("posix")); @@ -290,8 +289,8 @@ class WorkspaceRuntimeInstallProbeTest { var dataSource = new DriverManagerDataSource( "jdbc:h2:mem:probe;MODE=MySQL;DB_CLOSE_DELAY=-1", "sa", ""); return new WorkspaceRuntimeResolver(null, - new WorkspaceExecutionStore(new JdbcTemplate(dataSource), - new DataSourceTransactionManager(dataSource)), + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(new JdbcTemplate(dataSource), + new DataSourceTransactionManager(dataSource))), mountProperties(root)); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolutionPivotTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolutionPivotTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolutionPivotTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolutionPivotTest.java index 49e8b995e9e..85979787112 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolutionPivotTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeResolutionPivotTest.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.managedagent.workspace.runtime; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.StoreModels.OperationRecord; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; @@ -60,11 +60,11 @@ class WorkspaceRuntimeResolutionPivotTest { properties.getRuntimeBroker().setWorkspaceMounts(List.of( new ManagedAgentProperties.RuntimeBroker.WorkspaceMount( TENANT, STORAGE, root.toString()))); - ManagedAgentStore store = new ManagedAgentStore(jdbc, - new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties); + ManagedAgentStore store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, + new ObjectMapper(), Clock.systemUTC(), ignored -> {}, new ManagedWorkspaceRegistry(jdbc), properties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); WorkspaceRuntimeResolver resolver = new WorkspaceRuntimeResolver( - store, new WorkspaceExecutionStore(jdbc, - new DataSourceTransactionManager(dataSource)), + store, com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(jdbc, + new DataSourceTransactionManager(dataSource))), properties); String sessionId = new TransactionTemplate( diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTest.java index 6f48318dbac..65bfac52dd8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTest.java @@ -2,19 +2,15 @@ package com.alibaba.qwen.code.managedagent.workspace.runtime; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties.RuntimeBroker.WorkspaceMount; +import com.alibaba.qwen.code.managedagent.contracts.workspace.WorkspaceSelection; import com.alibaba.qwen.code.managedagent.model.StoreModels.SessionRecord; import com.alibaba.qwen.code.managedagent.persistence.ManagedAgentStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceOperatorRecoveryStore; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageGuard; -import com.alibaba.qwen.code.managedagent.workspace.WorkspaceSelection; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; @@ -25,10 +21,14 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaProjection; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -464,9 +464,9 @@ class WorkspaceRuntimeTest { void authorizationRefusesAnUnverifiedPhysicalMount() { SessionRecord session = createSession("storage", "."); WorkspaceStorageGuard guard = mock(WorkspaceStorageGuard.class); - var checked = new WorkspaceExecutionStore(new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource), - new DataSourceTransactionManager(dataSource), guard); - doThrow(WorkspaceExecutionStore.unavailable()).when(guard).verify(session.workspace()); + var checked = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource), + new DataSourceTransactionManager(dataSource), guard)); + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(guard).verify(session.workspace()); assertUnavailable(() -> checked.authorize(session)); checked.authorizePassiveAttachment(session); verify(guard).verify(session.workspace()); @@ -579,7 +579,7 @@ class WorkspaceRuntimeTest { var binding = session.workspace(); RuntimeSessionRecord first = holder(session, "first"); RuntimeSessionRecord second = holder(secondSession, "second"); - var otherClient = new WorkspaceExecutionStore(new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource), new DataSourceTransactionManager(dataSource)); + var otherClient = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource), new DataSourceTransactionManager(dataSource))); CyclicBarrier start = new CyclicBarrier(2); try (var pool = Executors.newFixedThreadPool(2)) { var one = pool.submit(() -> contend(authority, binding, first, start)); @@ -885,7 +885,7 @@ class WorkspaceRuntimeTest { SessionRecord session = createSession("storage", "."); var fixture = transport(session); when(fixture.http().installContext(any(), any(), any(), any())) - .thenThrow(WorkspaceExecutionStore.unavailable()); + .thenThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()); assertThatThrownBy(() -> fixture.transport().acquire(fixture.lease(), fixture.record().getSession())) .isInstanceOfSatisfying(RuntimeBrokerException.class, error -> assertThat(error.getCode()).isEqualTo("runtime_session_acquire_failed")); @@ -1056,9 +1056,9 @@ class WorkspaceRuntimeTest { var runtimeSession = fixture.record().getSession(); authority.claim(session.workspace(), fixture.record()); WorkspaceStorageGuard guard = mock(WorkspaceStorageGuard.class); - var checked = new WorkspaceExecutionStore(new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource), - new DataSourceTransactionManager(dataSource), guard); - doThrow(WorkspaceExecutionStore.unavailable()).when(guard).verify(session.workspace()); + var checked = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(new com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate(dataSource), + new DataSourceTransactionManager(dataSource), guard)); + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(guard).verify(session.workspace()); var transport = new WorkspaceRuntimeTransport(fixture.http(), resolver(session, temp.toRealPath()), checked, fixture.bindings(), new JdbcRuntimeSessionRepository(dataSource)); assertUnavailable(() -> transport.control(fixture.lease(), runtimeSession, mcpControl(session, "mcp-configure"))); @@ -1143,8 +1143,8 @@ class WorkspaceRuntimeTest { authority.claim(session.workspace(), fixture.record()); var closeProperties = new ManagedAgentProperties(); closeProperties.getHarness().setWorkspaceFilesEnabled(true); - var closeStore = new ManagedAgentStore(jdbc, new ObjectMapper(), java.time.Clock.systemUTC(), ignored -> {}, - new com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry(jdbc), closeProperties); + var closeStore = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedAgentStore(jdbc, new ObjectMapper(), java.time.Clock.systemUTC(), ignored -> {}, + new com.alibaba.qwen.code.managedagent.persistence.ManagedWorkspaceRegistry(jdbc), closeProperties, new com.alibaba.qwen.code.managedagent.harness.SdkHarnessRules())); new TransactionTemplate(new DataSourceTransactionManager(dataSource)).execute(status -> { var admittedClose = closeStore.beginWorkspaceClose(session.tenantId(), session.sessionId(), "actor", "actor", "close", "digest", true); return closeStore.claimOperation(session.tenantId(), session.sessionId(), admittedClose.operation().operationId(), diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransportTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransportTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransportTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransportTest.java index bb26accff24..c1aece2ce74 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransportTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/workspace/runtime/WorkspaceRuntimeTransportTest.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.managedagent.workspace.runtime; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; @@ -14,8 +15,8 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiProtocol; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; @@ -24,7 +25,6 @@ import java.math.BigDecimal; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Path; import java.time.Instant; import java.util.LinkedHashMap; import java.util.Map; @@ -46,7 +46,7 @@ class WorkspaceRuntimeTransportTest { void invalidRemoteCwdIsRejectedBeforeClaimingStorageOrInstallingContext() throws Exception { var fixture = sshAcquisitionFixture(); var binding = fixture.resolver().savedBinding(fixture.session().getHarnessSessionId()); - doThrow(WorkspaceExecutionStore.unavailable()).when(fixture.resolver()) + doThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()).when(fixture.resolver()) .verifyInstallable(binding, binding.getCwdRelative()); unavailable(() -> fixture.transport().acquire(fixture.lease(), fixture.session())); verifyNoInteractions(fixture.ownership(), fixture.http()); @@ -133,7 +133,7 @@ class WorkspaceRuntimeTransportTest { @Test void dedicatedWorkspaceExemptionDoesNotOpenTheExistingGenericRoutes() throws Exception { var fixture = fixture(); - when(fixture.resolver().resolve(any(), any())).thenThrow(WorkspaceExecutionStore.unavailable()); + when(fixture.resolver().resolve(any(), any())).thenThrow(com.alibaba.qwen.code.managedagent.repository.WorkspaceExecutionStore.unavailable()); Map reference = Map.of("runtimeProtocol", 3, "callId", "original"); unavailable(() -> fixture.transport().statusV3(fixture.lease(), fixture.session(), reference, 0)); unavailable(() -> fixture.transport().cancelV3(fixture.lease(), fixture.session(), reference)); @@ -294,7 +294,7 @@ class WorkspaceRuntimeTransportTest { private static Fixture fixture() throws Exception { var fixture = ManagedCsiProtocol.parseAcknowledgement(Files.readAllBytes( - Path.of("../cli/src/serve/contracts/managed-csi-worker-ack-v1.fixtures.json"))); + VerificationFixtures.moduleRoot().resolve("../cli/src/serve/contracts/managed-csi-worker-ack-v1.fixtures.json"))); var context = with(map(map(fixture.get("boot")).get("context")), "capabilityDigest", WorkspaceExecutionProfile.CAPABILITY_DIGEST); var boot = with(map(fixture.get("boot")), "context", context); var request = with(map(fixture.get("request")), "context", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerProcess.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerProcess.java similarity index 88% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerProcess.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerProcess.java index 714f40ac38f..f518bd21dd7 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerProcess.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerProcess.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.runtimebroker; - import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; +import com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig; import com.alibaba.qwen.code.runtimebroker.provisioning.FaultGateBroker; import java.io.BufferedReader; @@ -21,18 +21,18 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; /** A Broker JVM subprocess driven through {@link FaultGateBroker}. */ -final class BrokerProcess implements AutoCloseable { +public final class BrokerProcess implements AutoCloseable { private static final Duration START_TIMEOUT = Duration.ofSeconds(60); private static final Duration CALL_TIMEOUT = Duration.ofSeconds(120); - record Reply(boolean ok, Object value, int status, String code, + public record Reply(boolean ok, Object value, int status, String code, boolean retryable, String message) { - JSONObject object() { + public JSONObject object() { requireOk(); return (JSONObject) value; } - Reply requireOk() { + public Reply requireOk() { if (!ok) { throw new AssertionError("Broker call failed: " + status + " " + code + " " + message); @@ -70,7 +70,7 @@ final class BrokerProcess implements AutoCloseable { reader.start(); } - static BrokerProcess start(String name, Path config, Path log, Path home) + public static BrokerProcess start(String name, Path config, Path log, Path home) throws IOException, InterruptedException { ProcessBuilder builder = new ProcessBuilder( Path.of(System.getProperty("java.home"), "bin", "java") @@ -92,16 +92,16 @@ final class BrokerProcess implements AutoCloseable { return broker; } - Reply warm(String harness) { + public Reply warm(String harness) { return call("warm", Map.of("harness", harness)); } - Reply acquire(String harness, String runtimeSession) { + public Reply acquire(String harness, String runtimeSession) { return call("acquire", Map.of("harness", harness, "runtimeSession", runtimeSession)); } - Reply create(String harness, String runtimeSession, String key, + public Reply create(String harness, String runtimeSession, String key, FaultGateRig.ToolCall toolCall) { return call("create", Map.of("harness", harness, "runtimeSession", runtimeSession, "key", key, @@ -109,63 +109,63 @@ final class BrokerProcess implements AutoCloseable { } /** Creates and dispatches in one step, never reserving first. */ - Reply createImmediate(String harness, String runtimeSession, String key, + public Reply createImmediate(String harness, String runtimeSession, String key, FaultGateRig.ToolCall toolCall) { return call("createImmediate", Map.of("harness", harness, "runtimeSession", runtimeSession, "key", key, "reference", toolCall.immediateReference())); } - Reply get(String harness, String runtimeSession, String execution) { + public Reply get(String harness, String runtimeSession, String execution) { return executionCall("get", harness, runtimeSession, execution); } - Reply cancel(String harness, String runtimeSession, String execution) { + public Reply cancel(String harness, String runtimeSession, String execution) { return executionCall("cancel", harness, runtimeSession, execution); } - Reply reconcile(String harness, String runtimeSession, + public Reply reconcile(String harness, String runtimeSession, String execution) { return executionCall("reconcile", harness, runtimeSession, execution); } - Reply release(String harness, String runtimeSession) { + public Reply release(String harness, String runtimeSession) { return call("release", Map.of("harness", harness, "runtimeSession", runtimeSession)); } - long pid() { + public long pid() { return process.pid(); } /** The worker processes this Broker started and that are still alive. */ - List workers() { + public List workers() { return process.children().filter(ProcessTrees::running).toList(); } /** The whole tree below this Broker, for cleanup after a kill. */ - List descendants() { + public List descendants() { return process.descendants().toList(); } /** SIGKILLs the Broker JVM alone; its workers become orphans. */ - void kill() throws InterruptedException { + public void kill() throws InterruptedException { process.destroyForcibly(); if (!process.waitFor(30, TimeUnit.SECONDS)) { throw new AssertionError("Broker " + name + " survived SIGKILL"); } } - void pause() { + public void pause() { ProcessTrees.signal(process.pid(), "STOP"); } - void resume() { + public void resume() { ProcessTrees.signal(process.pid(), "CONT"); } - String logTail() { + public String logTail() { try { List lines = Files.readAllLines(log); return String.join("\n", lines.subList( diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerRenewalPinningTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerRenewalPinningTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerRenewalPinningTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerRenewalPinningTest.java index 359fda02401..4fe01272029 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerRenewalPinningTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerRenewalPinningTest.java @@ -1,6 +1,12 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.assertTrue; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import java.net.URI; import java.time.Duration; @@ -13,9 +19,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; + +import static org.junit.jupiter.api.Assertions.assertTrue; // Witness for the BindingRenewal half of #13388: the packaged // stack measured in #13365 pinned its carriers at diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerValuesTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerValuesTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerValuesTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerValuesTest.java index 28966577422..cc456357de8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerValuesTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerValuesTest.java @@ -1,5 +1,17 @@ package com.alibaba.qwen.code.runtimebroker; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -8,17 +20,6 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONReader; -import com.alibaba.fastjson2.JSONWriter; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; -import org.junit.jupiter.api.Test; - class BrokerValuesTest { @Test void wellFormedJsonFindsALoneSurrogateInAnyStringOrKey() { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerVirtualThreadPinningTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerVirtualThreadPinningTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerVirtualThreadPinningTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerVirtualThreadPinningTest.java index c95dae584e6..33e839525ee 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerVirtualThreadPinningTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/BrokerVirtualThreadPinningTest.java @@ -1,6 +1,12 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.assertTrue; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import java.net.URI; import java.time.Clock; @@ -18,9 +24,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; + +import static org.junit.jupiter.api.Assertions.assertTrue; // Broker-side sibling of HarnessEventStreamPinningTest for // https://github.com/QwenLM/qwen-code/issues/13333 : in the packaged @@ -68,7 +73,7 @@ class BrokerVirtualThreadPinningTest { int callerCount = carriers + 2; sessions = new LatchedSessionRepository( new InMemoryRuntimeSessionRepository(), carriers); - service = new RuntimeBrokerService( + service = com.alibaba.qwen.code.runtimebroker.service.BrokerServiceTestAccess.create( ignored -> CompletableFuture.completedFuture(SCOPE), new InlineProvisioner(), new InlineTransport(), new InMemoryRuntimeBindingRepository(clock, () -> "binding"), diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CarrierCount.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CarrierCount.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CarrierCount.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CarrierCount.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CarrierCountTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CarrierCountTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CarrierCountTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CarrierCountTest.java index b242c2636e3..21bdd32d388 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CarrierCountTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CarrierCountTest.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + // Pins CarrierCount's base-10 read of the scheduler property: the JDK // parses jdk.virtualThreadScheduler.parallelism base-10 while // Integer.getInteger resolves through Integer.decode, so "0100" must diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ConcurrencyStorageFaultGateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ConcurrencyStorageFaultGateTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ConcurrencyStorageFaultGateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ConcurrencyStorageFaultGateTest.java index d30d9579b32..01cc96a6e64 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ConcurrencyStorageFaultGateTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ConcurrencyStorageFaultGateTest.java @@ -1,19 +1,20 @@ package com.alibaba.qwen.code.runtimebroker; - -import static com.alibaba.qwen.code.runtimebroker.FaultGateRig.HARNESS; -import static com.alibaba.qwen.code.runtimebroker.FaultGateRig.SESSION; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.time.Duration; -import java.util.List; +import com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import java.time.Duration; +import java.util.List; + +import static com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig.HARNESS; +import static com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig.SESSION; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + /** FG4: two Brokers on one database, and a database that goes away. */ @Tag("fault-gate") class ConcurrencyStorageFaultGateTest { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ContextInstallationFaultGateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ContextInstallationFaultGateTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ContextInstallationFaultGateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ContextInstallationFaultGateTest.java index bd347c65812..de2f69b9325 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ContextInstallationFaultGateTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ContextInstallationFaultGateTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.runtimebroker; - import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; @@ -19,8 +19,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import static com.alibaba.qwen.code.runtimebroker.FaultGateRig.HARNESS; -import static com.alibaba.qwen.code.runtimebroker.FaultGateRig.SESSION; +import static com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig.HARNESS; +import static com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig.SESSION; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProofTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProofTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProofTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProofTest.java index 469e169f56d..e1c21d87cb1 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProofTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeActivationProofTest.java @@ -1,13 +1,11 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.Test; + import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Base64; @@ -15,7 +13,10 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.function.Function; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class CsiNativeActivationProofTest { private static final ObjectMapper JSON = new ObjectMapper(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFileHistoryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFileHistoryTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFileHistoryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFileHistoryTest.java index 730f2580cb7..d1f6757d2b8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFileHistoryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFileHistoryTest.java @@ -1,21 +1,22 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; - import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.Test; + import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; /** Structural transition units; manual prefixes do not qualify a SQL owner or physical preimage. */ class CsiNativeFileHistoryTest { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFinalOutputProofTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFinalOutputProofTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFinalOutputProofTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFinalOutputProofTest.java index 83394a543d9..cc12539359a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFinalOutputProofTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeFinalOutputProofTest.java @@ -1,15 +1,11 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.Test; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; @@ -19,7 +15,12 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class CsiNativeFinalOutputProofTest { private static final ObjectMapper JSON = new ObjectMapper(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeTextProofTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeTextProofTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeTextProofTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeTextProofTest.java index bb240bd200e..8b56de3b0ed 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeTextProofTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeTextProofTest.java @@ -1,15 +1,13 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + import java.io.IOException; import java.util.Base64; import java.util.HashMap; @@ -17,9 +15,12 @@ import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.function.Supplier; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class CsiNativeTextProofTest { private static final ObjectMapper JSON = new ObjectMapper(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolResultProofTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolResultProofTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolResultProofTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolResultProofTest.java index 5c395a24da9..9977d0b06c5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolResultProofTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolResultProofTest.java @@ -1,11 +1,11 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + /** Pure conversion coverage; this test does not exercise SQL admission. */ class CsiNativeToolResultProofTest { private static final ObjectMapper JSON = new ObjectMapper(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiOwnershipRevisionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiOwnershipRevisionTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiOwnershipRevisionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiOwnershipRevisionTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiRetirementSealTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiRetirementSealTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiRetirementSealTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiRetirementSealTest.java index ed9af62ff3f..0db0421b595 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiRetirementSealTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiRetirementSealTest.java @@ -1,6 +1,8 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DelegatingBindingRepository.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/DelegatingBindingRepository.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DelegatingBindingRepository.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/DelegatingBindingRepository.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DelegatingToolExecutionRepository.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/DelegatingToolExecutionRepository.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DelegatingToolExecutionRepository.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/DelegatingToolExecutionRepository.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DriverManagerDataSource.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/DriverManagerDataSource.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DriverManagerDataSource.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/DriverManagerDataSource.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableLocalRuntimeFaultGateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableLocalRuntimeFaultGateTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableLocalRuntimeFaultGateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableLocalRuntimeFaultGateTest.java index bdd0689b848..c296c9106ab 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableLocalRuntimeFaultGateTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableLocalRuntimeFaultGateTest.java @@ -1,19 +1,20 @@ package com.alibaba.qwen.code.runtimebroker; - -import static com.alibaba.qwen.code.runtimebroker.FaultGateRig.HARNESS; -import static com.alibaba.qwen.code.runtimebroker.FaultGateRig.SESSION; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import java.nio.file.Files; import java.time.Duration; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; + +import static com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig.HARNESS; +import static com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig.SESSION; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** Real JVMs, real worker and SQL; portable identity injection is not reboot evidence. */ @Tag("fault-gate") diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableRuntimeRecoveryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableRuntimeRecoveryTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableRuntimeRecoveryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableRuntimeRecoveryTest.java index 69e5214d541..47421785c62 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableRuntimeRecoveryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/DurableRuntimeRecoveryTest.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; +import org.junit.jupiter.api.Test; import java.net.URI; import java.time.Clock; @@ -20,7 +20,12 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class DurableRuntimeRecoveryTest { private static final RuntimeScope SCOPE = new RuntimeScope("tenant", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ExecutionTakeoverContract.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ExecutionTakeoverContract.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ExecutionTakeoverContract.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ExecutionTakeoverContract.java index bbee57139d2..4680a7ed737 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ExecutionTakeoverContract.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ExecutionTakeoverContract.java @@ -12,11 +12,11 @@ import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; -final class ExecutionTakeoverContract { +public final class ExecutionTakeoverContract { private ExecutionTakeoverContract() { } - static void verify(ToolExecutionRepository repository, String prefix) { + public static void verify(ToolExecutionRepository repository, String prefix) { RuntimeScope scope = new RuntimeScope("tenant", "workspace", "generation", "/workspace", "capability", "session"); RuntimeSessionRecord session = new RuntimeSessionRecord( @@ -86,7 +86,7 @@ final class ExecutionTakeoverContract { * hold row still counts: the row's settle is not the Runtime Session's * release (the release-state filter lives in the service, tested there — * a settled hold whose Session was never released is still owed). */ - static void verifyHoldRowsByHarnessSession(ToolExecutionRepository repository, String prefix) { + public static void verifyHoldRowsByHarnessSession(ToolExecutionRepository repository, String prefix) { RuntimeScope scope = new RuntimeScope("tenant", "workspace", "generation", "/workspace", "capability", "session"); String harness = prefix + "-harness"; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ExternalRuntimeWorkerFaultGateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ExternalRuntimeWorkerFaultGateTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ExternalRuntimeWorkerFaultGateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ExternalRuntimeWorkerFaultGateTest.java index 5a39521f4f1..4ad27dd9142 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ExternalRuntimeWorkerFaultGateTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ExternalRuntimeWorkerFaultGateTest.java @@ -1,8 +1,12 @@ package com.alibaba.qwen.code.runtimebroker; - +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalHttpRuntimeClient; import com.alibaba.qwen.code.runtimebroker.provisioning.ExternalRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpServer; import org.junit.jupiter.api.Tag; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FakeAttestationWorkerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FakeAttestationWorkerTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FakeAttestationWorkerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FakeAttestationWorkerTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FakeKubernetesRuntimeClient.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FakeKubernetesRuntimeClient.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FakeKubernetesRuntimeClient.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FakeKubernetesRuntimeClient.java index 69fe580a319..91935749447 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FakeKubernetesRuntimeClient.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FakeKubernetesRuntimeClient.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.runtimebroker; import com.alibaba.fastjson2.JSON; -import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesRuntimeClient; +import com.alibaba.qwen.code.runtimebroker.spi.KubernetesRuntimeClient; import java.util.LinkedHashMap; import java.util.List; @@ -72,7 +72,7 @@ public final class FakeKubernetesRuntimeClient implements KubernetesRuntimeClien return resource + "/" + namespace + "/" + name; } - static Map mutable(Map value) { + public static Map mutable(Map value) { return JSON.parseObject(JsonCodec.encode(value)); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateControlTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateControlTest.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateControlTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateControlTest.java index 32075c71189..dac45839248 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateControlTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateControlTest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.runtimebroker; - -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; @@ -10,8 +10,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import static com.alibaba.qwen.code.runtimebroker.FaultGateRig.HARNESS; -import static com.alibaba.qwen.code.runtimebroker.FaultGateRig.SESSION; +import static com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig.HARNESS; +import static com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig.SESSION; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateTransport.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateTransport.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateTransport.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateTransport.java index d45a587944f..fdb39fc5dba 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateTransport.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateTransport.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.runtimebroker; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; import java.nio.charset.StandardCharsets; import java.util.Map; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultProxy.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultProxy.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultProxy.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultProxy.java index 929f5a122e3..9b1382cbc91 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultProxy.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultProxy.java @@ -34,10 +34,10 @@ import java.util.stream.Collectors; * a gate can count the transport calls a Broker made, including those whose * answer never came back. */ -final class FaultProxy implements AutoCloseable { +public final class FaultProxy implements AutoCloseable { private static final int HEAD_LIMIT = 64 * 1024; - enum Action { + public enum Action { /** Forward the request and deliver the response. */ PASS, /** Forward the request, read the response, then close silently. */ @@ -53,7 +53,7 @@ final class FaultProxy implements AutoCloseable { } /** One scheduled fault; hold faults report when they caught a request. */ - static final class Fault { + public static final class Fault { private final Action action; private final Duration delay; private final CountDownLatch held = new CountDownLatch(1); @@ -66,22 +66,22 @@ final class FaultProxy implements AutoCloseable { this.delay = delay; } - static Fault of(Action action) { + public static Fault of(Action action) { return new Fault(action, Duration.ZERO); } - static Fault delay(Duration delay) { + public static Fault delay(Duration delay) { return new Fault(Action.DELAY, delay); } - void awaitHeld(Duration timeout) throws InterruptedException { + public void awaitHeld(Duration timeout) throws InterruptedException { if (!held.await(timeout.toMillis(), TimeUnit.MILLISECONDS)) { throw new AssertionError(action + " fault caught nothing in " + timeout); } } - void awaitDelivered(Duration timeout) throws InterruptedException { + public void awaitDelivered(Duration timeout) throws InterruptedException { if (!delivered.await(timeout.toMillis(), TimeUnit.MILLISECONDS)) { throw new AssertionError(action + " fault delivered nothing in " + timeout); @@ -89,12 +89,12 @@ final class FaultProxy implements AutoCloseable { } /** PASS delivers the held exchange; DROP or RESET ends it. */ - void release(Action outcome) { + public void release(Action outcome) { release.complete(outcome); } } - record Exchange(String operation, long arrivedNanos) { + public record Exchange(String operation, long arrivedNanos) { } private final ServerSocket server; @@ -103,47 +103,47 @@ final class FaultProxy implements AutoCloseable { private final List exchanges = new CopyOnWriteArrayList<>(); private final Set open = ConcurrentHashMap.newKeySet(); - FaultProxy() throws IOException { + public FaultProxy() throws IOException { server = new ServerSocket(0, 50, InetAddress.getLoopbackAddress()); Thread acceptor = new Thread(this::accept, "fault-proxy-accept"); acceptor.setDaemon(true); acceptor.start(); } - int port() { + public int port() { return server.getLocalPort(); } /** Queues a fault for the next request of {@code operation}. */ - Fault schedule(String operation, Fault fault) { + public Fault schedule(String operation, Fault fault) { faults.computeIfAbsent(operation, ignored -> new ConcurrentLinkedQueue<>()).add(fault); scheduled.add(fault); return fault; } - Fault schedule(String operation, Action action) { + public Fault schedule(String operation, Action action) { return schedule(operation, Fault.of(action)); } - long count(String operation) { + public long count(String operation) { return exchanges.stream() .filter(exchange -> exchange.operation().equals(operation)) .count(); } - long countSince(long nanos) { + public long countSince(long nanos) { return exchanges.stream() .filter(exchange -> exchange.arrivedNanos() >= nanos) .count(); } - List exchanges() { + public List exchanges() { return List.copyOf(exchanges); } /** How many requests of each operation arrived, first arrival first. */ - Map counts() { + public Map counts() { return exchanges.stream().collect(Collectors.groupingBy( Exchange::operation, LinkedHashMap::new, Collectors.counting())); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnershipTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnershipTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnershipTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnershipTest.java index 90c23eba281..1e7980ec957 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnershipTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/HarnessOwnershipTest.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.runtimebroker; +import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Test; - class HarnessOwnershipTest { private static final String PROFILE = WorkspaceExecutionProfile.CAPABILITY_DIGEST; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/InMemoryRepositoryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/InMemoryRepositoryTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/InMemoryRepositoryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/InMemoryRepositoryTest.java index 50275f76b23..449009e4187 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/InMemoryRepositoryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/InMemoryRepositoryTest.java @@ -1,12 +1,9 @@ package com.alibaba.qwen.code.runtimebroker; -import static com.alibaba.qwen.code.runtimebroker.ToolExecutionRecordFixtures.withIdentity; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.net.URI; @@ -28,7 +25,14 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; -import org.junit.jupiter.api.Test; + +import static com.alibaba.qwen.code.runtimebroker.ToolExecutionRecordFixtures.withIdentity; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class InMemoryRepositoryTest { @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183AdversarialTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183AdversarialTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183AdversarialTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183AdversarialTest.java index 89e77ba2a79..b4738a3b7a4 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183AdversarialTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183AdversarialTest.java @@ -2,10 +2,14 @@ package com.alibaba.qwen.code.runtimebroker; import com.alibaba.qwen.code.runtimebroker.Issue13183RegressionTest.AttestingTransport; import com.alibaba.qwen.code.runtimebroker.Issue13183RegressionTest.ReclaimProvisioner; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocolTest; import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisionerTest; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocolTest; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.Test; import javax.sql.DataSource; @@ -43,7 +47,6 @@ import static org.junit.jupiter.api.Assertions.fail; */ @org.junit.jupiter.api.Timeout(180) class Issue13183AdversarialTest { - /** * Rounds of one thread admitting an execution while another runs * beginSessionRelease from a second repository stack. Odd rounds release @@ -711,10 +714,4 @@ class Issue13183AdversarialTest { provisioner, new AttestingTransport(), bindings, sessions, executions, owner, lease, lease); } - - - - - - } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183RegressionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183RegressionTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183RegressionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183RegressionTest.java index 88f615b64b1..edb98bf7d7e 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183RegressionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/Issue13183RegressionTest.java @@ -1,13 +1,20 @@ package com.alibaba.qwen.code.runtimebroker; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.http.RuntimeBrokerHttpServer; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocolTest; import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisionerTest; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocolTest; import com.alibaba.qwen.code.runtimebroker.provisioning.StaticRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.security.SecretProtector; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; -import com.alibaba.qwen.code.runtimebroker.transport.RuntimeBrokerHttpServer; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; @@ -1012,12 +1019,12 @@ class Issue13183RegressionTest { assertEquals(List.of(100L, 200L, 400L, 800L, 1600L, 2000L, 2000L, 2000L), IntStream.rangeClosed(0, 7) - .mapToObj(RuntimeBrokerService::v3PollDelayMillis) + .mapToObj(com.alibaba.qwen.code.runtimebroker.service.BrokerServiceTestAccess::pollDelay) .toList()); // The window has a floor but no ceiling, so the attempt count is // unbounded; past the ramp every attempt must sit at the cap. IntStream.rangeClosed(8, 100_000).forEach(attempt -> assertEquals( - 2_000L, RuntimeBrokerService.v3PollDelayMillis(attempt), + 2_000L, com.alibaba.qwen.code.runtimebroker.service.BrokerServiceTestAccess.pollDelay(attempt), "the backoff left the cap at attempt " + attempt)); } @@ -1279,7 +1286,7 @@ class Issue13183RegressionTest { "bindingGeneration", "1")); } }; - return new RuntimeBrokerService( + return com.alibaba.qwen.code.runtimebroker.service.BrokerServiceTestAccess.create( harnessId -> CompletableFuture.completedFuture(SCOPE), new StaticRuntimeProvisioner(new RuntimeLease("instance", URI.create("http://127.0.0.1:1234"), "token", "lease", @@ -1590,7 +1597,7 @@ class Issue13183RegressionTest { UnknownObservationHarness(ToolExecutionRepository executions) throws IOException { - service = new RuntimeBrokerService( + service = com.alibaba.qwen.code.runtimebroker.service.BrokerServiceTestAccess.create( harnessId -> CompletableFuture.completedFuture( new RuntimeScope("tenant-cooldown", "workspace", "generation", "/workspace", "capability", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcPhysicalStorageReleaseTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcPhysicalStorageReleaseTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcPhysicalStorageReleaseTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcPhysicalStorageReleaseTest.java index cd9cb48bbc5..69c6fcbc1f2 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcPhysicalStorageReleaseTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcPhysicalStorageReleaseTest.java @@ -2,9 +2,12 @@ package com.alibaba.qwen.code.runtimebroker; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.WorkspaceExecutionStore; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -34,7 +37,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -class JdbcPhysicalStorageReleaseTest { +public class JdbcPhysicalStorageReleaseTest { @Test void aliasesReleaseOnlyTheirMatchingExecutionMember() throws Exception { var source = new JdbcDataSource(); @@ -45,7 +48,7 @@ class JdbcPhysicalStorageReleaseTest { verify(source); } - static void verify(DataSource source) throws Exception { + public static void verify(DataSource source) throws Exception { ManagedSchema.initialize(source); verifyCleanupOrdering(source, false, false); verifyCleanupOrdering(source, false, true); @@ -159,8 +162,8 @@ class JdbcPhysicalStorageReleaseTest { observing.armed = true; var pending = pool.submit(() -> { if (lost) { - new WorkspaceExecutionStore(new ManagedSchemaJdbcTemplate(observing), - new DataSourceTransactionManager(observing)).releaseLost(original); + com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new WorkspaceExecutionStore(new ManagedSchemaJdbcTemplate(observing), + new DataSourceTransactionManager(observing))).releaseLost(original); } else { fixture.release(); } @@ -189,7 +192,7 @@ class JdbcPhysicalStorageReleaseTest { private final CountDownLatch ownerRequested = new CountDownLatch(1); private volatile boolean armed; - CleanupDataSource(DataSource source) { + public CleanupDataSource(DataSource source) { super(source); } @@ -233,11 +236,11 @@ class JdbcPhysicalStorageReleaseTest { private final RuntimeSessionRecord session; private final String storageKind; - Fixture(DataSource source) throws Exception { + public Fixture(DataSource source) throws Exception { this(source, false); } - Fixture(DataSource source, boolean ssh) throws Exception { + public Fixture(DataSource source, boolean ssh) throws Exception { this.source = source; storageKind = ssh ? "SSH_LOCAL" : "LOCAL"; bindings = diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositoryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositoryTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositoryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositoryTest.java index 38da24ebf95..effbb5e6ba9 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositoryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositoryTest.java @@ -1,10 +1,12 @@ package com.alibaba.qwen.code.runtimebroker; - -import java.util.UUID; -import javax.sql.DataSource; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRepositoryContract; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; +import javax.sql.DataSource; + +import java.util.UUID; + class JdbcRepositoryTest { @Test void repositoriesPreserveTheirContractsAcrossInstances() diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/KubernetesRuntimeWorkerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/KubernetesRuntimeWorkerTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/KubernetesRuntimeWorkerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/KubernetesRuntimeWorkerTest.java index 4646ef16c30..c9e5be9cf8b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/KubernetesRuntimeWorkerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/KubernetesRuntimeWorkerTest.java @@ -1,8 +1,12 @@ package com.alibaba.qwen.code.runtimebroker; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesRuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.provisioning.KubernetesRuntimeProvisionerTest; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.junit.jupiter.api.io.TempDir; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/LocalRebootFaultGateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/LocalRebootFaultGateTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/LocalRebootFaultGateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/LocalRebootFaultGateTest.java index 9cf6de154f7..a545310c86b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/LocalRebootFaultGateTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/LocalRebootFaultGateTest.java @@ -1,13 +1,14 @@ package com.alibaba.qwen.code.runtimebroker; - -import static org.junit.jupiter.api.Assertions.*; +import com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import java.nio.file.Files; import java.util.ArrayList; import java.util.concurrent.TimeUnit; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; + +import static org.junit.jupiter.api.Assertions.*; @Tag("fault-gate") class LocalRebootFaultGateTest { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/LostResponseFaultGateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/LostResponseFaultGateTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/LostResponseFaultGateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/LostResponseFaultGateTest.java index c56acbb4399..bfe5954c750 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/LostResponseFaultGateTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/LostResponseFaultGateTest.java @@ -1,9 +1,8 @@ package com.alibaba.qwen.code.runtimebroker; - import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; @@ -17,8 +16,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import static com.alibaba.qwen.code.runtimebroker.FaultGateRig.HARNESS; -import static com.alibaba.qwen.code.runtimebroker.FaultGateRig.SESSION; +import static com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig.HARNESS; +import static com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig.SESSION; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; @@ -236,7 +235,7 @@ class LostResponseFaultGateTest { rig.scope.getCanonicalCwd(), rig.scope.getCapabilityDigest(), rig.scope.getIsolationClass()); RuntimeProvisionRequest candidate = new RuntimeProvisionRequest( - other, null, LocalProcessRuntimeProvisioner.KIND); + other, null, com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS); assertEquals(RuntimeBindingRecord.State.PROVISIONING, rig.bindings.findOrCreate(candidate).getState()); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/MaintenanceProbeWaiterTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/MaintenanceProbeWaiterTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/MaintenanceProbeWaiterTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/MaintenanceProbeWaiterTest.java index 5f9db91be59..c16dfa75b3b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/MaintenanceProbeWaiterTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/MaintenanceProbeWaiterTest.java @@ -1,8 +1,10 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; +import org.junit.jupiter.api.Test; import java.net.URI; import java.time.Duration; @@ -12,7 +14,10 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; class MaintenanceProbeWaiterTest { @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistoryMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistoryMySqlIT.java similarity index 87% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistoryMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistoryMySqlIT.java index 6f742d735c7..a1850d60ad6 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistoryMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistoryMySqlIT.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.runtimebroker; - import com.alibaba.qwen.code.managedagent.persistence.IsolatedManagedSchemaMySqlTestDatabase; +import com.alibaba.qwen.code.runtimebroker.persistence.ManagedCheckpointHistorySqlTest; import org.junit.jupiter.api.AfterEach; import javax.sql.DataSource; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedContextRecoveryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedContextRecoveryTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedContextRecoveryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedContextRecoveryTest.java index eeea1c271d6..700701a0b95 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedContextRecoveryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedContextRecoveryTest.java @@ -1,11 +1,16 @@ package com.alibaba.qwen.code.runtimebroker; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocolTest; import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocolTest; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.security.SecretProtector; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import com.sun.net.httpserver.HttpServer; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; @@ -14,7 +19,6 @@ import javax.sql.DataSource; import java.net.InetSocketAddress; import java.net.URI; -import java.nio.file.Files; import java.nio.file.Path; import java.sql.Connection; import java.time.Duration; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiAcknowledgementProtocolTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiAcknowledgementProtocolTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiAcknowledgementProtocolTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiAcknowledgementProtocolTest.java index 6e4c407d15e..449c662b095 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiAcknowledgementProtocolTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiAcknowledgementProtocolTest.java @@ -1,5 +1,6 @@ package com.alibaba.qwen.code.runtimebroker; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiProtocol; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; @@ -7,7 +8,6 @@ import org.junit.jupiter.api.Test; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Path; import java.util.LinkedHashMap; import java.util.Map; @@ -19,7 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class ManagedCsiAcknowledgementProtocolTest { public static Map fixtures() throws Exception { return ManagedCsiProtocol.parseAcknowledgement(Files.readAllBytes( - Path.of("../cli/src/serve/contracts/managed-csi-worker-ack-v1.fixtures.json"))); + VerificationFixtures.moduleRoot().resolve("../cli/src/serve/contracts/managed-csi-worker-ack-v1.fixtures.json"))); } @SuppressWarnings("unchecked") @@ -33,7 +33,7 @@ public class ManagedCsiAcknowledgementProtocolTest { return changed; } - static RuntimeScope scope(Map boot) { + public static RuntimeScope scope(Map boot) { var context = map(boot.get("context")); return new RuntimeScope((String) context.get("tenantId"), (String) context.get("workspaceId"), (String) context.get("workspaceGeneration"), (String) context.get("mountRoot"), diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiFilesHttpTransportTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiFilesHttpTransportTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiFilesHttpTransportTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiFilesHttpTransportTest.java index 1a1ae099703..b8325dbca00 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiFilesHttpTransportTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiFilesHttpTransportTest.java @@ -1,9 +1,9 @@ package com.alibaba.qwen.code.runtimebroker; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.alibaba.qwen.code.runtimebroker.transport.CsiNativeReadbackProtocol; import com.alibaba.qwen.code.runtimebroker.transport.CsiNativeReadbackProtocolTest; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiFilesProtocol; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiFilesProtocolTest; import com.sun.net.httpserver.HttpServer; @@ -205,5 +205,4 @@ class ManagedCsiFilesHttpTransportTest { server.stop(0); } } - } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiHttpTransportTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiHttpTransportTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiHttpTransportTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiHttpTransportTest.java index 8aaf918463b..75e45851c23 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiHttpTransportTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCsiHttpTransportTest.java @@ -1,7 +1,8 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiProtocol; import com.sun.net.httpserver.HttpServer; import org.junit.jupiter.api.Test; @@ -9,7 +10,6 @@ import org.junit.jupiter.api.Test; import java.net.InetSocketAddress; import java.net.URI; import java.nio.file.Files; -import java.nio.file.Path; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.Map; @@ -24,7 +24,7 @@ class ManagedCsiHttpTransportTest { @Test void sendsOneAuthenticatedCsiRequestAndRefusesCrossedStorageOrContextOnlyReplies() throws Exception { var fixtures = ManagedContextProtocol.parse(Files.readAllBytes( - Path.of("../cli/src/serve/contracts/managed-csi-v1.fixtures.json"))); + VerificationFixtures.moduleRoot().resolve("../cli/src/serve/contracts/managed-csi-v1.fixtures.json"))); var boot = map(fixtures.get("boot")); var context = map(boot.get("context")); var storage = map(boot.get("storage")); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedExtensionExecutionProfileTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedExtensionExecutionProfileTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedExtensionExecutionProfileTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedExtensionExecutionProfileTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedSchemaMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedSchemaMySqlIT.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedSchemaMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedSchemaMySqlIT.java index 4b155ca6120..6bc7d750586 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedSchemaMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedSchemaMySqlIT.java @@ -2,10 +2,10 @@ package com.alibaba.qwen.code.runtimebroker; import com.alibaba.qwen.code.managedagent.persistence.IsolatedManagedSchemaMySqlTestDatabase; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaRows; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.junit.jupiter.api.Test; import javax.sql.DataSource; @@ -166,7 +166,7 @@ class ManagedSchemaMySqlIT { largeBody[0] = 1; largeBody[largeBody.length - 1] = 2; String resourceKey = - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( prefix, "large-message"); try (var insert = connection.prepareStatement( @@ -177,7 +177,7 @@ class ManagedSchemaMySqlIT { insert.setString(1, resourceKey); insert.setString( 2, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( prefix)); insert.setString(3, prefix + "tenant"); insert.setString(4, prefix + "session"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedToolResultConformanceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedToolResultConformanceTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedToolResultConformanceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedToolResultConformanceTest.java index 933ce6dd7ac..7dc470c260c 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedToolResultConformanceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedToolResultConformanceTest.java @@ -1,12 +1,10 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; +import org.junit.jupiter.api.Test; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.file.Files; @@ -22,7 +20,10 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Pins the managed-tool-result/1 contract (O1a) to the language-neutral diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessCrashFaultGateTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessCrashFaultGateTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessCrashFaultGateTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessCrashFaultGateTest.java index 755bb97232a..65937ffdc60 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessCrashFaultGateTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessCrashFaultGateTest.java @@ -1,7 +1,7 @@ package com.alibaba.qwen.code.runtimebroker; - import com.alibaba.fastjson2.JSONObject; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; @@ -14,8 +14,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import static com.alibaba.qwen.code.runtimebroker.FaultGateRig.HARNESS; -import static com.alibaba.qwen.code.runtimebroker.FaultGateRig.SESSION; +import static com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig.HARNESS; +import static com.alibaba.qwen.code.runtimebroker.persistence.FaultGateRig.SESSION; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessTrees.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessTrees.java similarity index 90% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessTrees.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessTrees.java index aabb8d527d1..7331c075970 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessTrees.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ProcessTrees.java @@ -13,14 +13,14 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** Process-tree helpers for the fault gates. POSIX only. */ -final class ProcessTrees { +public final class ProcessTrees { private static final boolean PROCFS = Files.isDirectory( Path.of("/proc/self")); private ProcessTrees() { } - static Set childPids() { + public static Set childPids() { return ProcessHandle.current().children().map(ProcessHandle::pid) .collect(Collectors.toSet()); } @@ -29,7 +29,7 @@ final class ProcessTrees { * Whether a process still runs. The JDK counts a zombie as alive, and a * killed tool's orphans stay zombies wherever PID 1 does not reap them. */ - static boolean running(ProcessHandle process) { + public static boolean running(ProcessHandle process) { if (!process.isAlive()) { return false; } @@ -52,7 +52,7 @@ final class ProcessTrees { } /** SIGKILLs a process and every descendant, then waits for them. */ - static void kill(ProcessHandle root, Duration timeout) { + public static void kill(ProcessHandle root, Duration timeout) { List tree = new ArrayList<>( root.descendants().toList()); tree.add(root); @@ -60,7 +60,7 @@ final class ProcessTrees { } /** SIGKILLs every process given, then waits until none runs. */ - static void kill(Collection processes, Duration timeout) { + public static void kill(Collection processes, Duration timeout) { processes.forEach(ProcessHandle::destroyForcibly); long deadline = System.nanoTime() + timeout.toNanos(); for (ProcessHandle process : processes) { @@ -80,7 +80,7 @@ final class ProcessTrees { } /** Sends a signal by name, such as STOP or CONT. */ - static void signal(long pid, String name) { + public static void signal(long pid, String name) { try { Process kill = new ProcessBuilder("kill", "-" + name, Long.toString(pid)).inheritIO().start(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ProviderRuntimeTransportTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ProviderRuntimeTransportTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ProviderRuntimeTransportTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ProviderRuntimeTransportTest.java index a46f1a4d60d..cc654506b44 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ProviderRuntimeTransportTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ProviderRuntimeTransportTest.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.provisioning.ProviderRuntimeProtocol; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; -import com.alibaba.qwen.code.runtimebroker.transport.ManagedRuntimeAttestationConformanceTest; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.ManagedRuntimeAttestationConformanceTest; +import com.alibaba.qwen.code.runtimebroker.protocol.ProviderRuntimeProtocol; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.net.httpserver.Headers; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RecoverableProcessProvisioner.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RecoverableProcessProvisioner.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RecoverableProcessProvisioner.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RecoverableProcessProvisioner.java index a01e2c2995b..3718b14a459 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RecoverableProcessProvisioner.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RecoverableProcessProvisioner.java @@ -2,8 +2,8 @@ package com.alibaba.qwen.code.runtimebroker; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerStorageAdmissionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerStorageAdmissionTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerStorageAdmissionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerStorageAdmissionTest.java index 408eb983c46..154b9da3360 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerStorageAdmissionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerStorageAdmissionTest.java @@ -1,6 +1,10 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -249,7 +253,7 @@ class RuntimeBrokerStorageAdmissionTest { private static RuntimeBrokerService broker(RuntimeProvisioner provisioner, InMemoryRuntimeBindingRepository bindings, Clock clock, Duration duration) { - return new RuntimeBrokerService(ignored -> CompletableFuture.completedFuture(SCOPE), provisioner, + return com.alibaba.qwen.code.runtimebroker.service.BrokerServiceTestAccess.create(ignored -> CompletableFuture.completedFuture(SCOPE), provisioner, new HttpRuntimeTransport(), bindings, new InMemoryRuntimeSessionRepository(), new InMemoryToolExecutionRepository(), "coordinator", duration, Duration.ofSeconds(30), clock, () -> "execution"); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeDispatchAdmissionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeDispatchAdmissionTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeDispatchAdmissionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeDispatchAdmissionTest.java index ec4d18c15e1..5e3478ae180 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeDispatchAdmissionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeDispatchAdmissionTest.java @@ -1,6 +1,12 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionMetadataControlTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionMetadataControlTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionMetadataControlTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionMetadataControlTest.java index ac00a1432d2..06f1762d739 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionMetadataControlTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeExtensionMetadataControlTest.java @@ -1,5 +1,9 @@ package com.alibaba.qwen.code.runtimebroker; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.Test; import java.lang.reflect.Proxy; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeMaintenanceRecoveryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeMaintenanceRecoveryTest.java similarity index 88% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeMaintenanceRecoveryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeMaintenanceRecoveryTest.java index 4a777aa9508..f29f7bd0b8e 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeMaintenanceRecoveryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeMaintenanceRecoveryTest.java @@ -1,6 +1,13 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -18,10 +25,10 @@ import java.util.concurrent.atomic.AtomicInteger; import static org.junit.jupiter.api.Assertions.*; -class RuntimeMaintenanceRecoveryTest { +public class RuntimeMaintenanceRecoveryTest { @ParameterizedTest @ValueSource(booleans = {false, true}) - void maintenanceRequiresHolderCleanupAndNeverResolvesOrReprovisions(boolean jdbc) throws Exception { + public void maintenanceRequiresHolderCleanupAndNeverResolvesOrReprovisions(boolean jdbc) throws Exception { if (jdbc) { var source = new JdbcDataSource(); source.setURL("jdbc:h2:mem:maintenance-" + UUID.randomUUID() @@ -35,7 +42,7 @@ class RuntimeMaintenanceRecoveryTest { } } - static void verifyBatches(RuntimeBindingRepository bindings, RuntimeSessionRepository sessions, + public static void verifyBatches(RuntimeBindingRepository bindings, RuntimeSessionRepository sessions, ToolExecutionRepository executions, String id) throws Exception { var fixture = new Fixture(bindings, sessions, executions, id); assertTrue(bindings.findRecoveryCandidates("local-process", null, 100).stream() @@ -137,7 +144,7 @@ class RuntimeMaintenanceRecoveryTest { } } - static RuntimeProvisioner provisioner(java.util.function.Function> cleanup) { + public static RuntimeProvisioner provisioner(java.util.function.Function> cleanup) { return new RuntimeProvisioner() { @Override public String kind() { return "local-process"; } @@ -167,15 +174,15 @@ class RuntimeMaintenanceRecoveryTest { provisioner, transport, bindings, sessions, executions, "maintenance", lease, lease); } - static final class Fixture { - final RuntimeBindingRepository bindings; - final RuntimeSessionRepository sessions; - final ToolExecutionRepository executions; - final String id; - final RuntimeBindingRecord binding; - final RuntimeSessionRecord session; + public static final class Fixture { + public final RuntimeBindingRepository bindings; + public final RuntimeSessionRepository sessions; + public final ToolExecutionRepository executions; + public final String id; + public final RuntimeBindingRecord binding; + public final RuntimeSessionRecord session; - Fixture(RuntimeBindingRepository bindings, RuntimeSessionRepository sessions, + public Fixture(RuntimeBindingRepository bindings, RuntimeSessionRepository sessions, ToolExecutionRepository executions, String id) { this.bindings = bindings; this.sessions = sessions; @@ -196,14 +203,14 @@ class RuntimeMaintenanceRecoveryTest { session = sessions.compareAndSet(acquiring, acquiring.withState(RuntimeSessionRecord.State.READY, Instant.now())); } - void prepare(String call) { + public void prepare(String call) { bindings.admitExecution(sessions, executions, ToolExecutionRecord.prepared(id + "-" + call, id + "-" + call, binding.getBindingId(), binding.getGeneration(), id, session.getRuntimeSessionId(), "turn", call, "digest", Map.of("sessionId", session.getRuntimeSessionId(), "promptId", "turn", "callId", call, "argsDigest", "digest"))); } - RuntimeBindingRecord lose() { + public RuntimeBindingRecord lose() { return bindings.compareAndSet(binding, binding.withRecoveryEvidence( RuntimeRecoveryContract.evidence(binding, RuntimeRecoveryEvidence.Fact.JOURNAL_LOST), RuntimeRecoveryContract.evidence(binding, RuntimeRecoveryEvidence.Fact.WRITERS_STOPPED), Instant.now())); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeedTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeedTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeedTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeedTest.java index 4fdae823f70..ebcb47f75a5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeedTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeProvisionSeedTest.java @@ -1,11 +1,12 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; import java.util.List; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; class RuntimeProvisionSeedTest { @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryContract.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryContract.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryContract.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryContract.java index 28659601db0..e8878b001da 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryContract.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryContract.java @@ -1,7 +1,5 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.*; - import java.net.URI; import java.time.Duration; import java.time.Instant; @@ -13,11 +11,13 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -final class RuntimeRecoveryContract { +import static org.junit.jupiter.api.Assertions.*; + +public final class RuntimeRecoveryContract { private RuntimeRecoveryContract() { } - static void verify(RuntimeBindingRepository bindings, RuntimeSessionRepository sessions, + public static void verify(RuntimeBindingRepository bindings, RuntimeSessionRepository sessions, ToolExecutionRepository executions, String prefix) throws Exception { verifyHistoricalInventory(sessions, prefix + "-history-ids"); for (ToolExecutionRecord.State state : ToolExecutionRecord.State.values()) { @@ -210,7 +210,7 @@ final class RuntimeRecoveryContract { * RELEASED rows come back so the caller can discharge them, and a * duplicated identity is still refused as ambiguous. */ - static void verifyHistoricalInventory(RuntimeSessionRepository sessions, + public static void verifyHistoricalInventory(RuntimeSessionRepository sessions, String prefix) { String tenant = prefix + "-tenant"; String harness = prefix + "-harness"; @@ -272,7 +272,7 @@ final class RuntimeRecoveryContract { * Session row lock is the cross-process race in * {@code Issue13183AdversarialTest}. */ - static void verifyBeginSessionRelease(RuntimeBindingRepository bindings, + public static void verifyBeginSessionRelease(RuntimeBindingRepository bindings, RuntimeSessionRepository sessions, ToolExecutionRepository executions, String prefix) { // A READY session with no executions transitions, once. @@ -367,7 +367,7 @@ final class RuntimeRecoveryContract { * capture never proved the writer stopped; a complete or detached * capture, or a result without one, frees the mount. */ - static void verifyUnprovenCapture(RuntimeBindingRepository bindings, + public static void verifyUnprovenCapture(RuntimeBindingRepository bindings, RuntimeSessionRepository sessions, ToolExecutionRepository executions, String prefix) { Fixture fixture = new Fixture(bindings, sessions, executions, @@ -407,7 +407,7 @@ final class RuntimeRecoveryContract { Instant.now()), "dispatcher", claimed.getDispatchGeneration()); } - static RuntimeRecoveryEvidence evidence(RuntimeBindingRecord binding, RuntimeRecoveryEvidence.Fact fact) { + public static RuntimeRecoveryEvidence evidence(RuntimeBindingRecord binding, RuntimeRecoveryEvidence.Fact fact) { RuntimeProvisionSeed seed = binding.getProvisionSeed(); return new RuntimeRecoveryEvidence(UUID.randomUUID().toString(), fact, "deterministic-test-supervisor", Instant.now(), "test-host/boot/domain-" + binding.getBindingId(), seed.getProvisionRequestId(), @@ -415,20 +415,20 @@ final class RuntimeRecoveryContract { binding.getResourceHandle()); } - static final class Fixture { - final String id; - final RuntimeBindingRepository bindings; - final RuntimeSessionRepository sessions; - final ToolExecutionRepository executions; - final RuntimeBindingRecord binding; - final RuntimeSessionRecord session; + public static final class Fixture { + public final String id; + public final RuntimeBindingRepository bindings; + public final RuntimeSessionRepository sessions; + public final ToolExecutionRepository executions; + public final RuntimeBindingRecord binding; + public final RuntimeSessionRecord session; - Fixture(RuntimeBindingRepository bindings, RuntimeSessionRepository sessions, + public Fixture(RuntimeBindingRepository bindings, RuntimeSessionRepository sessions, ToolExecutionRepository executions, String id) { this(bindings, sessions, executions, id, id + "-session"); } - Fixture(RuntimeBindingRepository bindings, RuntimeSessionRepository sessions, + public Fixture(RuntimeBindingRepository bindings, RuntimeSessionRepository sessions, ToolExecutionRepository executions, String id, String runtimeSessionId) { this.id = id; this.bindings = bindings; @@ -452,7 +452,7 @@ final class RuntimeRecoveryContract { session = sessions.compareAndSet(acquiring, acquiring.withState(RuntimeSessionRecord.State.READY, Instant.now())); } - ToolExecutionRecord prepare(String suffix) { + public ToolExecutionRecord prepare(String suffix) { return bindings.admitExecution(sessions, executions, ToolExecutionRecord.prepared(id + suffix, id + suffix + "-key", binding.getBindingId(), binding.getGeneration(), id + "-harness", session.getRuntimeSessionId(), "turn", suffix, "digest", @@ -460,7 +460,7 @@ final class RuntimeRecoveryContract { "argsDigest", "digest"))); } - RuntimeBindingRecord lose(boolean stopped) { + public RuntimeBindingRecord lose(boolean stopped) { return bindings.compareAndSet(binding, binding.withRecoveryEvidence( evidence(binding, RuntimeRecoveryEvidence.Fact.JOURNAL_LOST), stopped ? evidence(binding, RuntimeRecoveryEvidence.Fact.WRITERS_STOPPED) : null, Instant.now())); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryTest.java index 5b768b0c416..115a01abe0d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeRecoveryTest.java @@ -1,8 +1,12 @@ package com.alibaba.qwen.code.runtimebroker; import com.alibaba.fastjson2.JSON; +import com.alibaba.qwen.code.runtimebroker.http.RuntimeBrokerHttpServer; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.StaticRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.transport.RuntimeBrokerHttpServer; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.Test; import java.lang.reflect.Proxy; @@ -152,5 +156,4 @@ class RuntimeRecoveryTest { } } } - } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionTombstoneTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionTombstoneTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionTombstoneTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionTombstoneTest.java index d04b86a8e2e..c1e5d2c0b78 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionTombstoneTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSessionTombstoneTest.java @@ -1,6 +1,10 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSshAcknowledgementTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSshAcknowledgementTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSshAcknowledgementTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSshAcknowledgementTest.java index 6e58de8f3c2..31158e15ce0 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSshAcknowledgementTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeSshAcknowledgementTest.java @@ -1,9 +1,11 @@ package com.alibaba.qwen.code.runtimebroker; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.provisioning.SshRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -28,7 +30,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class RuntimeSshAcknowledgementTest { @Test void acknowledgesTheNoStartOnlyAfterTheDispatchResultHasCommitted() { - try (var fixture = new Fixture(SshRuntimeProvisioner.KIND)) { + try (var fixture = new Fixture(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS)) { Map result = new LinkedHashMap<>(Map.of("executionStatus", "not_started")); result.put("capture", null); fixture.transport.dispatchResult = result; @@ -48,7 +50,7 @@ class RuntimeSshAcknowledgementTest { @ParameterizedTest @ValueSource(booleans = {false, true}) void drainNeverUsesStopAsAFallbackForAnIdentityConflict(boolean responseConflict) { - try (var fixture = new Fixture(SshRuntimeProvisioner.KIND)) { + try (var fixture = new Fixture(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS)) { fixture.persist(true, "not_started", true, false); if (responseConflict) { var session = fixture.sessions.findById(fixture.scope, "runtime"); @@ -70,7 +72,7 @@ class RuntimeSshAcknowledgementTest { @ParameterizedTest @ValueSource(booleans = {false, true}) void drainRetriesOnlyTheOriginalDurableNoStartAcknowledgement(boolean deferred) { - try (var fixture = new Fixture(SshRuntimeProvisioner.KIND)) { + try (var fixture = new Fixture(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS)) { ToolExecutionRecord execution = fixture.persist(deferred, "not_started", true, false); fixture.transport.failNext = true; fixture.service.release("harness", "runtime").toCompletableFuture().join(); @@ -93,7 +95,7 @@ class RuntimeSshAcknowledgementTest { @ParameterizedTest @ValueSource(booleans = {false, true}) void explicitSshNoStartAcknowledgementRequiresTheExactDurableReceipt(boolean deferred) { - try (var fixture = new Fixture(SshRuntimeProvisioner.KIND)) { + try (var fixture = new Fixture(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS)) { ToolExecutionRecord execution = fixture.persist(deferred, "not_started", true, false); Map wrong = new LinkedHashMap<>(receipt(execution)); wrong.put("deliveryStatus", "blocked"); @@ -109,7 +111,7 @@ class RuntimeSshAcknowledgementTest { @ParameterizedTest @ValueSource(strings = {"local", "unknown", "running", "capture", "missing_capture", "success"}) void doesNotAcknowledgeUnsupportedOrUnsettledResults(String scenario) { - try (var fixture = new Fixture("local".equals(scenario) ? LocalProcessRuntimeProvisioner.KIND : SshRuntimeProvisioner.KIND)) { + try (var fixture = new Fixture("local".equals(scenario) ? com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS : com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS)) { fixture.requireAcknowledgement = false; ToolExecutionRecord execution = fixture.persist(true, "success".equals(scenario) ? "success" : "not_started", !"missing_capture".equals(scenario), "capture".equals(scenario)); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/TakeoverReconciliationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/TakeoverReconciliationTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/TakeoverReconciliationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/TakeoverReconciliationTest.java index fae63fbc73a..9d3426cdc7d 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/TakeoverReconciliationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/TakeoverReconciliationTest.java @@ -1,6 +1,13 @@ package com.alibaba.qwen.code.runtimebroker; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/TcpRelay.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/TcpRelay.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/TcpRelay.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/TcpRelay.java index 15a134d463a..1ec68dd9e31 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/TcpRelay.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/TcpRelay.java @@ -16,14 +16,14 @@ import java.util.concurrent.atomic.AtomicInteger; * database unreachable for everything behind the relay: open connections are * reset and new ones are refused until {@link #restore()}. */ -final class TcpRelay implements AutoCloseable { +public final class TcpRelay implements AutoCloseable { private final ServerSocket server; private final int upstreamPort; private final AtomicBoolean available = new AtomicBoolean(true); private final AtomicInteger refused = new AtomicInteger(); private final Set open = ConcurrentHashMap.newKeySet(); - TcpRelay(int upstreamPort) throws IOException { + public TcpRelay(int upstreamPort) throws IOException { this.upstreamPort = upstreamPort; server = new ServerSocket(0, 50, InetAddress.getLoopbackAddress()); Thread acceptor = new Thread(this::accept, "tcp-relay-accept"); @@ -31,26 +31,26 @@ final class TcpRelay implements AutoCloseable { acceptor.start(); } - int port() { + public int port() { return server.getLocalPort(); } - void cut() { + public void cut() { available.set(false); open.forEach(TcpRelay::reset); } - void restore() { + public void restore() { available.set(true); } /** Connection attempts refused while the relay was cut. */ - int refused() { + public int refused() { return refused.get(); } /** Whether no connection is open through the relay right now. */ - boolean idle() { + public boolean idle() { return open.isEmpty(); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecordFixtures.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecordFixtures.java similarity index 87% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecordFixtures.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecordFixtures.java index 5e4be021cde..c00d4d54d72 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecordFixtures.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/ToolExecutionRecordFixtures.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.runtimebroker; -final class ToolExecutionRecordFixtures { +public final class ToolExecutionRecordFixtures { private ToolExecutionRecordFixtures() { } // A snapshot that matches the stored claim and version but carries a // different request identity. - static ToolExecutionRecord withIdentity(ToolExecutionRecord stored, + public static ToolExecutionRecord withIdentity(ToolExecutionRecord stored, String idempotencyKey, String bindingId, long runtimeGeneration, String harnessSessionId) { return ToolExecutionRecord.prepared(stored.getExecutionCallId(), diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/UnknownExecutionObservationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/UnknownExecutionObservationTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/UnknownExecutionObservationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/UnknownExecutionObservationTest.java index cdfa551ebbf..6200897c685 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/UnknownExecutionObservationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/UnknownExecutionObservationTest.java @@ -1,7 +1,11 @@ package com.alibaba.qwen.code.runtimebroker; import com.alibaba.fastjson2.JSON; -import com.alibaba.qwen.code.runtimebroker.transport.RuntimeBrokerHttpServer; +import com.alibaba.qwen.code.runtimebroker.http.RuntimeBrokerHttpServer; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.Test; import java.net.InetSocketAddress; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceCreationSchemaMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceCreationSchemaMySqlIT.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceCreationSchemaMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceCreationSchemaMySqlIT.java index fb06c9071f0..0ab58c15f17 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceCreationSchemaMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceCreationSchemaMySqlIT.java @@ -2,8 +2,8 @@ package com.alibaba.qwen.code.runtimebroker; import com.alibaba.qwen.code.managedagent.persistence.IsolatedManagedSchemaMySqlTestDatabase; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaContract; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.flywaydb.core.Flyway; import org.junit.jupiter.api.Test; import org.springframework.dao.DataAccessException; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceExecutionProfileTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceExecutionProfileTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceExecutionProfileTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceExecutionProfileTest.java index c93d4890ab2..b82748b6eee 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceExecutionProfileTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceExecutionProfileTest.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.runtimebroker; +import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Test; - class WorkspaceExecutionProfileTest { @Test void admitsOnlySessionIsolation() { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceMigrationRepositoryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceMigrationRepositoryTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceMigrationRepositoryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceMigrationRepositoryTest.java index 31ee1893ea1..f6a34f0b162 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceMigrationRepositoryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/WorkspaceMigrationRepositoryTest.java @@ -1,9 +1,13 @@ package com.alibaba.qwen.code.runtimebroker; -import static org.junit.jupiter.api.Assertions.*; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import org.junit.jupiter.api.Test; import java.time.Instant; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; class WorkspaceMigrationRepositoryTest { @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/HttpRuntimeTransportTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/HttpRuntimeTransportTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/HttpRuntimeTransportTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/HttpRuntimeTransportTest.java index 77ed0db7d6d..b35f8762c90 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/HttpRuntimeTransportTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/HttpRuntimeTransportTest.java @@ -1,14 +1,11 @@ -package com.alibaba.qwen.code.runtimebroker.transport; +package com.alibaba.qwen.code.runtimebroker.adapter; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.runtimebroker.BrokerValues; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.alibaba.qwen.code.runtimebroker.RuntimeAttestation; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerTestAccess; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; @@ -19,9 +16,15 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocolTest; -import com.alibaba.qwen.code.runtimebroker.provisioning.ProviderRuntimeProtocol; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.RuntimeBrokerTestAccess; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocolTest; +import com.alibaba.qwen.code.runtimebroker.protocol.ProviderRuntimeProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.ManagedHookProtocol; +import com.alibaba.qwen.code.runtimebroker.transport.ManagedMcpProtocol; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -977,8 +980,7 @@ class HttpRuntimeTransportTest { @Test void exchangesToolV3WithTheRealTypeScriptHandler() throws Exception { - Path repository = Path.of("").toAbsolutePath() - .resolve("../..").normalize(); + Path repository = VerificationFixtures.repositoryRoot(); Path handler = repository.resolve("packages/cli/dist/src/serve/managed-runtime-tool-v3-routes.js"); Assumptions.assumeTrue(Files.isRegularFile(handler), "Build the TypeScript workspace before the interop test."); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/HttpRuntimeTransportTestAccess.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/HttpRuntimeTransportTestAccess.java similarity index 85% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/HttpRuntimeTransportTestAccess.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/HttpRuntimeTransportTestAccess.java index f33724cdcbc..abe498c73ff 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/HttpRuntimeTransportTestAccess.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/HttpRuntimeTransportTestAccess.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.transport; +package com.alibaba.qwen.code.runtimebroker.adapter; import java.net.http.HttpClient; import java.time.Duration; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiAcknowledgementHttpTransportTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/ManagedCsiAcknowledgementHttpTransportTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiAcknowledgementHttpTransportTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/ManagedCsiAcknowledgementHttpTransportTest.java index 3fa6e8b7b84..5e22a8fe22e 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiAcknowledgementHttpTransportTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/ManagedCsiAcknowledgementHttpTransportTest.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.transport; +package com.alibaba.qwen.code.runtimebroker.adapter; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.alibaba.qwen.code.runtimebroker.ManagedCsiAcknowledgementProtocolTest; @@ -6,6 +6,7 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.transport.ManagedCsiProtocol; import com.sun.net.httpserver.HttpServer; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedRuntimeAttestationConformanceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/ManagedRuntimeAttestationConformanceTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedRuntimeAttestationConformanceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/ManagedRuntimeAttestationConformanceTest.java index 45022b9a5b8..93c91b67141 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedRuntimeAttestationConformanceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/ManagedRuntimeAttestationConformanceTest.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.transport; +package com.alibaba.qwen.code.runtimebroker.adapter; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/security/AesGcmSecretProtectorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/security/AesGcmSecretProtectorTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/security/AesGcmSecretProtectorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/security/AesGcmSecretProtectorTest.java index fa8141f7361..c3a394222f5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/security/AesGcmSecretProtectorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/adapter/security/AesGcmSecretProtectorTest.java @@ -1,5 +1,6 @@ -package com.alibaba.qwen.code.runtimebroker.security; +package com.alibaba.qwen.code.runtimebroker.adapter.security; +import com.alibaba.qwen.code.runtimebroker.security.ProtectedSecret; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionExecutionContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/http/ManagedExtensionExecutionContractTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionExecutionContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/http/ManagedExtensionExecutionContractTest.java index bd7a9d0291c..22f9dbf6c03 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionExecutionContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/http/ManagedExtensionExecutionContractTest.java @@ -1,4 +1,4 @@ -package com.alibaba.qwen.code.runtimebroker.transport; +package com.alibaba.qwen.code.runtimebroker.http; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.fasterxml.jackson.databind.JsonNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalogTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalogTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalogTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalogTest.java index 1e14aa19056..54bc1055cb8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalogTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaCatalogTest.java @@ -1,8 +1,6 @@ package com.alibaba.qwen.code.runtimebroker.managedschema; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract; import org.junit.jupiter.api.Test; import java.lang.reflect.InvocationTargetException; @@ -13,6 +11,9 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.util.UUID; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + class ManagedSchemaCatalogTest { private static final String BODY = "\n CONSTRAINT ck_probe CHECK (state = 'OK')\n"; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaMapping.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaMapping.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaMapping.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaMapping.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaProjectionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaProjectionTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaProjectionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaProjectionTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaRowsTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaRowsTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaRowsTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaRowsTest.java index 50a97376dbb..a84224586ad 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaRowsTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaRowsTest.java @@ -1,9 +1,7 @@ package com.alibaba.qwen.code.runtimebroker.managedschema; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; +import org.h2.jdbcx.JdbcDataSource; +import org.junit.jupiter.api.Test; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; @@ -13,8 +11,11 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Timestamp; -import org.h2.jdbcx.JdbcDataSource; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; class ManagedSchemaRowsTest { @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTest.java index 362306e6e31..f72ac7cd00b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTest.java @@ -1,5 +1,7 @@ package com.alibaba.qwen.code.runtimebroker.managedschema; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaContract; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import org.flywaydb.core.Flyway; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTimeContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTimeContractTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTimeContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTimeContractTest.java index 3112bb0b090..8ca9049e59b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTimeContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTimeContractTest.java @@ -1,10 +1,9 @@ package com.alibaba.qwen.code.runtimebroker.managedschema; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; +import org.h2.jdbcx.JdbcDataSource; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; import java.sql.Types; import java.util.ArrayList; @@ -14,9 +13,12 @@ import java.util.List; import java.util.Map; import java.util.UUID; import java.util.regex.Pattern; -import org.h2.jdbcx.JdbcDataSource; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; class ManagedSchemaTimeContractTest { private static final List TIMESTAMPS = List.of( diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTimeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTimeTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTimeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedschema/ManagedSchemaTimeTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ConstructorVisibilityTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ConstructorVisibilityTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ConstructorVisibilityTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ConstructorVisibilityTest.java index 5472e6e3093..e304ddf3633 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ConstructorVisibilityTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ConstructorVisibilityTest.java @@ -1,9 +1,11 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; -import static org.junit.jupiter.api.Assertions.assertEquals; +import com.alibaba.qwen.code.runtimebroker.workspace.GrantedWorkspaceAccessPolicy; +import org.junit.jupiter.api.Test; import java.util.List; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; class ConstructorVisibilityTest { @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBindingTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBindingTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBindingTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBindingTest.java index 192c67fb7ce..99180d33365 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBindingTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ContextBindingTest.java @@ -1,5 +1,14 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; +import org.junit.jupiter.api.Test; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.copy; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -8,14 +17,6 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.Consumer; -import org.junit.jupiter.api.Test; - class ContextBindingTest { @Test void derivesThePinnedDigestOfTheRootFixture() { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedContextEnvelopeConformanceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedContextEnvelopeConformanceTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedContextEnvelopeConformanceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedContextEnvelopeConformanceTest.java index 4a20eaa5a67..2eb7fa5c462 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedContextEnvelopeConformanceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedContextEnvelopeConformanceTest.java @@ -1,12 +1,10 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.IntNode; +import org.junit.jupiter.api.Test; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -16,7 +14,10 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.regex.Pattern; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Pins the managed-context/1 envelope (W0a-2) to the language-neutral schema diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedWorkspaceBindingConformanceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedWorkspaceBindingConformanceTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedWorkspaceBindingConformanceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedWorkspaceBindingConformanceTest.java index 8830025e7b4..215885a81ac 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedWorkspaceBindingConformanceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ManagedWorkspaceBindingConformanceTest.java @@ -1,11 +1,9 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -17,7 +15,10 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Runs the language-neutral W0a fixtures that the TypeScript implementation diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TestWorkspaces.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TestWorkspaces.java similarity index 68% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TestWorkspaces.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TestWorkspaces.java index 25024cf9f01..e012ddffd09 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TestWorkspaces.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TestWorkspaces.java @@ -1,8 +1,8 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; -final class TestWorkspaces { - static final String TENANT = "tenant-a"; - static final String OTHER_TENANT = "tenant-b"; +public final class TestWorkspaces { + public static final String TENANT = "tenant-a"; + public static final String OTHER_TENANT = "tenant-b"; private TestWorkspaces() { } @@ -12,17 +12,17 @@ final class TestWorkspaces { * interned, so only a copy makes a test fail when code compares strings * with {@code ==} instead of {@code equals}. */ - static String copy(String value) { + public static String copy(String value) { return new String(value); } - static WorkspaceRecord workspace(String tenantId, String workspaceId, + public static WorkspaceRecord workspace(String tenantId, String workspaceId, WorkspaceState state) { return workspace(tenantId, workspaceId, 1, "storage-" + workspaceId, state); } - static WorkspaceRecord workspace(String tenantId, String workspaceId, + public static WorkspaceRecord workspace(String tenantId, String workspaceId, long generation, String storageId, WorkspaceState state) { return new WorkspaceRecord(tenantId, workspaceId, generation, storageId, "Workspace " + workspaceId, state, "policy:default", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TextFieldProbeTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TextFieldProbeTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TextFieldProbeTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TextFieldProbeTest.java index 720848609ef..757050081df 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TextFieldProbeTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/TextFieldProbeTest.java @@ -1,12 +1,14 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; -import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.TENANT; -import static org.junit.jupiter.api.Assertions.assertEquals; +import com.alibaba.qwen.code.runtimebroker.workspace.GrantedWorkspaceAccessPolicy; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -import org.junit.jupiter.api.Test; + +import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.TENANT; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Probes the text rule of display names and actor IDs with every code point diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessTest.java index eaa947c156a..92973ed5cdb 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceAccessTest.java @@ -1,11 +1,11 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; +import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Test; - class WorkspaceAccessTest { @Test void ranksRolesInGrantOrder() { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceActorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceActorTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceActorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceActorTest.java index 01b85971519..39ba3987817 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceActorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceActorTest.java @@ -1,13 +1,13 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; +import org.junit.jupiter.api.Test; + import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.copy; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import org.junit.jupiter.api.Test; - class WorkspaceActorTest { @Test void keepsTheAuthenticatedIdentity() { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalogTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalogTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalogTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalogTest.java index 116d02cf198..3dbf04cae48 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalogTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceCatalogTest.java @@ -1,5 +1,17 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; +import com.alibaba.qwen.code.runtimebroker.workspace.ConfiguredWorkspaceRegistry; +import com.alibaba.qwen.code.runtimebroker.workspace.GrantedWorkspaceAccessPolicy; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; + import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.OTHER_TENANT; import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.TENANT; import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.copy; @@ -9,15 +21,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicReference; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - // A listing that stops advancing must fail here, not hang the build. The // scan never checks for interrupts, so the timeout needs its own thread. @Timeout(value = 10, threadMode = Timeout.ThreadMode.SEPARATE_THREAD) diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecordTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecordTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecordTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecordTest.java index f236005ae16..0b076195592 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecordTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRecordTest.java @@ -1,15 +1,16 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.function.Consumer; + import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.copy; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import java.util.List; -import java.util.function.Consumer; -import org.junit.jupiter.api.Test; - class WorkspaceRecordTest { @Test void keepsEveryField() { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRelativePathTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRelativePathTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRelativePathTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRelativePathTest.java index cbb217eecd2..d95cd5fd743 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRelativePathTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceRelativePathTest.java @@ -1,12 +1,13 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; +import org.junit.jupiter.api.Test; + +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; -import java.util.List; -import org.junit.jupiter.api.Test; - class WorkspaceRelativePathTest { @Test void normalizesSeparatorsAndDotSegments() { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceSelectionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceSelectionTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceSelectionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceSelectionTest.java index 9be72429e0a..6399a55c301 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceSelectionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceSelectionTest.java @@ -1,5 +1,9 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; +import org.junit.jupiter.api.Test; + +import java.util.List; + import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.copy; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -7,9 +11,6 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.List; -import org.junit.jupiter.api.Test; - class WorkspaceSelectionTest { @Test void describesAnOmittedSelection() { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceViewTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceViewTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceViewTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceViewTest.java index 2efa78542da..d60098b5678 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceViewTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/WorkspaceViewTest.java @@ -1,5 +1,7 @@ package com.alibaba.qwen.code.runtimebroker.managedworkspace; +import org.junit.jupiter.api.Test; + import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.TENANT; import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.copy; import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.workspace; @@ -8,8 +10,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Test; - class WorkspaceViewTest { private static final WorkspaceRecord ALPHA = workspace(TENANT, "alpha", WorkspaceState.ACTIVE); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BindingExecutionInventoryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/BindingExecutionInventoryTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BindingExecutionInventoryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/BindingExecutionInventoryTest.java index a5b11eebc7e..51dc06d2493 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/BindingExecutionInventoryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/BindingExecutionInventoryTest.java @@ -1,6 +1,18 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; -import static org.junit.jupiter.api.Assertions.*; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; +import com.alibaba.qwen.code.runtimebroker.RuntimeRecoveryEvidence; +import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.ToolHoldRow; +import org.h2.jdbcx.JdbcDataSource; +import org.junit.jupiter.api.Test; + +import javax.sql.DataSource; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; @@ -15,9 +27,8 @@ import java.util.Map; import java.util.UUID; import java.util.function.Consumer; import java.util.stream.IntStream; -import javax.sql.DataSource; -import org.h2.jdbcx.JdbcDataSource; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; class BindingExecutionInventoryTest { private static final String BINDING = "binding"; @@ -89,7 +100,7 @@ class BindingExecutionInventoryTest { + "WHERE execution_call_id = ?")) { statement.setString(1, "0".repeat(64)); statement.setBytes(2, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys .identity("plain")); assertEquals(1, statement.executeUpdate()); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiApplicationProgressTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiApplicationProgressTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiApplicationProgressTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiApplicationProgressTest.java index 7e6282c0993..06adeae35c5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiApplicationProgressTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiApplicationProgressTest.java @@ -1,20 +1,25 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.CsiOwnershipRevision; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.managedschema.JdbcRuntimeBrokerSchema; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys; +import com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys; import com.fasterxml.jackson.databind.ObjectMapper; +import org.h2.jdbcx.JdbcDataSource; +import org.junit.jupiter.api.Test; + import java.sql.Connection; import java.time.Instant; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; -import org.h2.jdbcx.JdbcDataSource; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; class CsiApplicationProgressTest { private static final String ALIAS = "ec77f5fe1b4225e8f8203d59b5858eb235f9155261c440efaa02efd5513bd2df"; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiFilesRetirementProfileTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiFilesRetirementProfileTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiFilesRetirementProfileTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiFilesRetirementProfileTest.java index 977b2cb8616..d60aa96169a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiFilesRetirementProfileTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiFilesRetirementProfileTest.java @@ -1,13 +1,16 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; + +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import org.junit.jupiter.api.Test; + +import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import java.util.UUID; -import org.junit.jupiter.api.Test; - class CsiFilesRetirementProfileTest { private final ContextBinding workspace = new ContextBinding("tenant", "workspace", 3, "storage", ".", CsiFilesRetirementProfile.CONTEXT_CONFIG_REF, 1); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeReadContinuationProofTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiNativeReadContinuationProofTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeReadContinuationProofTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiNativeReadContinuationProofTest.java index c1dcd676c1b..1b96c3f1dcc 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeReadContinuationProofTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiNativeReadContinuationProofTest.java @@ -1,5 +1,9 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.alibaba.qwen.code.runtimebroker.transport.CsiNativeReadbackProtocol; import com.fasterxml.jackson.databind.JsonNode; @@ -91,7 +95,7 @@ class CsiNativeReadContinuationProofTest { for (String path : List.of(".", " ./ ", "../outside", "/absolute")) { ObjectNode args = JSON.createObjectNode().put("file_path", path); var call = new CsiNativeActivationProof.FunctionCall(source.id(), "read_file", args, 0, 0); - assertEquals("CSI file input is invalid.", CsiNativeToolReservation.refusalReason(original, call)); + assertEquals("CSI file input is invalid.", com.alibaba.qwen.code.runtimebroker.CsiNativeToolContent.refusalReason(original, call)); } ObjectNode args = JSON.createObjectNode().put("file_path", "owned.txt").put("content", ""); var call = new CsiNativeActivationProof.FunctionCall(source.id(), "write_file", args, 0, 0); @@ -101,9 +105,9 @@ class CsiNativeReadContinuationProofTest { .put("runtimeSessionId", original.getIsolationKey()).put("payloadJson", CsiNativeActivationProof.canonical(payload)); int overhead = CsiNativeActivationProof.utf8(CsiNativeActivationProof.canonical(wrapper)).length; args.put("content", "x".repeat(64 * 1024 - overhead)); - assertNull(CsiNativeToolReservation.refusalReason(original, call)); + assertNull(com.alibaba.qwen.code.runtimebroker.CsiNativeToolContent.refusalReason(original, call)); args.put("content", args.path("content").textValue() + "x"); - assertEquals("CSI file input exceeds the durable Session limit.", CsiNativeToolReservation.refusalReason(original, call)); + assertEquals("CSI file input exceeds the durable Session limit.", com.alibaba.qwen.code.runtimebroker.CsiNativeToolContent.refusalReason(original, call)); } private Changed refusal(CsiNativeActivationProof.Prefix before, CsiNativeActivationProof.FunctionCall call, diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolReservationTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiNativeToolReservationTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolReservationTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiNativeToolReservationTest.java index f193bb74bd4..14a779f54fa 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/CsiNativeToolReservationTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/CsiNativeToolReservationTest.java @@ -1,5 +1,12 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeSession; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.managedschema.JdbcRuntimeBrokerSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; @@ -84,7 +91,7 @@ class CsiNativeToolReservationTest { var before = prefix(parts); var pending = before.pendingBatch(); var refused = pending.calls().get(1); - assertEquals("CSI file input is invalid.", CsiNativeToolReservation.refusalReason(request, refused)); + assertEquals("CSI file input is invalid.", com.alibaba.qwen.code.runtimebroker.CsiNativeToolContent.refusalReason(request, refused)); var after = new CsiNativeActivationProof.Prefix(before.input(), before.checkpoint(), before.lastMessageId(), before.attempt(), before.assistantCommitted(), before.stream(), before.usedIds(), new CsiNativeActivationProof.PendingBatch(pending.messageId(), pending.contentRef(), pending.calls(), diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DispatchAuthorizationEvidenceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/DispatchAuthorizationEvidenceTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DispatchAuthorizationEvidenceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/DispatchAuthorizationEvidenceTest.java index 283ab9c081b..751853c90f3 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/DispatchAuthorizationEvidenceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/DispatchAuthorizationEvidenceTest.java @@ -1,6 +1,19 @@ -package com.alibaba.qwen.code.runtimebroker; - -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeRecoveryContract; +import com.alibaba.qwen.code.runtimebroker.RuntimeRecoveryEvidence; +import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSession; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateRig.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/FaultGateRig.java similarity index 82% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateRig.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/FaultGateRig.java index 006d70a81e8..4c58a1ab9dd 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/FaultGateRig.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/FaultGateRig.java @@ -1,9 +1,19 @@ -package com.alibaba.qwen.code.runtimebroker; - +package com.alibaba.qwen.code.runtimebroker.persistence; import com.alibaba.fastjson2.JSON; +import com.alibaba.qwen.code.runtimebroker.BrokerProcess; +import com.alibaba.qwen.code.runtimebroker.DriverManagerDataSource; +import com.alibaba.qwen.code.runtimebroker.FaultProxy; +import com.alibaba.qwen.code.runtimebroker.ProcessTrees; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSession; +import com.alibaba.qwen.code.runtimebroker.TcpRelay; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.provisioning.FaultGateBroker; import org.h2.tools.Server; import javax.sql.DataSource; @@ -44,20 +54,20 @@ import java.util.function.Supplier; * second Broker sees the same rows. The gate reads those rows directly. * Missing prerequisites fail the gate; nothing is skipped. */ -final class FaultGateRig implements AutoCloseable { - static final String CLI_PROPERTY = "qwen.cli.entry"; - static final String HARNESS = "harness-1"; - static final String SESSION = "runtime-session-1"; - static final String STORAGE = "storage-a"; - static final Duration OPERATION_LEASE = Duration.ofSeconds(2); - static final Duration DISPATCH_LEASE = Duration.ofSeconds(2); - static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(10); - static final Duration WAIT = Duration.ofSeconds(45); +public final class FaultGateRig implements AutoCloseable { + public static final String CLI_PROPERTY = "qwen.cli.entry"; + public static final String HARNESS = "harness-1"; + public static final String SESSION = "runtime-session-1"; + public static final String STORAGE = "storage-a"; + public static final Duration OPERATION_LEASE = Duration.ofSeconds(2); + public static final Duration DISPATCH_LEASE = Duration.ofSeconds(2); + public static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(10); + public static final Duration WAIT = Duration.ofSeconds(45); private static final String SECRET_KEY = Base64.getEncoder() .encodeToString("fault-gate-secret-key-0123456789" .getBytes(StandardCharsets.US_ASCII)); - enum Provisioner { + public enum Provisioner { /** The production provisioner alone: owns workers in memory. */ LOCAL_PROCESS, /** Production durable store; test host identity is injected only on non-Linux hosts. */ @@ -67,7 +77,7 @@ final class FaultGateRig implements AutoCloseable { RECOVERABLE } - enum Placement { + public enum Placement { /** Boot v1 and attestation v2: the tool path of FG1-FG4. */ LEGACY, /** @@ -78,16 +88,16 @@ final class FaultGateRig implements AutoCloseable { MANAGED } - final Path root; - final Path workspace; - final Placement placement; + public final Path root; + public final Path workspace; + public final Placement placement; /** Where the Session's tools run: the Workspace, or its context. */ - final Path directory; - final RuntimeScope scope; + public final Path directory; + public final RuntimeScope scope; /** The Session context a MANAGED acquire installs; null for LEGACY. */ - final ContextBinding context; - final JdbcToolExecutionRepository executions; - final JdbcRuntimeBindingRepository bindings; + public final ContextBinding context; + public final JdbcToolExecutionRepository executions; + public final JdbcRuntimeBindingRepository bindings; private final Path cli; private final Server database; private final DataSource dataSource; @@ -130,11 +140,11 @@ final class FaultGateRig implements AutoCloseable { } } - static FaultGateRig open() throws Exception { + public static FaultGateRig open() throws Exception { return open(Placement.LEGACY); } - static FaultGateRig open(Placement placement) throws Exception { + public static FaultGateRig open(Placement placement) throws Exception { if (System.getProperty("os.name").toLowerCase(Locale.ROOT) .startsWith("windows")) { throw new AssertionError( @@ -163,7 +173,7 @@ final class FaultGateRig implements AutoCloseable { return new FaultGateRig(cli, placement); } - FaultProxy proxy() throws IOException { + public FaultProxy proxy() throws IOException { FaultProxy proxy = new FaultProxy(); resources.add(proxy); proxies.add(proxy); @@ -171,23 +181,23 @@ final class FaultGateRig implements AutoCloseable { } /** A relay a gate can cut to take the database away from a Broker. */ - TcpRelay databaseRelay() throws IOException { + public TcpRelay databaseRelay() throws IOException { TcpRelay relay = new TcpRelay(database.getPort()); resources.add(relay); return relay; } - BrokerProcess broker(String name, FaultProxy proxy, + public BrokerProcess broker(String name, FaultProxy proxy, Provisioner provisioner) throws Exception { return broker(name, proxy, provisioner, null); } - BrokerProcess broker(String name, FaultProxy proxy, + public BrokerProcess broker(String name, FaultProxy proxy, Provisioner provisioner, TcpRelay relay) throws Exception { return broker(name, proxy, provisioner, relay, REQUEST_TIMEOUT); } - BrokerProcess broker(String name, FaultProxy proxy, + public BrokerProcess broker(String name, FaultProxy proxy, Provisioner provisioner, TcpRelay relay, Duration requestTimeout) throws Exception { Map scopeConfig = new LinkedHashMap<>(); @@ -234,18 +244,18 @@ final class FaultGateRig implements AutoCloseable { } /** SIGKILLs a Broker JVM alone and keeps its orphaned workers in view. */ - void killBroker(BrokerProcess broker) throws InterruptedException { + public void killBroker(BrokerProcess broker) throws InterruptedException { orphans.addAll(broker.descendants()); broker.kill(); } - void closeBroker(BrokerProcess broker) throws InterruptedException { + public void closeBroker(BrokerProcess broker) throws InterruptedException { orphans.addAll(broker.descendants()); broker.close(); } /** SIGKILLs a Broker's worker together with the tool processes below. */ - void killWorker(BrokerProcess broker) { + public void killWorker(BrokerProcess broker) { List workers = broker.workers(); if (workers.size() != 1) { throw new AssertionError("expected one worker, found " + workers); @@ -253,9 +263,9 @@ final class FaultGateRig implements AutoCloseable { ProcessTrees.kill(workers.get(0), WAIT); } - record ToolCall(Map reference, String payloadJson) { + public record ToolCall(Map reference, String payloadJson) { /** The raw reference of the immediate route: identity plus payload. */ - Map immediateReference() { + public Map immediateReference() { Map raw = new LinkedHashMap<>(reference); raw.putAll(JSON.parseObject(payloadJson)); return raw; @@ -263,12 +273,12 @@ final class FaultGateRig implements AutoCloseable { } /** A foreground shell call whose side effects land in the workspace. */ - static ToolCall shell(String callId, String command) { + public static ToolCall shell(String callId, String command) { return shell(SESSION, callId, command); } /** A foreground shell call of another Runtime Session. */ - static ToolCall shell(String session, String callId, + public static ToolCall shell(String session, String callId, String command) { Map input = new LinkedHashMap<>(); input.put("command", command); @@ -293,7 +303,7 @@ final class FaultGateRig implements AutoCloseable { } /** The lines a tool appended to a marker file in the workspace. */ - List marker(String name) { + public List marker(String name) { return lines(workspace.resolve(name)); } @@ -301,12 +311,12 @@ final class FaultGateRig implements AutoCloseable { * A command that appends its own working directory to a file outside * every Workspace, so a gate sees whether, and where, a tool ran. */ - String recordRun() { + public String recordRun() { return "pwd >> '" + root.resolve("runs") + "'"; } /** The working directory of every tool run {@link #recordRun} saw. */ - List runs() { + public List runs() { return lines(root.resolve("runs")); } @@ -320,11 +330,11 @@ final class FaultGateRig implements AutoCloseable { } } - ToolExecutionRecord execution(String executionCallId) { + public ToolExecutionRecord execution(String executionCallId) { return executions.findByExecutionCallId(executionCallId); } - ToolExecutionRecord awaitExecution(String executionCallId, + public ToolExecutionRecord awaitExecution(String executionCallId, Predicate condition, String what) throws InterruptedException { try { @@ -340,19 +350,19 @@ final class FaultGateRig implements AutoCloseable { } } - void awaitMarker(String name, List expected) + public void awaitMarker(String name, List expected) throws InterruptedException { await(() -> marker(name), expected::equals, name + " = " + expected); } /** Proves no further side effect reaches a marker for a while. */ - void holdMarker(String name, List expected, Duration period) + public void holdMarker(String name, List expected, Duration period) throws InterruptedException { hold(() -> marker(name), expected::equals, period, name); } /** Waits until the database clock has passed the dispatch lease. */ - void awaitDispatchLapse(String executionCallId) + public void awaitDispatchLapse(String executionCallId) throws InterruptedException { await(() -> execution(executionCallId), record -> record .getDispatchLeaseUntil() == null @@ -360,7 +370,7 @@ final class FaultGateRig implements AutoCloseable { "dispatch lease of " + executionCallId + " lapsed"); } - Instant databaseNow() { + public Instant databaseNow() { try (Connection connection = dataSource.getConnection()) { return JdbcRepositorySupport.databaseNow(connection); } catch (SQLException exception) { @@ -368,19 +378,19 @@ final class FaultGateRig implements AutoCloseable { } } - RuntimeBindingRecord activeBinding() { + public RuntimeBindingRecord activeBinding() { return bindings.findActive(placement == Placement.MANAGED ? new RuntimeProvisionRequest(scope, HARNESS, - LocalProcessRuntimeProvisioner.KIND, STORAGE) + com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, STORAGE) : new RuntimeProvisionRequest(scope, null, - LocalProcessRuntimeProvisioner.KIND)); + com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS)); } - RuntimeSession session() { + public RuntimeSession session() { return new RuntimeSession(HARNESS, SESSION, "bootstrap", scope); } - static T await(Supplier probe, Predicate done, String what) + public static T await(Supplier probe, Predicate done, String what) throws InterruptedException { long deadline = System.nanoTime() + WAIT.toNanos(); T value = probe.get(); @@ -396,7 +406,7 @@ final class FaultGateRig implements AutoCloseable { } /** Waits until a value has stopped changing for a quiet period. */ - static T settle(Supplier probe, Duration quiet, String what) + public static T settle(Supplier probe, Duration quiet, String what) throws InterruptedException { long deadline = System.nanoTime() + WAIT.toNanos(); T value = probe.get(); @@ -417,7 +427,7 @@ final class FaultGateRig implements AutoCloseable { } /** Proves a condition still holds after a quiet period. */ - static void hold(Supplier probe, Predicate invariant, + public static void hold(Supplier probe, Predicate invariant, Duration period, String what) throws InterruptedException { long deadline = System.nanoTime() + period.toNanos(); do { @@ -436,7 +446,7 @@ final class FaultGateRig implements AutoCloseable { * whether its execute request timed out or its worker was gone before * dispatch, and neither path logs; these tell the two apart. */ - String diagnostics() { + public String diagnostics() { StringBuilder text = new StringBuilder("\n--- rig ---"); for (FaultProxy proxy : proxies) { text.append("\nproxy ").append(proxy.port()).append(" requests: ") @@ -451,7 +461,7 @@ final class FaultGateRig implements AutoCloseable { return text.toString(); } - String logs() { + public String logs() { StringBuilder text = new StringBuilder(); for (BrokerProcess broker : brokers) { text.append("\n--- broker ").append(broker.pid()).append(" ---\n") diff --git a/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/InMemoryTestAccess.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/InMemoryTestAccess.java new file mode 100644 index 00000000000..584c11ceafd --- /dev/null +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/InMemoryTestAccess.java @@ -0,0 +1,8 @@ +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +public final class InMemoryTestAccess { + private InMemoryTestAccess() { } + public static ToolExecutionRecord authorizeDispatch(InMemoryToolExecutionRepository repository, ToolExecutionRecord expected, String owner, long generation, long version) { + return repository.authorizeDispatch(expected, owner, generation, version); + } +} diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiBindingControlTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiBindingControlTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiBindingControlTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiBindingControlTest.java index 8ad8c36cbf5..710b74c1cae 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcCsiBindingControlTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcCsiBindingControlTest.java @@ -1,8 +1,13 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.JdbcRuntimeBrokerSchema; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; @@ -97,7 +102,7 @@ class JdbcCsiBindingControlTest { try (var connection = fixture.source().getConnection()) { sql(connection, "INSERT INTO ma_tenant_guard VALUES ('" - + com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys + + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys .key("tenant") + "', 'other', 'output_publication')"); } @@ -211,15 +216,15 @@ class JdbcCsiBindingControlTest { String id = UUID.randomUUID().toString(); statement.setString( 1, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( "CSI_RETIREMENT", "tenant", id)); statement.setString(2, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( "tenant")); JdbcRepositorySupport.setIdentity(statement, 3, id); statement.setString( 4, - com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaKeys.key( + com.alibaba.qwen.code.runtimebroker.schema.ManagedSchemaKeys.key( claimed.getBindingId())); statement.executeUpdate(); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcPlacementGuardTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcPlacementGuardTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcPlacementGuardTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcPlacementGuardTest.java index 321ccab0a0f..e9e3aa62017 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcPlacementGuardTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcPlacementGuardTest.java @@ -1,8 +1,15 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; +import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.JdbcRuntimeBrokerSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.security.ProtectedSecret; import com.alibaba.qwen.code.runtimebroker.security.SecretProtector; import org.h2.jdbcx.JdbcDataSource; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositoryContract.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRepositoryContract.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositoryContract.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRepositoryContract.java index 08b28cccd60..29ed9a70ee6 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositoryContract.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRepositoryContract.java @@ -1,6 +1,20 @@ -package com.alibaba.qwen.code.runtimebroker; - -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.ExecutionTakeoverContract; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; +import com.alibaba.qwen.code.runtimebroker.RuntimeRecoveryContract; +import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSession; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.security.SecretProtector; import javax.sql.DataSource; @@ -1424,6 +1438,6 @@ public final class JdbcRepositoryContract { @FunctionalInterface private interface IndexedOperation { - T run(int index) throws Exception; + public T run(int index) throws Exception; } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositorySupportTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRepositorySupportTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositorySupportTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRepositorySupportTest.java index 526a1b6aecf..591443b98db 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRepositorySupportTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRepositorySupportTest.java @@ -1,6 +1,7 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocolTest; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocolTest; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeBrokerMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeBrokerMySqlIT.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeBrokerMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeBrokerMySqlIT.java index d5775188bd6..10e70696e60 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeBrokerMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeBrokerMySqlIT.java @@ -1,7 +1,21 @@ -package com.alibaba.qwen.code.runtimebroker; - +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.DriverManagerDataSource; +import com.alibaba.qwen.code.runtimebroker.JdbcPhysicalStorageReleaseTest; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeDrainReceipt; +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.RuntimeMaintenanceRecoveryTest; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSession; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; import org.junit.jupiter.api.Test; import javax.sql.DataSource; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeRetentionMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeRetentionMySqlIT.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeRetentionMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeRetentionMySqlIT.java index 5b265764358..829ce09c989 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeRetentionMySqlIT.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeRetentionMySqlIT.java @@ -1,8 +1,11 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; + +import com.alibaba.qwen.code.runtimebroker.DriverManagerDataSource; +import org.junit.jupiter.api.Test; + +import javax.sql.DataSource; import java.util.UUID; -import javax.sql.DataSource; -import org.junit.jupiter.api.Test; class JdbcRuntimeRetentionMySqlIT { @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeRetentionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeRetentionTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeRetentionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeRetentionTest.java index 159ff825164..6d06cf2310a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/JdbcRuntimeRetentionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/JdbcRuntimeRetentionTest.java @@ -1,8 +1,22 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeDrainReceipt; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.RuntimeRecoveryEvidence; +import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSession; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.JdbcRuntimeBrokerSchema; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchemaTime; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistorySqlTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/ManagedCheckpointHistorySqlTest.java similarity index 90% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistorySqlTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/ManagedCheckpointHistorySqlTest.java index 194cefaca64..482d9072ef4 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ManagedCheckpointHistorySqlTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/ManagedCheckpointHistorySqlTest.java @@ -1,8 +1,13 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ManagedSessionStoreModels; import com.alibaba.qwen.code.managedagent.persistence.ManagedSchemaJdbcTemplate; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; -import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStoreModels; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; +import com.alibaba.qwen.code.runtimebroker.ManagedCheckpointHistory; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; import com.fasterxml.jackson.databind.JsonNode; @@ -16,6 +21,7 @@ import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.support.TransactionTemplate; import javax.sql.DataSource; + import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.List; @@ -39,12 +45,12 @@ public class ManagedCheckpointHistorySqlTest { } @BeforeEach - void setUp() throws Exception { + public void setUp() throws Exception { var source = dataSource(); ManagedSchema.initialize(source); jdbc = new ManagedSchemaJdbcTemplate(source); transaction = new TransactionTemplate(new DataSourceTransactionManager(source)); - var store = new ManagedSessionStore(jdbc); + var store = com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)); String session = UUID.randomUUID().toString(); original = new JdbcCsiFilesRetirementGuard.Original(CsiFilesRetirementProfile.request(new ContextBinding( "tenant", "workspace", 1, "storage", ".", CsiFilesRetirementProfile.CONTEXT_CONFIG_REF, 1), @@ -80,7 +86,7 @@ public class ManagedCheckpointHistorySqlTest { } @Test - void pageChannelReadsEarlierCommittedOwnershipWhileOrdinaryReadsRemainExact() { + public void pageChannelReadsEarlierCommittedOwnershipWhileOrdinaryReadsRemainExact() { transaction.execute(status -> jdbc.execute((org.springframework.jdbc.core.ConnectionCallback) connection -> { var reader = JdbcCsiActivationAdmission.checkpointReader(connection, original, 2, JdbcCsiActivationAdmission.JournalRead.LIVE); @@ -96,7 +102,7 @@ public class ManagedCheckpointHistorySqlTest { } @Test - void hydratesACommittedPageFromAnEarlierRevision() throws Exception { + public void hydratesACommittedPageFromAnEarlierRevision() throws Exception { ObjectNode root = commitCheckpoint(); transaction.execute(status -> jdbc.execute((org.springframework.jdbc.core.ConnectionCallback) connection -> { var sql = JdbcCsiActivationAdmission.checkpointReader(connection, original, 2, @@ -117,7 +123,7 @@ public class ManagedCheckpointHistorySqlTest { } @Test - void proofReadsEarlierPagesWhileActiveReferencesStillRequireCurrentOwnership() throws Exception { + public void proofReadsEarlierPagesWhileActiveReferencesStillRequireCurrentOwnership() throws Exception { ObjectNode root = commitCheckpoint(); ObjectNode ref = resource("root", "managed-checkpoint", root.toString()); ref.remove("bytesBase64"); @@ -169,7 +175,7 @@ public class ManagedCheckpointHistorySqlTest { .put("recordBytesBase64", Base64.getEncoder().encodeToString(records.getBytes(StandardCharsets.UTF_8))); commit.putArray("resources").add(active).add(resource("root", "managed-checkpoint", root.toString())).add(pageRef); var request = JSON.treeToValue(commit, ManagedSessionStoreModels.CommitTransactionRequest.class); - transaction.execute(status -> new ManagedSessionStore(jdbc).commit("tenant", original.request().getIsolationKey(), + transaction.execute(status -> com.alibaba.qwen.code.managedagent.VerificationFixtures.configure(new ManagedSessionStore(jdbc)).commit("tenant", original.request().getIsolationKey(), "a".repeat(32), request)); return root; } @@ -182,13 +188,13 @@ public class ManagedCheckpointHistorySqlTest { } @Test - void ownershipWithoutItsCommittedTransactionCannotBeReused() { + public void ownershipWithoutItsCommittedTransactionCannotBeReused() { jdbc.update("DELETE FROM ma_session_journal_tx"); assertNoHistory(); } @Test - void committedOwnershipAfterThePinnedRevisionCannotBeReused() { + public void committedOwnershipAfterThePinnedRevisionCannotBeReused() { jdbc.update("UPDATE ma_session_journal_tx SET journal_revision = 99"); jdbc.update("UPDATE ma_resource_ref SET owner_revision = 99, journal_revision = 99"); assertNoHistory(); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ResidentWorkspaceAdmissionTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/ResidentWorkspaceAdmissionTest.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ResidentWorkspaceAdmissionTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/ResidentWorkspaceAdmissionTest.java index ba818b45218..25927c9ad35 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ResidentWorkspaceAdmissionTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/ResidentWorkspaceAdmissionTest.java @@ -1,6 +1,14 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.ResidentWorkspaceProfile; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ResidentWorkspacePlacementTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/ResidentWorkspacePlacementTest.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ResidentWorkspacePlacementTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/ResidentWorkspacePlacementTest.java index fa3025f664c..d9d8db6cd24 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/ResidentWorkspacePlacementTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/ResidentWorkspacePlacementTest.java @@ -1,7 +1,14 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; +import com.alibaba.qwen.code.runtimebroker.ResidentWorkspaceProfile; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedschema.ManagedSchema; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerHttpServerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/RuntimeBrokerHttpServerTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerHttpServerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/RuntimeBrokerHttpServerTest.java index 63a7f87002f..3180058465b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerHttpServerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/RuntimeBrokerHttpServerTest.java @@ -1,11 +1,28 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; import com.alibaba.fastjson2.JSON; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.HarnessSessionResolver; +import com.alibaba.qwen.code.runtimebroker.ResidentWorkspaceProfile; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimePublicationGrant; +import com.alibaba.qwen.code.runtimebroker.RuntimePublicationVerifier; +import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSession; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.http.RuntimeBrokerHttpServer; import com.alibaba.qwen.code.runtimebroker.managedschema.JdbcRuntimeBrokerSchema; import com.alibaba.qwen.code.runtimebroker.provisioning.StaticRuntimeProvisioner; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.security.ProtectedSecret; -import com.alibaba.qwen.code.runtimebroker.transport.RuntimeBrokerHttpServer; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.Test; import java.lang.reflect.Proxy; @@ -37,7 +54,7 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -class RuntimeBrokerHttpServerTest { +public class RuntimeBrokerHttpServerTest { @Test void capabilityReadsResolveAdmissionWithoutAllocatingOrAcquiringARuntime() throws Exception { var scope = new RuntimeScope("tenant", "workspace", "1", "/workspace", @@ -1150,7 +1167,7 @@ class RuntimeBrokerHttpServerTest { @Test @SuppressWarnings("unchecked") - void answersAStoreFailureAsRetryableServiceUnavailable() throws Exception { + public void answersAStoreFailureAsRetryableServiceUnavailable() throws Exception { InMemoryToolExecutionRepository inner = new InMemoryToolExecutionRepository(Clock.systemUTC()); AtomicBoolean failing = new AtomicBoolean(true); AtomicReference failingMethod = new AtomicReference<>("findByIdempotencyKey"); @@ -1430,7 +1447,7 @@ class RuntimeBrokerHttpServerTest { @Test @SuppressWarnings("unchecked") - void answersAPrivateCsiStoreFailureAsRetryableServiceUnavailable() throws Exception { + public void answersAPrivateCsiStoreFailureAsRetryableServiceUnavailable() throws Exception { String session = "550e8400-e29b-41d4-a716-446655440313"; RuntimeProvisionRequest request = CsiFilesRetirementProfile.request( new com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding( @@ -1735,7 +1752,7 @@ class RuntimeBrokerHttpServerTest { // The HTTP face's automatic observation reuses the freshest answer for // a short cooldown; a changed Runtime answer is observed once it lapses. private static void awaitObservationCooldown() throws InterruptedException { - Thread.sleep(RuntimeBrokerService.UNKNOWN_LOOKUP_COOLDOWN.toMillis() + Thread.sleep(com.alibaba.qwen.code.runtimebroker.service.BrokerServiceTestAccess.UNKNOWN_LOOKUP_COOLDOWN.toMillis() + 100); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerTestAccess.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/RuntimeBrokerTestAccess.java similarity index 64% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerTestAccess.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/RuntimeBrokerTestAccess.java index 464fbb9c140..b776d1824fd 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerTestAccess.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/RuntimeBrokerTestAccess.java @@ -1,4 +1,18 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; + +import com.alibaba.qwen.code.runtimebroker.HarnessSessionResolver; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.RuntimeRecoveryEvidence; +import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import javax.sql.DataSource; @@ -47,12 +61,12 @@ public final class RuntimeBrokerTestAccess { RuntimeBindingRepository bindings, RuntimeSessionRepository runtimeSessions, ToolExecutionRepository executions, String owner, Duration operationLease, Duration dispatchLease, Clock clock, Supplier executionIds) { - return new RuntimeBrokerService(sessions, provisioner, transport, bindings, + return com.alibaba.qwen.code.runtimebroker.service.BrokerServiceTestAccess.create(sessions, provisioner, transport, bindings, runtimeSessions, executions, owner, operationLease, dispatchLease, clock, executionIds); } @FunctionalInterface public interface SqlWork { - T run(Connection connection) throws SQLException; + public T run(Connection connection) throws SQLException; } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/SharedHarnessOwnershipTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/SharedHarnessOwnershipTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/SharedHarnessOwnershipTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/SharedHarnessOwnershipTest.java index ba1cfde87d5..8394b3275f5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/SharedHarnessOwnershipTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/persistence/SharedHarnessOwnershipTest.java @@ -1,6 +1,21 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.persistence; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSession; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ManagedContextProtocolTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/protocol/ManagedContextProtocolTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ManagedContextProtocolTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/protocol/ManagedContextProtocolTest.java index 4a6539e8e54..9834e4c45a5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ManagedContextProtocolTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/protocol/ManagedContextProtocolTest.java @@ -1,13 +1,14 @@ -package com.alibaba.qwen.code.runtimebroker.provisioning; +package com.alibaba.qwen.code.runtimebroker.protocol; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.ManagedRuntimeAttestationConformanceTest; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; -import com.alibaba.qwen.code.runtimebroker.transport.ManagedRuntimeAttestationConformanceTest; +import com.alibaba.qwen.code.runtimebroker.provisioning.LocalProcessRuntimeProvisioner; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/DurableLocalProcessRuntimeProvisionerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/DurableLocalProcessRuntimeProvisionerTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/DurableLocalProcessRuntimeProvisionerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/DurableLocalProcessRuntimeProvisionerTest.java index f58efa15602..1391bb7a9a2 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/DurableLocalProcessRuntimeProvisionerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/DurableLocalProcessRuntimeProvisionerTest.java @@ -1,12 +1,7 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerTestAccess; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeObservation; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; @@ -14,7 +9,12 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.RuntimeBrokerTestAccess; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; @@ -269,7 +269,7 @@ class DurableLocalProcessRuntimeProvisionerTest { void operatorStopRequiresTheExactWorkerToExitBeforeTombstoning() throws Exception { var scope = new RuntimeScope("tenant", "workspace", "1", directory.toString(), WorkspaceExecutionProfile.CAPABILITY_DIGEST, "session"); - var request = new RuntimeProvisionRequest(scope, "harness", LocalProcessRuntimeProvisioner.KIND, + var request = new RuntimeProvisionRequest(scope, "harness", com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, "storage:a"); var store = store(); try (var provisioner = provisioner(store)) { @@ -312,7 +312,7 @@ class DurableLocalProcessRuntimeProvisionerTest { void operatorStopCanCompleteAfterSameHostReboot() throws Exception { var scope = new RuntimeScope("tenant", "workspace", "1", directory.toString(), WorkspaceExecutionProfile.CAPABILITY_DIGEST, "session"); - var request = new RuntimeProvisionRequest(scope, "harness", LocalProcessRuntimeProvisioner.KIND, + var request = new RuntimeProvisionRequest(scope, "harness", com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, "storage:a"); var store = store(); try (var original = provisioner(store)) { @@ -561,8 +561,8 @@ class DurableLocalProcessRuntimeProvisionerTest { var lease = launch(provisioner, store, request); assertEquals(RuntimeObservation.Outcome.CONFLICT, await(provisioner.reconcile(request(true), SEED, handle, lease)).getOutcome()); - var old = new RuntimeResourceHandle(LocalProcessRuntimeProvisioner.KIND, 1, - Map.of("provider", LocalProcessRuntimeProvisioner.KIND)); + var old = new RuntimeResourceHandle(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, 1, + Map.of("provider", com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS)); assertFalse(provisioner.supportsStartupRecovery(old)); assertEquals(RuntimeObservation.Outcome.UNKNOWN, await(provisioner.reconcile(request, SEED, old, lease)).getOutcome()); @@ -816,7 +816,7 @@ class DurableLocalProcessRuntimeProvisionerTest { private RuntimeProvisionRequest closeRequest() { return new RuntimeProvisionRequest(new RuntimeScope("tenant", "workspace", "1", directory.toAbsolutePath().toString(), WorkspaceExecutionProfile.CAPABILITY_DIGEST, "session"), - "harness", LocalProcessRuntimeProvisioner.KIND, "storage:a"); + "harness", com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, "storage:a"); } private RuntimeBindingRecord closeBinding(RuntimeProvisionRequest request, RuntimeResourceHandle handle, @@ -828,7 +828,7 @@ class DurableLocalProcessRuntimeProvisionerTest { private RuntimeProvisionRequest request(boolean managed) { return new RuntimeProvisionRequest(new RuntimeScope("tenant", "workspace", "1", directory.toAbsolutePath().toString(), "sha256:" + "a".repeat(64), "workspace"), - null, LocalProcessRuntimeProvisioner.KIND, managed ? "storage:a" : null); + null, com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, managed ? "storage:a" : null); } private LocalRuntimeStore store() throws Exception { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalHttpRuntimeClientTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalHttpRuntimeClientTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalHttpRuntimeClientTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalHttpRuntimeClientTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisionerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisionerTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisionerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisionerTest.java index cb570cdbaf4..48acbb10e62 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisionerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalRuntimeProvisionerTest.java @@ -1,19 +1,20 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeObservation; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; +import com.alibaba.qwen.code.runtimebroker.spi.ExternalRuntimeClient; import com.sun.net.httpserver.HttpServer; import org.junit.jupiter.api.Test; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceBootContractTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceBootContractTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceBootContractTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceBootContractTest.java index df7df9780c1..1843c3d01ea 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceBootContractTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceBootContractTest.java @@ -1,9 +1,12 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.spi.ExternalRuntimeClient; +import com.alibaba.qwen.code.runtimebroker.spi.ExternalStorageRegistry; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -112,7 +115,7 @@ class ExternalWorkspaceBootContractTest { @Timeout(120) @DisabledOnOs(OS.WINDOWS) void theShippedWorkerParserAdmitsTheMintedBoot() throws Exception { - Path repoRoot = repoRoot(); + Path repoRoot = VerificationFixtures.repositoryRoot(); Assumptions.assumeTrue(Files.isRegularFile(repoRoot.resolve( "packages/cli/src/serve/managed-runtime-attestation-worker.ts")), "needs the qwen-code checkout this module lives in"); @@ -145,12 +148,6 @@ class ExternalWorkspaceBootContractTest { assertTrue(refused.stderr().contains("boot payload is invalid"), refused.stderr()); } - private static Path repoRoot() { - Path module = Path.of(System.getProperty("user.dir")).toAbsolutePath().normalize(); - return module.getParent() == null || module.getParent().getParent() == null - ? module : module.getParent().getParent(); - } - private static void privateDirectory(Path directory, Set permissions) throws IOException { Files.setPosixFilePermissions(directory, permissions); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisionerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisionerTest.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisionerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisionerTest.java index 0a667690cd4..ee2c37d7b14 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisionerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/ExternalWorkspaceProvisionerTest.java @@ -9,6 +9,9 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.protocol.ExternalRuntimeKinds; +import com.alibaba.qwen.code.runtimebroker.spi.ExternalRuntimeClient; +import com.alibaba.qwen.code.runtimebroker.spi.ExternalStorageRegistry; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -76,7 +79,7 @@ class ExternalWorkspaceProvisionerTest { private static RuntimeProvisionRequest request() { return new RuntimeProvisionRequest(scope(CAPABILITY), "isolation-a", - ExternalWorkspaceProvisioner.KIND, ALIAS); + com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE, ALIAS); } private static RuntimeProvisionSeed seed(String name) { @@ -120,7 +123,7 @@ class ExternalWorkspaceProvisionerTest { ManagedExtensionExecutionProfile.LINUX_CAPABILITY_DIGEST, ManagedExtensionExecutionProfile.DEVELOPER_CAPABILITY_DIGEST)) { RuntimeProvisionRequest plain = subject.createRequest(scope(capability), "isolation-a"); - assertEquals(ExternalWorkspaceProvisioner.KIND, plain.getProvisionerKind()); + assertEquals(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE, plain.getProvisionerKind()); assertFalse(plain.isManagedContext(), capability); RuntimeBrokerException refused = failure( subject.ensureResource(plain, seed("plain-scope"), null)); @@ -144,7 +147,7 @@ class ExternalWorkspaceProvisionerTest { @Test void theSelectedAliasMakesTheRequestManagedContext() { RuntimeProvisionRequest request = provisioner(null).createRequest(scope(CAPABILITY), "isolation-a"); - assertEquals(ExternalWorkspaceProvisioner.KIND, request.getProvisionerKind()); + assertEquals(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE, request.getProvisionerKind()); assertTrue(request.isManagedContext()); assertEquals(ALIAS, request.getStorageId()); } @@ -154,7 +157,7 @@ class ExternalWorkspaceProvisionerTest { // A harness-Session caller that resolved no storage must not have a // storage-bound placement invented for it. RuntimeProvisionRequest plain = new RuntimeProvisionRequest(scope(CAPABILITY), - "isolation-a", ExternalWorkspaceProvisioner.KIND); + "isolation-a", com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE); RuntimeBrokerException refused = failure( provisioner(registration(PROVIDER, 1, MOUNT)).ensureResource(plain, seed("plain"), null)); assertEquals("external_workspace_storage_required", refused.getCode()); @@ -193,7 +196,7 @@ class ExternalWorkspaceProvisionerTest { void aCapabilityDigestTheWorkerRefusesIsRefusedHere() { String notWorkspace = "sha256:" + "b".repeat(64); RuntimeProvisionRequest request = new RuntimeProvisionRequest(scope(notWorkspace), - "isolation-a", ExternalWorkspaceProvisioner.KIND, ALIAS); + "isolation-a", com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE, ALIAS); RuntimeBrokerException refused = failure( provisioner(registration(PROVIDER, 1, MOUNT)).ensureResource(request, seed("cap"), null)); assertEquals("external_storage_registration_mismatch", refused.getCode()); @@ -384,14 +387,14 @@ class ExternalWorkspaceProvisionerTest { @Test void theFamilyIsNamedOnce() { - assertTrue(ExternalRuntimeKinds.isExternal(ExternalRuntimeProvisioner.KIND)); - assertTrue(ExternalRuntimeKinds.isExternal(ExternalWorkspaceProvisioner.KIND)); + assertTrue(ExternalRuntimeKinds.isExternal(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH)); + assertTrue(ExternalRuntimeKinds.isExternal(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE)); assertFalse(ExternalRuntimeKinds.isExternal("local-process")); assertFalse(ExternalRuntimeKinds.isExternal("external-workspace-typo")); - assertFalse(ExternalRuntimeKinds.isWorkspace(ExternalRuntimeProvisioner.KIND)); - assertTrue(ExternalRuntimeKinds.isWorkspace(ExternalWorkspaceProvisioner.KIND)); - assertTrue(ExternalRuntimeKinds.supported().contains(ExternalRuntimeProvisioner.KIND)); - assertTrue(ExternalRuntimeKinds.supported().contains(ExternalWorkspaceProvisioner.KIND)); + assertFalse(ExternalRuntimeKinds.isWorkspace(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH)); + assertTrue(ExternalRuntimeKinds.isWorkspace(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE)); + assertTrue(ExternalRuntimeKinds.supported().contains(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH)); + assertTrue(ExternalRuntimeKinds.supported().contains(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_WORKSPACE)); } @Test @@ -399,7 +402,7 @@ class ExternalWorkspaceProvisionerTest { ExternalWorkspaceProvisioner subject = provisioner(registration(PROVIDER, 1, MOUNT)); Placement placement = place(subject, "handle"); RuntimeResourceHandle scratchShaped = new RuntimeResourceHandle( - ExternalRuntimeProvisioner.KIND, placement.handle().getVersion(), + com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.EXTERNAL_SCRATCH, placement.handle().getVersion(), placement.handle().getValue()); assertTrue(subject.supportsStartupRecovery(placement.handle())); assertFalse(subject.supportsStartupRecovery(scratchShaped)); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/FaultGateBroker.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/FaultGateBroker.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/FaultGateBroker.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/FaultGateBroker.java index 9b960935ad7..d2cd8073ccf 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/FaultGateBroker.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/FaultGateBroker.java @@ -4,21 +4,21 @@ import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.alibaba.qwen.code.runtimebroker.DriverManagerDataSource; import com.alibaba.qwen.code.runtimebroker.FaultGateTransport; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RecoverableProcessProvisioner; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransportTestAccess; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransportTestAccess; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import javax.sql.DataSource; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReaderTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReaderTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReaderTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReaderTest.java index 122e6fba489..6fb31da662c 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReaderTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesCsiLogReaderTest.java @@ -1,6 +1,7 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.spi.KubernetesRuntimeClient; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClientTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClientTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClientTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClientTest.java index 548fcd29c7f..38b6a02fbd8 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClientTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesHttpRuntimeClientTest.java @@ -20,8 +20,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; import java.util.Arrays; -import java.util.Map; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.CountDownLatch; @@ -31,11 +31,11 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisionerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisionerTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisionerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisionerTest.java index ba4d3b5cea8..e1bf6573a45 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisionerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/KubernetesRuntimeProvisionerTest.java @@ -1,16 +1,16 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; import com.alibaba.qwen.code.runtimebroker.FakeKubernetesRuntimeClient; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerTestAccess; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeObservation; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.RuntimeBrokerTestAccess; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -35,15 +35,15 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class KubernetesRuntimeProvisionerTest { public static final String IMAGE = "registry.example/qwen@sha256:" + "a".repeat(64); - static final List COMMAND = List.of("node", "/opt/qwen/dist/cli.js"); - static final RuntimeProvisionSeed SEED = RuntimeProvisionSeed.create("binding", 1); + public static final List COMMAND = List.of("node", "/opt/qwen/dist/cli.js"); + public static final RuntimeProvisionSeed SEED = RuntimeProvisionSeed.create("binding", 1); - static KubernetesRuntimeProvisioner provisioner(FakeKubernetesRuntimeClient client) { + public static KubernetesRuntimeProvisioner provisioner(FakeKubernetesRuntimeClient client) { return new KubernetesRuntimeProvisioner(client, "cluster-a", "runtimes", IMAGE, COMMAND, Duration.ofMillis(250)); } - static RuntimeProvisionRequest request(KubernetesRuntimeProvisioner provisioner, String directory) { + public static RuntimeProvisionRequest request(KubernetesRuntimeProvisioner provisioner, String directory) { return provisioner.createRequest(new RuntimeScope("tenant", "workspace", "1", directory, "sha256:" + "a".repeat(64), "session"), "harness"); } @@ -190,7 +190,7 @@ public class KubernetesRuntimeProvisionerTest { @ParameterizedTest @ValueSource(strings = {"missing-pod", "missing-all", "replaced-pod"}) - void pendingAdmissionNeverShadowsTheSuppliedOriginalHandle(String mutation) { + public void pendingAdmissionNeverShadowsTheSuppliedOriginalHandle(String mutation) { var api = new FakeKubernetesRuntimeClient(); RuntimeResourceHandle original; RuntimeProvisionRequest request; @@ -414,20 +414,20 @@ public class KubernetesRuntimeProvisionerTest { } } - static Map container(FakeKubernetesRuntimeClient api) { + public static Map container(FakeKubernetesRuntimeClient api) { return map(((List) map(api.object("pods").get("spec")).get("containers")).getFirst()); } - static Map status(FakeKubernetesRuntimeClient api) { + public static Map status(FakeKubernetesRuntimeClient api) { return map(((List) map(api.object("pods").get("status")).get("containerStatuses")).getFirst()); } - static RuntimeBrokerException failure(CompletionStage result) { + public static RuntimeBrokerException failure(CompletionStage result) { return (RuntimeBrokerException) assertThrows(CompletionException.class, () -> result.toCompletableFuture().join()).getCause(); } - static T join(CompletionStage result) { + public static T join(CompletionStage result) { return result.toCompletableFuture().join(); } } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisionerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisionerTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisionerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisionerTest.java index 5d0da20c950..63cca67df2a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisionerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessRuntimeProvisionerTest.java @@ -2,13 +2,9 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.DelegatingBindingRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeAttestation; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeObservation; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; @@ -19,7 +15,12 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocolTest; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -75,7 +76,7 @@ public class LocalProcessRuntimeProvisionerTest { @org.junit.jupiter.api.condition.DisabledOnOs(org.junit.jupiter.api.condition.OS.WINDOWS) @Test - void refusesPrivateProfileBeforeDurableRegistration( + public void refusesPrivateProfileBeforeDurableRegistration( @org.junit.jupiter.api.io.TempDir Path directory) throws Exception { Path root = directory.toRealPath(); assertPrivateProfileRefused(root, @@ -242,7 +243,7 @@ public class LocalProcessRuntimeProvisionerTest { @Test void pinsThePersistedKind() { - assertEquals("local-process", LocalProcessRuntimeProvisioner.KIND); + assertEquals("local-process", com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS); } @Test @@ -251,7 +252,7 @@ public class LocalProcessRuntimeProvisionerTest { try (LocalProcessRuntimeProvisioner provisioner = new LocalProcessRuntimeProvisioner(List.of("node"), Path.of("").toAbsolutePath(), transport)) { - assertEquals(LocalProcessRuntimeProvisioner.KIND, + assertEquals(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, provisioner.kind()); RuntimeScope scope = new RuntimeScope("tenant-a", "workspace-a", "7", "/runtime/workspace", DIGEST, "workspace"); @@ -262,7 +263,7 @@ public class LocalProcessRuntimeProvisionerTest { RuntimeResourceHandle handle = provisioner .ensureResource(request, seed, null) .toCompletableFuture().get(2, TimeUnit.SECONDS); - assertEquals(LocalProcessRuntimeProvisioner.KIND, + assertEquals(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, handle.getKind()); RuntimeLease missing = new RuntimeLease( seed.getProvisionalRuntimeId(), @@ -364,7 +365,7 @@ public class LocalProcessRuntimeProvisionerTest { RuntimeScope scope = new RuntimeScope("tenant-a", "workspace-a", "7", "/runtime/workspace", DIGEST, "workspace"); RuntimeProvisionRequest request = new RuntimeProvisionRequest( - scope, null, LocalProcessRuntimeProvisioner.KIND); + scope, null, com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS); RuntimeProvisionSeed firstSeed = RuntimeProvisionSeed.create( "binding-1", 1); RuntimeProvisionSeed nextSeed = sameSeed ? firstSeed @@ -423,7 +424,7 @@ public class LocalProcessRuntimeProvisionerTest { RuntimeScope scope = new RuntimeScope("tenant-a", "workspace-a", "7", "/runtime/workspace", DIGEST, "workspace"); RuntimeProvisionRequest request = new RuntimeProvisionRequest( - scope, null, LocalProcessRuntimeProvisioner.KIND); + scope, null, com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS); RuntimeProvisionSeed seed = RuntimeProvisionSeed.create( "binding-1", 1); Set before = childPids(); @@ -778,7 +779,7 @@ public class LocalProcessRuntimeProvisionerTest { RuntimeScope scope = new RuntimeScope("tenant-a", "workspace-a", "7", "/runtime/workspace", DIGEST, "workspace"); RuntimeProvisionRequest request = new RuntimeProvisionRequest( - scope, null, LocalProcessRuntimeProvisioner.KIND); + scope, null, com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS); RuntimeProvisionSeed seed = RuntimeProvisionSeed.create( "binding-1", 1); Set before = childPids(); @@ -831,7 +832,7 @@ public class LocalProcessRuntimeProvisionerTest { RuntimeScope scope = new RuntimeScope("tenant-a", "workspace-a", "7", "/runtime/workspace", DIGEST, "workspace"); RuntimeProvisionRequest request = new RuntimeProvisionRequest( - scope, null, LocalProcessRuntimeProvisioner.KIND); + scope, null, com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS); RuntimeProvisionSeed seed = RuntimeProvisionSeed.create( "binding-1", 1); Set before = childPids(); @@ -913,7 +914,7 @@ public class LocalProcessRuntimeProvisionerTest { RuntimeScope scope = new RuntimeScope("tenant-a", "workspace-a", "7", "/runtime/workspace", DIGEST, "workspace"); RuntimeProvisionRequest request = new RuntimeProvisionRequest( - scope, null, LocalProcessRuntimeProvisioner.KIND); + scope, null, com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS); RuntimeProvisionSeed seed = RuntimeProvisionSeed.create( "binding-1", 1); Set before = childPids(); @@ -1018,7 +1019,7 @@ public class LocalProcessRuntimeProvisionerTest { RuntimeScope scope = new RuntimeScope("tenant-a", "workspace-a", "7", "/runtime/workspace", DIGEST, "workspace"); RuntimeProvisionRequest request = new RuntimeProvisionRequest( - scope, null, LocalProcessRuntimeProvisioner.KIND); + scope, null, com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS); RuntimeProvisionSeed seed = RuntimeProvisionSeed.create( "binding-1", 1); Set before = childPids(); @@ -1040,8 +1041,8 @@ public class LocalProcessRuntimeProvisionerTest { assertEquals(RuntimeObservation.Outcome.CONFLICT, foreignSeed.getOutcome()); RuntimeResourceHandle v2Handle = new RuntimeResourceHandle( - LocalProcessRuntimeProvisioner.KIND, 2, - java.util.Map.of("provider", LocalProcessRuntimeProvisioner.KIND)); + com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, 2, + java.util.Map.of("provider", com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS)); RuntimeObservation foreignVersion = provisioner .reconcile(request, seed, v2Handle, lease) .toCompletableFuture().get(10, TimeUnit.SECONDS); @@ -1142,7 +1143,7 @@ public class LocalProcessRuntimeProvisionerTest { finishLivenessCheck = new java.util.concurrent.CountDownLatch(1); private final long pid; - BlockingExitProcess(long pid) { + public BlockingExitProcess(long pid) { this.pid = pid; } @@ -1209,7 +1210,7 @@ public class LocalProcessRuntimeProvisionerTest { */ private static final class FencingBindingRepository extends DelegatingBindingRepository { - FencingBindingRepository() { + public FencingBindingRepository() { super(new InMemoryRuntimeBindingRepository()); } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessStopExecutorTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessStopExecutorTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessStopExecutorTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessStopExecutorTest.java index a9442cb5535..0a2169b2aab 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessStopExecutorTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalProcessStopExecutorTest.java @@ -6,7 +6,7 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; @@ -41,7 +41,7 @@ class LocalProcessStopExecutorTest { var store = new LocalRuntimeStore(directory.toRealPath(), host); var scope = new RuntimeScope("tenant", "workspace", "1", directory.toAbsolutePath().toString(), WorkspaceExecutionProfile.CAPABILITY_DIGEST, "session"); - var request = new RuntimeProvisionRequest(scope, "harness", LocalProcessRuntimeProvisioner.KIND, + var request = new RuntimeProvisionRequest(scope, "harness", com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, "storage:a"); var seed = RuntimeProvisionSeed.create("binding", 1); var started = new CountDownLatch(parallelism); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRebootTestSupport.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRebootTestSupport.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRebootTestSupport.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRebootTestSupport.java index 20b3a0a079d..a8e2c407977 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRebootTestSupport.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRebootTestSupport.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import java.nio.file.Path; import java.util.List; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStoreTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStoreTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStoreTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStoreTest.java index d92720c9ec3..834ba7b944f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStoreTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/LocalRuntimeStoreTest.java @@ -1,6 +1,6 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import org.junit.jupiter.api.Test; import java.nio.file.Files; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/MacOsHostIdentityTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/MacOsHostIdentityTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/MacOsHostIdentityTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/MacOsHostIdentityTest.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RoutingRuntimeProvisionerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RoutingRuntimeProvisionerTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RoutingRuntimeProvisionerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RoutingRuntimeProvisionerTest.java index 18b62be2c11..140c353339c 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RoutingRuntimeProvisionerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RoutingRuntimeProvisionerTest.java @@ -1,12 +1,8 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeAttestation; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeDrainReceipt; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeObservation; @@ -18,6 +14,10 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.junit.jupiter.api.Test; import java.net.URI; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RuntimeHarnessDrainMySqlIT.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RuntimeHarnessDrainMySqlIT.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RuntimeHarnessDrainMySqlIT.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RuntimeHarnessDrainMySqlIT.java diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RuntimeHarnessDrainTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RuntimeHarnessDrainTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RuntimeHarnessDrainTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RuntimeHarnessDrainTest.java index 5e301abbf65..941f9b6d856 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RuntimeHarnessDrainTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/RuntimeHarnessDrainTest.java @@ -2,18 +2,10 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.HarnessSessionResolver; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryToolExecutionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.JdbcToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeAttestation; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerTestAccess; import com.alibaba.qwen.code.runtimebroker.RuntimeDrainReceipt; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority; @@ -31,8 +23,16 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.security.AesGcmSecretProtector; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.security.AesGcmSecretProtector; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.JdbcToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.RuntimeBrokerTestAccess; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManagerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManagerTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManagerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManagerTest.java index 06750721125..9ab4cbe9060 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManagerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshConnectionManagerTest.java @@ -3,6 +3,7 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeNode; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeInteropTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeInteropTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeInteropTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeInteropTest.java index 67890fb1701..783cbf61db5 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeInteropTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeInteropTest.java @@ -1,13 +1,10 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.runtimebroker.CsiNativeActivationProof; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeBindingRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeSessionRepository; -import com.alibaba.qwen.code.runtimebroker.InMemoryToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.alibaba.qwen.code.runtimebroker.RuntimeAttestation; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; -import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerService; import com.alibaba.qwen.code.runtimebroker.RuntimeDrainReceipt; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; @@ -18,8 +15,16 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeSession; import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ProviderRuntimeProtocol; +import com.alibaba.qwen.code.runtimebroker.service.RuntimeBrokerService; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeNode; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeStorage; import com.sun.net.httpserver.HttpServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -78,7 +83,7 @@ class SshRuntimeInteropTest { void setUp() throws Exception { root = temporary.toRealPath(); controlDirectory = Files.createTempDirectory(Path.of("/tmp"), "qws-").toRealPath(); - Path repository = Path.of("").toAbsolutePath().resolve("../..").normalize(); + Path repository = VerificationFixtures.repositoryRoot(); Path entry = repository.resolve("packages/cli/dist/index.js"); assertTrue(Files.isRegularFile(entry), "Build the CLI before running SSH interop"); String executable = command(List.of("node", "-p", "process.execPath")).strip(); @@ -393,7 +398,7 @@ class SshRuntimeInteropTest { "payload-digest", reference)); var claim = executions.claimDispatch(prepared.getExecutionCallId(), "dispatcher", Duration.ofMinutes(1)); if (deferred) { - var fixtures = JsonCodec.parseObject(Files.readAllBytes(Path.of("").toAbsolutePath() + var fixtures = JsonCodec.parseObject(Files.readAllBytes(VerificationFixtures.moduleRoot() .resolve("../core/src/managed-runtime/contracts/managed-tool-publication-v1.fixtures.json").normalize()), "publication fixture"); Map publication = ((List) fixtures.get("cases")).stream() .map(ProviderRuntimeProtocol::object).filter(item -> "distinct-model-runtime-ids".equals(item.get("id"))) @@ -657,7 +662,7 @@ class SshRuntimeInteropTest { (String) boot.get("capabilityDigest"), (String) boot.get("isolationClass")); var seed = new RuntimeProvisionSeed((String) boot.get("provisionRequestId"), (String) boot.get("runtimeInstanceId"), (String) boot.get("runtimeIncarnation"), (String) boot.get("leaseId"), ((Number) boot.get("epoch")).longValue(), (String) boot.get("token")); - var placement = new RuntimeProvisionRequest(scope, "harness", SshRuntimeProvisioner.KIND, (String) boot.get("storageId")); + var placement = new RuntimeProvisionRequest(scope, "harness", com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS, (String) boot.get("storageId")); var lease = new RuntimeLease(seed.getProvisionalRuntimeId(), ManagedContextProtocol.ready(ready, boot), seed.getToken(), seed.getLeaseId(), seed.getEpoch()); manager.register(lease, node); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisionerTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisionerTest.java similarity index 95% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisionerTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisionerTest.java index 0f04850ae89..49a75b4bc5e 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisionerTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/SshRuntimeProvisionerTest.java @@ -1,6 +1,5 @@ package com.alibaba.qwen.code.runtimebroker.provisioning; -import com.alibaba.qwen.code.runtimebroker.InMemoryRuntimeBindingRepository; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; @@ -10,7 +9,12 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ProviderRuntimeProtocol; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeNode; +import com.alibaba.qwen.code.runtimebroker.spi.SshRuntimeStorage; import com.sun.net.httpserver.HttpServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -37,7 +41,6 @@ import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; import static org.junit.jupiter.api.Assertions.assertTrue; @DisabledOnOs(OS.WINDOWS) @@ -198,21 +201,11 @@ class SshRuntimeProvisionerTest { for (int i = 0; i < 8; i++) controls.add(limited.confirm(request, lease)); var closing = CompletableFuture.runAsync(limited::close); try { - assertTimeoutPreemptively(Duration.ofSeconds(2), () -> { - while (true) { - try { - manager.registerDirect(lease); - } catch (RuntimeBrokerException closed) { - assertEquals("ssh_runtime_unavailable", closed.getCode()); - return; - } - Thread.sleep(1); - } - }); + closing.get(2, TimeUnit.SECONDS); + manager.registerDirect(lease); } finally { release.countDown(); } - closing.get(3, TimeUnit.SECONDS); for (var control : controls) assertThrows(ExecutionException.class, () -> await(control)); } finally { release.countDown(); @@ -352,7 +345,7 @@ class SshRuntimeProvisionerTest { var changed = new LinkedHashMap<>(handle.getValue()); changed.put("nodeId", "other-node"); assertThrows(ExecutionException.class, () -> await(provisioner.ensureResource(request, seed, - new RuntimeResourceHandle(SshRuntimeProvisioner.KIND, 1, changed)))); + new RuntimeResourceHandle(com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.SSH_PROCESS, 1, changed)))); assertFalse(provisioner.supportsStartupRecovery(new RuntimeResourceHandle("static", 1, Map.of()))); assertThrows(RuntimeBrokerException.class, () -> provisioner.createRequest(new RuntimeScope("tenant", "workspace", "1", "/workspace/project", WorkspaceExecutionProfile.CAPABILITY_DIGEST, "workspace"), null)); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkerEnvironmentRoutingTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkerEnvironmentRoutingTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkerEnvironmentRoutingTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkerEnvironmentRoutingTest.java index 748ed33bfd9..5aec805126b 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkerEnvironmentRoutingTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkerEnvironmentRoutingTest.java @@ -4,7 +4,7 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.transport.HttpRuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; @@ -91,7 +91,7 @@ class WorkerEnvironmentRoutingTest { private RuntimeProvisionRequest request() { return new RuntimeProvisionRequest(new RuntimeScope("tenant", "workspace", "1", directory.toAbsolutePath().toString(), WorkspaceExecutionProfile.CAPABILITY_DIGEST, "session"), - "harness", LocalProcessRuntimeProvisioner.KIND, "storage:a"); + "harness", com.alibaba.qwen.code.runtimebroker.protocol.RuntimeProviderKinds.LOCAL_PROCESS, "storage:a"); } private static Throwable awaitFailure(CompletionStage launch) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkspaceFileProcessTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkspaceFileProcessTest.java similarity index 100% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkspaceFileProcessTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/provisioning/WorkspaceFileProcessTest.java diff --git a/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/service/BrokerServiceTestAccess.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/service/BrokerServiceTestAccess.java new file mode 100644 index 00000000000..e08197904fa --- /dev/null +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/service/BrokerServiceTestAccess.java @@ -0,0 +1,46 @@ +package com.alibaba.qwen.code.runtimebroker.service; + +import com.alibaba.qwen.code.runtimebroker.HarnessSessionResolver; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.RuntimePublicationVerifier; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; + +import java.time.Clock; +import java.time.Duration; +import java.util.function.Supplier; + +public final class BrokerServiceTestAccess { + private BrokerServiceTestAccess() { } + + public static RuntimeBrokerService create(HarnessSessionResolver sessionResolver, + RuntimeProvisioner provisioner, RuntimeTransport transport, + RuntimeBindingRepository bindingRepository, + RuntimeSessionRepository sessionRepository, + ToolExecutionRepository executionRepository, + String brokerOwnerId, Duration operationLeaseDuration, + Duration dispatchLeaseDuration, + RuntimePublicationVerifier publicationVerifier, + Duration v3ResultWindow) { + return new RuntimeBrokerService(sessionResolver, provisioner, transport, bindingRepository, + sessionRepository, executionRepository, brokerOwnerId, operationLeaseDuration, + dispatchLeaseDuration, publicationVerifier, v3ResultWindow); + } + + public static RuntimeBrokerService create(HarnessSessionResolver sessionResolver, + RuntimeProvisioner provisioner, RuntimeTransport transport, + RuntimeBindingRepository bindingRepository, + RuntimeSessionRepository sessionRepository, + ToolExecutionRepository executionRepository, + String brokerOwnerId, Duration operationLeaseDuration, + Duration dispatchLeaseDuration, Clock clock, + Supplier executionIdSupplier) { + return new RuntimeBrokerService(sessionResolver, provisioner, transport, bindingRepository, + sessionRepository, executionRepository, brokerOwnerId, operationLeaseDuration, + dispatchLeaseDuration, clock, executionIdSupplier); + } + public static long pollDelay(int attempt) { return RuntimeBrokerService.v3PollDelayMillis(attempt); } + public static final java.time.Duration UNKNOWN_LOOKUP_COOLDOWN = RuntimeBrokerService.UNKNOWN_LOOKUP_COOLDOWN; +} diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerServiceBindingReadTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/service/RuntimeBrokerServiceBindingReadTest.java similarity index 91% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerServiceBindingReadTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/service/RuntimeBrokerServiceBindingReadTest.java index 22f65d389a6..54e264f86cd 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerServiceBindingReadTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/service/RuntimeBrokerServiceBindingReadTest.java @@ -1,10 +1,18 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.service; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; +import com.alibaba.qwen.code.runtimebroker.HarnessSessionResolver; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import org.junit.jupiter.api.Test; import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.URI; import java.time.Duration; @@ -12,11 +20,12 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; /** The live-binding-only read the relay and the cascade bond to. */ class RuntimeBrokerServiceBindingReadTest { - private static T stub(Class iface) { return iface.cast(Proxy.newProxyInstance(iface.getClassLoader(), new Class[] {iface}, (proxy, method, args) -> { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerServiceTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/service/RuntimeBrokerServiceTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerServiceTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/service/RuntimeBrokerServiceTest.java index 2de90c35385..f0d4a28c67f 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/RuntimeBrokerServiceTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/service/RuntimeBrokerServiceTest.java @@ -1,5 +1,37 @@ -package com.alibaba.qwen.code.runtimebroker; +package com.alibaba.qwen.code.runtimebroker.service; +import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; +import com.alibaba.qwen.code.runtimebroker.DelegatingBindingRepository; +import com.alibaba.qwen.code.runtimebroker.ExecutionReconciliation; +import com.alibaba.qwen.code.runtimebroker.HarnessSessionResolver; +import com.alibaba.qwen.code.runtimebroker.RuntimeAttestation; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; +import com.alibaba.qwen.code.runtimebroker.RuntimeLease; +import com.alibaba.qwen.code.runtimebroker.RuntimeLifecycleAuthority; +import com.alibaba.qwen.code.runtimebroker.RuntimeObservation; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; +import com.alibaba.qwen.code.runtimebroker.RuntimeProvisioner; +import com.alibaba.qwen.code.runtimebroker.RuntimePublicationGrant; +import com.alibaba.qwen.code.runtimebroker.RuntimePublicationVerifier; +import com.alibaba.qwen.code.runtimebroker.RuntimeRecoveryContract; +import com.alibaba.qwen.code.runtimebroker.RuntimeRecoveryEvidence; +import com.alibaba.qwen.code.runtimebroker.RuntimeResourceHandle; +import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.RuntimeSession; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRecord; +import com.alibaba.qwen.code.runtimebroker.RuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.RuntimeTransport; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRecord; +import com.alibaba.qwen.code.runtimebroker.ToolExecutionRepository; +import com.alibaba.qwen.code.runtimebroker.ToolHoldRow; +import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeBindingRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryRuntimeSessionRepository; +import com.alibaba.qwen.code.runtimebroker.persistence.InMemoryToolExecutionRepository; import com.alibaba.qwen.code.runtimebroker.provisioning.StaticRuntimeProvisioner; import org.junit.jupiter.api.Test; @@ -85,7 +117,7 @@ class RuntimeBrokerServiceTest { "callId", "call", "argsDigest", "sha256:" + "b".repeat(64))); fixture.executionRepository.findOrCreate(candidate); var execution = fixture.executionRepository.claimDispatch("execution", "owner", Duration.ofMinutes(1)); - execution = fixture.executionRepository.authorizeDispatch(execution, "owner", execution.getDispatchGeneration(), binding.getVersion()); + execution = com.alibaba.qwen.code.runtimebroker.persistence.InMemoryTestAccess.authorizeDispatch(fixture.executionRepository, execution, "owner", execution.getDispatchGeneration(), binding.getVersion()); assertEquals("csi_execution_cancel_unavailable", failure(fixture.service.cancelExecution( PROVIDER_SESSION, PROVIDER_SESSION, "execution")).getCode()); assertSame(execution, fixture.executionRepository.findByExecutionCallId("execution")); @@ -3396,7 +3428,6 @@ class RuntimeBrokerServiceTest { }; } - @Test void drainReleaseSettlesARunningBackgroundShellOnceTheStopProvesIt() throws Exception { String payload = "{\"toolName\":\"run_shell_command\",\"input\":{\"command\":\"tail -f\",\"is_background\":true}}"; @@ -7483,7 +7514,6 @@ class RuntimeBrokerServiceTest { delegate = new InMemoryToolExecutionRepository(clock); } - @Override public ToolExecutionRecord settlePrepared(ToolExecutionRecord expected, Map result, Instant settlementTime) { @@ -7626,7 +7656,6 @@ class RuntimeBrokerServiceTest { delegate = new InMemoryToolExecutionRepository(clock); } - @Override public ToolExecutionRecord settlePrepared(ToolExecutionRecord expected, Map result, Instant settlementTime) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocolTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocolTest.java similarity index 96% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocolTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocolTest.java index dd1b6f97155..e42d53dcf17 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocolTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileFinalizeProtocolTest.java @@ -1,24 +1,25 @@ package com.alibaba.qwen.code.runtimebroker.transport; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; +import com.alibaba.qwen.code.runtimebroker.BrokerValues; +import com.alibaba.qwen.code.runtimebroker.JsonCodec; +import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; +import org.junit.jupiter.api.Test; + +import java.nio.file.Files; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.alibaba.qwen.code.runtimebroker.BrokerValues; -import com.alibaba.qwen.code.runtimebroker.JsonCodec; -import com.alibaba.qwen.code.runtimebroker.WorkspaceCsiRuntimeIdentity; -import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; - class CsiFileFinalizeProtocolTest { private static Map fixture() throws Exception { return ManagedCsiFilesProtocol.parse(Files.readAllBytes( - Path.of("../cli/src/serve/contracts/managed-csi-file-finalize-v1.fixtures.json"))); + VerificationFixtures.moduleRoot().resolve("../cli/src/serve/contracts/managed-csi-file-finalize-v1.fixtures.json"))); } @Test diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileHistoryProtocolTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileHistoryProtocolTest.java similarity index 89% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileHistoryProtocolTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileHistoryProtocolTest.java index 48f7a7dad86..cdc46822a66 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileHistoryProtocolTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiFileHistoryProtocolTest.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.runtimebroker.transport; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; -import java.nio.file.Path; import java.util.LinkedHashMap; import java.util.Map; @@ -15,7 +15,7 @@ class CsiFileHistoryProtocolTest { @Test void sharesEmptyAndInheritedPreimagePinsAndRejectsCrossedEvidence() throws Exception { var json = new ObjectMapper(); - var fixture = json.readTree(Path.of("../cli/src/serve/contracts/managed-csi-file-history-v1.fixtures.json").toFile()); + var fixture = json.readTree(VerificationFixtures.moduleRoot().resolve("../cli/src/serve/contracts/managed-csi-file-history-v1.fixtures.json").toFile()); String owner = fixture.path("request").path("identity").path("sessionId").textValue(); for (var value : fixture.path("valid")) { assertDoesNotThrow(() -> CsiFileHistoryProtocol.observation(value.path("observation"), owner)); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocolTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocolTest.java similarity index 93% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocolTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocolTest.java index c6df9e941e1..1e00bda7527 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocolTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/CsiNativeReadbackProtocolTest.java @@ -1,12 +1,12 @@ package com.alibaba.qwen.code.runtimebroker.transport; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.JsonCodec; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Path; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -17,8 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class CsiNativeReadbackProtocolTest { @Test void sharesAllThreeClosedActionsAndTheNullablePreparedRule() throws Exception { - var fixture = JsonCodec.parseObject(Files.readAllBytes(Path.of( - "../cli/src/serve/contracts/managed-csi-native-readback-v1.fixtures.json")), "shared fixture"); + var fixture = JsonCodec.parseObject(Files.readAllBytes(VerificationFixtures.moduleRoot().resolve("../cli/src/serve/contracts/managed-csi-native-readback-v1.fixtures.json")), "shared fixture"); for (Object candidate : (List) fixture.get("valid")) { var value = ManagedCsiFilesProtocolTest.map(candidate); var request = CsiNativeReadbackProtocol.request(JsonCodec.encode(ManagedCsiFilesProtocolTest.map(value.get("request")))); @@ -93,8 +92,7 @@ public class CsiNativeReadbackProtocolTest { } @Test void acceptsWorkerSizedUnicodeResultsAndRejectsAnOverLimitBodyOrForeignSubject() throws Exception { - var fixture = JsonCodec.parseObject(Files.readAllBytes(Path.of( - "../cli/src/serve/contracts/managed-csi-native-readback-v1.fixtures.json")), "fixture"); + var fixture = JsonCodec.parseObject(Files.readAllBytes(VerificationFixtures.moduleRoot().resolve("../cli/src/serve/contracts/managed-csi-native-readback-v1.fixtures.json")), "fixture"); var value = ((List) fixture.get("valid")).stream().map(ManagedCsiFilesProtocolTest::map) .filter(row -> "execute-read-only".equals(row.get("name"))).findFirst().orElseThrow(); var request = CsiNativeReadbackProtocol.request(ManagedCsiFilesProtocolTest.map(value.get("request"))); @@ -117,8 +115,7 @@ public class CsiNativeReadbackProtocolTest { @Test void appliesTheCheckpointReaderLimitWithoutWideningOrdinaryReferences() throws Exception { - var fixture = JsonCodec.parseObject(Files.readAllBytes(Path.of( - "../cli/src/serve/contracts/managed-csi-native-readback-v1.fixtures.json")), "fixture"); + var fixture = JsonCodec.parseObject(Files.readAllBytes(VerificationFixtures.moduleRoot().resolve("../cli/src/serve/contracts/managed-csi-native-readback-v1.fixtures.json")), "fixture"); var valid = ((List) fixture.get("valid")).stream().map(ManagedCsiFilesProtocolTest::map).toList(); var execute = valid.stream().filter(row -> "execute".equals(row.get("name"))).findFirst().orElseThrow(); var request = CsiNativeReadbackProtocol.request(ManagedCsiFilesProtocolTest.map(execute.get("request"))); @@ -155,5 +152,4 @@ public class CsiNativeReadbackProtocolTest { public static Map executionResult(String text) { return Map.of("executionStatus", "success", "responseParts", List.of(Map.of("text", text))); } - } diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocolTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocolTest.java similarity index 97% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocolTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocolTest.java index 802b74b023b..b7a5c071e83 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocolTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiFilesProtocolTest.java @@ -1,16 +1,16 @@ package com.alibaba.qwen.code.runtimebroker.transport; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; import com.alibaba.qwen.code.runtimebroker.BrokerValues; import com.alibaba.qwen.code.runtimebroker.CsiFilesRetirementProfile; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.WorkspaceExecutionProfile; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import org.junit.jupiter.api.Test; import java.nio.file.Files; -import java.nio.file.Path; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -23,7 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class ManagedCsiFilesProtocolTest { public static Map fixtures() throws Exception { return ManagedCsiFilesProtocol.parse(Files.readAllBytes( - Path.of("../cli/src/serve/contracts/managed-csi-files-v2.fixtures.json"))); + VerificationFixtures.moduleRoot().resolve("../cli/src/serve/contracts/managed-csi-files-v2.fixtures.json"))); } public static RuntimeProvisionRequest request(Map fixture) { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocolTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocolTest.java similarity index 90% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocolTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocolTest.java index 1c2c0244590..cfebccf9b74 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocolTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedCsiProtocolTest.java @@ -1,10 +1,10 @@ package com.alibaba.qwen.code.runtimebroker.transport; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.managedagent.VerificationFixtures; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import org.junit.jupiter.api.Test; import java.nio.file.Files; -import java.nio.file.Path; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -14,7 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; class ManagedCsiProtocolTest { @Test void replaysTheSameClosedEnvelopeAndIndependentPhysicalKeyFixturesAsTypescript() throws Exception { - var path = Path.of("../cli/src/serve/contracts/managed-csi-v1.fixtures.json"); + var path = VerificationFixtures.moduleRoot().resolve("../cli/src/serve/contracts/managed-csi-v1.fixtures.json"); var fixtures = ManagedContextProtocol.parse(Files.readAllBytes(path)); assertEquals(ManagedCsiProtocol.PROTOCOL, fixtures.get("managedCsi")); assertEquals(ManagedCsiProtocol.ATTEST_PATH, fixtures.get("attestPath")); @@ -43,7 +43,7 @@ class ManagedCsiProtocolTest { @Test void verifiesIndependentReservationAndMountReceiptsWithoutExtendingContextRecords() throws Exception { var fixtures = ManagedContextProtocol.parse(Files.readAllBytes( - Path.of("../cli/src/serve/contracts/managed-csi-v1.fixtures.json"))); + VerificationFixtures.moduleRoot().resolve("../cli/src/serve/contracts/managed-csi-v1.fixtures.json"))); var boot = map(fixtures.get("boot")); var pod = map(fixtures.get("expectedPod")); assertEquals(fixtures.get("attestationRequest"), ManagedCsiProtocol.attestationRequest(boot)); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocolTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocolTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocolTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocolTest.java index 956c568315b..de58b1d3820 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocolTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionProtocolTest.java @@ -6,6 +6,7 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeBrokerException; import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; import com.alibaba.qwen.code.runtimebroker.RuntimeSession; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import org.junit.jupiter.api.Test; import java.net.URI; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionTransportTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionTransportTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionTransportTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionTransportTest.java index e81cf28042e..40d4a2f8b97 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionTransportTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedExtensionTransportTest.java @@ -7,8 +7,9 @@ import com.alibaba.qwen.code.runtimebroker.RuntimeLease; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionRequest; import com.alibaba.qwen.code.runtimebroker.RuntimeProvisionSeed; import com.alibaba.qwen.code.runtimebroker.RuntimeScope; +import com.alibaba.qwen.code.runtimebroker.adapter.HttpRuntimeTransport; import com.alibaba.qwen.code.runtimebroker.managedworkspace.ContextBinding; -import com.alibaba.qwen.code.runtimebroker.provisioning.ManagedContextProtocol; +import com.alibaba.qwen.code.runtimebroker.protocol.ManagedContextProtocol; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.net.httpserver.HttpServer; diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocolTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocolTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocolTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocolTest.java index e1184b8e56b..ae693071c47 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocolTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedMonitorProtocolTest.java @@ -12,7 +12,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class ManagedMonitorProtocolTest { - private static RuntimeSession session() { return new RuntimeSession("harness", "runtime", "bootstrap", new RuntimeScope("tenant", "workspace", "gen", "/workspace", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocolTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocolTest.java similarity index 99% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocolTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocolTest.java index 4e59e7b7502..964d0177a46 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocolTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/transport/ManagedShellProtocolTest.java @@ -12,7 +12,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class ManagedShellProtocolTest { - private static RuntimeSession session() { return new RuntimeSession("harness", "runtime", "bootstrap", new RuntimeScope("tenant", "workspace", "gen", "/workspace", diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ConfiguredWorkspaceRegistryTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/workspace/ConfiguredWorkspaceRegistryTest.java similarity index 98% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ConfiguredWorkspaceRegistryTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/workspace/ConfiguredWorkspaceRegistryTest.java index 6d420b73a88..f3172621d57 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/ConfiguredWorkspaceRegistryTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/workspace/ConfiguredWorkspaceRegistryTest.java @@ -1,4 +1,14 @@ -package com.alibaba.qwen.code.runtimebroker.managedworkspace; +package com.alibaba.qwen.code.runtimebroker.workspace; + +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceRecord; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceState; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.OTHER_TENANT; import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.TENANT; @@ -9,13 +19,6 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; - class ConfiguredWorkspaceRegistryTest { @Test void findsOnlyWithinTheTenant() { diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/GrantedWorkspaceAccessPolicyTest.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/workspace/GrantedWorkspaceAccessPolicyTest.java similarity index 94% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/GrantedWorkspaceAccessPolicyTest.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/workspace/GrantedWorkspaceAccessPolicyTest.java index d27bb6e547a..fccd880b06a 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/runtimebroker/managedworkspace/GrantedWorkspaceAccessPolicyTest.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/runtimebroker/workspace/GrantedWorkspaceAccessPolicyTest.java @@ -1,4 +1,10 @@ -package com.alibaba.qwen.code.runtimebroker.managedworkspace; +package com.alibaba.qwen.code.runtimebroker.workspace; + +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceAccess; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceActor; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceRecord; +import com.alibaba.qwen.code.runtimebroker.managedworkspace.WorkspaceState; +import org.junit.jupiter.api.Test; import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.OTHER_TENANT; import static com.alibaba.qwen.code.runtimebroker.managedworkspace.TestWorkspaces.TENANT; @@ -9,8 +15,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Test; - class GrantedWorkspaceAccessPolicyTest { private static final WorkspaceRecord WORKSPACE = workspace(TENANT, "ws-a", WorkspaceState.ACTIVE); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/managedprobes/ProbeAdvice.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/managedprobes/ProbeAdvice.java similarity index 92% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/managedprobes/ProbeAdvice.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/managedprobes/ProbeAdvice.java index d3c02d12878..930b9987006 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/managedprobes/ProbeAdvice.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/managedprobes/ProbeAdvice.java @@ -23,8 +23,8 @@ import java.util.Map; @Order(Ordered.HIGHEST_PRECEDENCE) public final class ProbeAdvice { @ExceptionHandler(IllegalArgumentException.class) - ResponseEntity> probeInvalid(IllegalArgumentException error, - HttpServletRequest request) { + public ResponseEntity> + probeInvalid(IllegalArgumentException error, HttpServletRequest request) { System.out.println("PROBE_CONTRACT_REFUSAL " + request.getMethod() + " " + request.getRequestURI() + " query=" + request.getQueryString()); error.printStackTrace(System.out); diff --git a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/managedprobes/PublicationProbeWiring.java b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/managedprobes/PublicationProbeWiring.java similarity index 75% rename from packages/managed-agent-server/src/test/java/com/alibaba/qwen/managedprobes/PublicationProbeWiring.java rename to packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/managedprobes/PublicationProbeWiring.java index 2472a27eed4..a6b077e9aa2 100644 --- a/packages/managed-agent-server/src/test/java/com/alibaba/qwen/managedprobes/PublicationProbeWiring.java +++ b/packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/managedprobes/PublicationProbeWiring.java @@ -1,11 +1,11 @@ package com.alibaba.qwen.managedprobes; import com.alibaba.qwen.code.managedagent.api.ToolPublicationController; -import com.alibaba.qwen.code.managedagent.artifact.ToolPublicationObjectStore; import com.alibaba.qwen.code.managedagent.config.ManagedAgentProperties; +import com.alibaba.qwen.code.managedagent.contracts.artifact.ToolPublicationObjectStore; +import com.alibaba.qwen.code.managedagent.contracts.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.persistence.ManagedSessionStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationAdmissionStore; -import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationContract; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationDataStore; import com.alibaba.qwen.code.managedagent.persistence.ToolPublicationStore; import com.alibaba.qwen.code.runtimebroker.RuntimeBindingRepository; @@ -41,23 +41,29 @@ public final class PublicationProbeWiring { private static final long ACTIVE_CAPTURES = 8; @Bean - ToolPublicationObjectStore publicationObjectStore() { + public ToolPublicationObjectStore publicationObjectStore() { return new InMemoryObjectStore(); } @Bean - ToolPublicationStore publicationStore(JdbcTemplate jdbc, - PlatformTransactionManager manager, ManagedSessionStore sessions, - ToolExecutionRepository executions, RuntimeBindingRepository bindings) { + public ToolPublicationStore publicationStore( + JdbcTemplate jdbc, + PlatformTransactionManager manager, + ManagedSessionStore sessions, + ToolExecutionRepository executions, + RuntimeBindingRepository bindings) { return new ToolPublicationStore(jdbc, manager, sessions, executions, bindings, new ToolPublicationStore.Capacity(EXECUTION_BYTES, SESSION_BYTES, TENANT_BYTES, ACTIVE_CAPTURES)); } @Bean - ToolPublicationDataStore publicationDataStore(JdbcTemplate jdbc, - PlatformTransactionManager manager, ToolPublicationStore grants, - ManagedSessionStore sessions, ToolPublicationObjectStore objects) { + public ToolPublicationDataStore publicationDataStore( + JdbcTemplate jdbc, + PlatformTransactionManager manager, + ToolPublicationStore grants, + ManagedSessionStore sessions, + ToolPublicationObjectStore objects) { var operationTimeout = Duration.ofSeconds(60); var budget = new ToolPublicationDataStore.VerificationBudget( 1_048_576L, Duration.ofMinutes(10)); @@ -68,20 +74,24 @@ public final class PublicationProbeWiring { } @Bean - ToolPublicationAdmissionStore publicationAdmissionStore(JdbcTemplate jdbc, - PlatformTransactionManager manager, ManagedSessionStore sessions, + public ToolPublicationAdmissionStore publicationAdmissionStore( + JdbcTemplate jdbc, + PlatformTransactionManager manager, + ManagedSessionStore sessions, ToolPublicationDataStore data) { return new ToolPublicationAdmissionStore(jdbc, manager, sessions, data); } @Bean - ToolPublicationController publicationController(ToolPublicationStore grants, - ToolPublicationDataStore data, ToolPublicationAdmissionStore admissions, + public ToolPublicationController publicationController( + ToolPublicationStore grants, + ToolPublicationDataStore data, + ToolPublicationAdmissionStore admissions, ManagedAgentProperties properties) { return new ToolPublicationController(grants, data, admissions, properties); } - static final class InMemoryObjectStore implements ToolPublicationObjectStore { + public static final class InMemoryObjectStore implements ToolPublicationObjectStore { private final ConcurrentHashMap objects = new ConcurrentHashMap<>(); @Override diff --git a/packages/managed-agent-server/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/packages/managed-agent-server/verification/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension similarity index 100% rename from packages/managed-agent-server/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension rename to packages/managed-agent-server/verification/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension diff --git a/packages/managed-agent-server/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener b/packages/managed-agent-server/verification/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener similarity index 100% rename from packages/managed-agent-server/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener rename to packages/managed-agent-server/verification/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener diff --git a/packages/managed-agent-server/src/test/resources/capability-parity/README.md b/packages/managed-agent-server/verification/src/test/resources/capability-parity/README.md similarity index 100% rename from packages/managed-agent-server/src/test/resources/capability-parity/README.md rename to packages/managed-agent-server/verification/src/test/resources/capability-parity/README.md diff --git a/packages/managed-agent-server/src/test/resources/capability-parity/managed-surface.txt b/packages/managed-agent-server/verification/src/test/resources/capability-parity/managed-surface.txt similarity index 100% rename from packages/managed-agent-server/src/test/resources/capability-parity/managed-surface.txt rename to packages/managed-agent-server/verification/src/test/resources/capability-parity/managed-surface.txt diff --git a/packages/managed-agent-server/src/test/resources/csi-hosted-file-turns.mjs b/packages/managed-agent-server/verification/src/test/resources/csi-hosted-file-turns.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/csi-hosted-file-turns.mjs rename to packages/managed-agent-server/verification/src/test/resources/csi-hosted-file-turns.mjs diff --git a/packages/managed-agent-server/src/test/resources/csi-native-activation-fixture.json b/packages/managed-agent-server/verification/src/test/resources/csi-native-activation-fixture.json similarity index 100% rename from packages/managed-agent-server/src/test/resources/csi-native-activation-fixture.json rename to packages/managed-agent-server/verification/src/test/resources/csi-native-activation-fixture.json diff --git a/packages/managed-agent-server/src/test/resources/csi-native-activation-generator.mjs b/packages/managed-agent-server/verification/src/test/resources/csi-native-activation-generator.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/csi-native-activation-generator.mjs rename to packages/managed-agent-server/verification/src/test/resources/csi-native-activation-generator.mjs diff --git a/packages/managed-agent-server/src/test/resources/csi-native-final-output-fixture.json b/packages/managed-agent-server/verification/src/test/resources/csi-native-final-output-fixture.json similarity index 100% rename from packages/managed-agent-server/src/test/resources/csi-native-final-output-fixture.json rename to packages/managed-agent-server/verification/src/test/resources/csi-native-final-output-fixture.json diff --git a/packages/managed-agent-server/src/test/resources/csi-native-final-output-generator.mjs b/packages/managed-agent-server/verification/src/test/resources/csi-native-final-output-generator.mjs similarity index 98% rename from packages/managed-agent-server/src/test/resources/csi-native-final-output-generator.mjs rename to packages/managed-agent-server/verification/src/test/resources/csi-native-final-output-generator.mjs index 2b9e6270870..09cc97d42f6 100644 --- a/packages/managed-agent-server/src/test/resources/csi-native-final-output-generator.mjs +++ b/packages/managed-agent-server/verification/src/test/resources/csi-native-final-output-generator.mjs @@ -33,7 +33,7 @@ const { createManagedHarnessHandle } = await load( const { TurnBudget } = await load('core/turn-budget'); const seed = JSON.parse( await readFile( - 'packages/managed-agent-server/src/test/resources/csi-native-read-continuation-fixture.json', + 'packages/managed-agent-server/verification/src/test/resources/csi-native-read-continuation-fixture.json', 'utf8', ), ); diff --git a/packages/managed-agent-server/src/test/resources/csi-native-function-parts-baseline.json b/packages/managed-agent-server/verification/src/test/resources/csi-native-function-parts-baseline.json similarity index 100% rename from packages/managed-agent-server/src/test/resources/csi-native-function-parts-baseline.json rename to packages/managed-agent-server/verification/src/test/resources/csi-native-function-parts-baseline.json diff --git a/packages/managed-agent-server/src/test/resources/csi-native-json-fixture.json b/packages/managed-agent-server/verification/src/test/resources/csi-native-json-fixture.json similarity index 100% rename from packages/managed-agent-server/src/test/resources/csi-native-json-fixture.json rename to packages/managed-agent-server/verification/src/test/resources/csi-native-json-fixture.json diff --git a/packages/managed-agent-server/src/test/resources/csi-native-json-generator.mjs b/packages/managed-agent-server/verification/src/test/resources/csi-native-json-generator.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/csi-native-json-generator.mjs rename to packages/managed-agent-server/verification/src/test/resources/csi-native-json-generator.mjs diff --git a/packages/managed-agent-server/src/test/resources/csi-native-read-continuation-fixture.json b/packages/managed-agent-server/verification/src/test/resources/csi-native-read-continuation-fixture.json similarity index 100% rename from packages/managed-agent-server/src/test/resources/csi-native-read-continuation-fixture.json rename to packages/managed-agent-server/verification/src/test/resources/csi-native-read-continuation-fixture.json diff --git a/packages/managed-agent-server/src/test/resources/csi-native-resource-schema-fixture.sql b/packages/managed-agent-server/verification/src/test/resources/csi-native-resource-schema-fixture.sql similarity index 100% rename from packages/managed-agent-server/src/test/resources/csi-native-resource-schema-fixture.sql rename to packages/managed-agent-server/verification/src/test/resources/csi-native-resource-schema-fixture.sql diff --git a/packages/managed-agent-server/src/test/resources/csi-native-text-fixture.json b/packages/managed-agent-server/verification/src/test/resources/csi-native-text-fixture.json similarity index 100% rename from packages/managed-agent-server/src/test/resources/csi-native-text-fixture.json rename to packages/managed-agent-server/verification/src/test/resources/csi-native-text-fixture.json diff --git a/packages/managed-agent-server/src/test/resources/csi-native-text-turns.mjs b/packages/managed-agent-server/verification/src/test/resources/csi-native-text-turns.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/csi-native-text-turns.mjs rename to packages/managed-agent-server/verification/src/test/resources/csi-native-text-turns.mjs diff --git a/packages/managed-agent-server/src/test/resources/csi-sql-worker-acceptance.mjs b/packages/managed-agent-server/verification/src/test/resources/csi-sql-worker-acceptance.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/csi-sql-worker-acceptance.mjs rename to packages/managed-agent-server/verification/src/test/resources/csi-sql-worker-acceptance.mjs diff --git a/packages/managed-agent-server/src/test/resources/db/managed-schema/legacy-table-map.tsv b/packages/managed-agent-server/verification/src/test/resources/db/managed-schema/legacy-table-map.tsv similarity index 100% rename from packages/managed-agent-server/src/test/resources/db/managed-schema/legacy-table-map.tsv rename to packages/managed-agent-server/verification/src/test/resources/db/managed-schema/legacy-table-map.tsv diff --git a/packages/managed-agent-server/src/test/resources/fake-attestation-worker.mjs b/packages/managed-agent-server/verification/src/test/resources/fake-attestation-worker.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/fake-attestation-worker.mjs rename to packages/managed-agent-server/verification/src/test/resources/fake-attestation-worker.mjs diff --git a/packages/managed-agent-server/src/test/resources/hosted-definition-census.mjs b/packages/managed-agent-server/verification/src/test/resources/hosted-definition-census.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/hosted-definition-census.mjs rename to packages/managed-agent-server/verification/src/test/resources/hosted-definition-census.mjs diff --git a/packages/managed-agent-server/src/test/resources/hosted-extension-process.mjs b/packages/managed-agent-server/verification/src/test/resources/hosted-extension-process.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/hosted-extension-process.mjs rename to packages/managed-agent-server/verification/src/test/resources/hosted-extension-process.mjs diff --git a/packages/managed-agent-server/src/test/resources/hosted-hook-fault-process.mjs b/packages/managed-agent-server/verification/src/test/resources/hosted-hook-fault-process.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/hosted-hook-fault-process.mjs rename to packages/managed-agent-server/verification/src/test/resources/hosted-hook-fault-process.mjs diff --git a/packages/managed-agent-server/src/test/resources/hosted-mcp-fault-process.mjs b/packages/managed-agent-server/verification/src/test/resources/hosted-mcp-fault-process.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/hosted-mcp-fault-process.mjs rename to packages/managed-agent-server/verification/src/test/resources/hosted-mcp-fault-process.mjs diff --git a/packages/managed-agent-server/src/test/resources/managed-media-retention-generator.mjs b/packages/managed-agent-server/verification/src/test/resources/managed-media-retention-generator.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/managed-media-retention-generator.mjs rename to packages/managed-agent-server/verification/src/test/resources/managed-media-retention-generator.mjs diff --git a/packages/managed-agent-server/src/test/resources/openapi/contract-known-gaps.txt b/packages/managed-agent-server/verification/src/test/resources/openapi/contract-known-gaps.txt similarity index 100% rename from packages/managed-agent-server/src/test/resources/openapi/contract-known-gaps.txt rename to packages/managed-agent-server/verification/src/test/resources/openapi/contract-known-gaps.txt diff --git a/packages/managed-agent-server/src/test/resources/original-managed-table-inventory.tsv b/packages/managed-agent-server/verification/src/test/resources/original-managed-table-inventory.tsv similarity index 100% rename from packages/managed-agent-server/src/test/resources/original-managed-table-inventory.tsv rename to packages/managed-agent-server/verification/src/test/resources/original-managed-table-inventory.tsv diff --git a/packages/managed-agent-server/src/test/resources/recovery-initial-journal.mjs b/packages/managed-agent-server/verification/src/test/resources/recovery-initial-journal.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/recovery-initial-journal.mjs rename to packages/managed-agent-server/verification/src/test/resources/recovery-initial-journal.mjs diff --git a/packages/managed-agent-server/src/test/resources/resident-acceptance/hosted-and-model.mjs b/packages/managed-agent-server/verification/src/test/resources/resident-acceptance/hosted-and-model.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/resident-acceptance/hosted-and-model.mjs rename to packages/managed-agent-server/verification/src/test/resources/resident-acceptance/hosted-and-model.mjs diff --git a/packages/managed-agent-server/src/test/resources/resident-acceptance/resident-model.mjs b/packages/managed-agent-server/verification/src/test/resources/resident-acceptance/resident-model.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/resident-acceptance/resident-model.mjs rename to packages/managed-agent-server/verification/src/test/resources/resident-acceptance/resident-model.mjs diff --git a/packages/managed-agent-server/src/test/resources/resident-acceptance/resident-tool-evidence.mjs b/packages/managed-agent-server/verification/src/test/resources/resident-acceptance/resident-tool-evidence.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/resident-acceptance/resident-tool-evidence.mjs rename to packages/managed-agent-server/verification/src/test/resources/resident-acceptance/resident-tool-evidence.mjs diff --git a/packages/managed-agent-server/src/test/resources/resident-acceptance/resident-tool-evidence.test.mjs b/packages/managed-agent-server/verification/src/test/resources/resident-acceptance/resident-tool-evidence.test.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/resident-acceptance/resident-tool-evidence.test.mjs rename to packages/managed-agent-server/verification/src/test/resources/resident-acceptance/resident-tool-evidence.test.mjs diff --git a/packages/managed-agent-server/src/test/resources/tool-v3-interop-server.mjs b/packages/managed-agent-server/verification/src/test/resources/tool-v3-interop-server.mjs similarity index 100% rename from packages/managed-agent-server/src/test/resources/tool-v3-interop-server.mjs rename to packages/managed-agent-server/verification/src/test/resources/tool-v3-interop-server.mjs diff --git a/packages/web-shell/client/e2e/managed-recovery.spec.ts b/packages/web-shell/client/e2e/managed-recovery.spec.ts index 50038c19d03..6beedbbf387 100644 --- a/packages/web-shell/client/e2e/managed-recovery.spec.ts +++ b/packages/web-shell/client/e2e/managed-recovery.spec.ts @@ -302,6 +302,9 @@ test('new Session handoff is editable, preselected and never automatically sent page, }) => { const scenario = await installRecovery(page); + await expect( + page.getByText('Confirmed inspection complete', { exact: true }), + ).toBeVisible(); await page .getByRole('button', { name: 'Continue in a new Session', exact: true }) .click(); diff --git a/packages/web-shell/scripts/generate-managed-agent-api.mjs b/packages/web-shell/scripts/generate-managed-agent-api.mjs index 1a871622b88..071c4f6c63a 100644 --- a/packages/web-shell/scripts/generate-managed-agent-api.mjs +++ b/packages/web-shell/scripts/generate-managed-agent-api.mjs @@ -12,7 +12,7 @@ import { fileURLToPath } from 'node:url'; import openapiTS, { astToString } from 'openapi-typescript'; const SPEC = new URL( - '../../managed-agent-server/src/main/resources/openapi/managed-agent-public-api.openapi.json', + '../../managed-agent-server/contracts/src/main/resources/openapi/managed-agent-public-api.openapi.json', import.meta.url, ); export const OUTPUT = new URL( diff --git a/scripts/check-contract-open-prs.js b/scripts/check-contract-open-prs.js index a7085a75896..111d89e2bd9 100644 --- a/scripts/check-contract-open-prs.js +++ b/scripts/check-contract-open-prs.js @@ -35,6 +35,7 @@ import { promisify } from 'node:util'; import { CONTRACT_DOCUMENT, CONTRACT_DOCUMENT_PREDECESSOR, + CONTRACT_DOCUMENT_MONOLITH, compareVersions, documentVersion, } from './contract-version-utils.js'; @@ -189,6 +190,7 @@ async function claims(repo, prs, main, budget, gh) { for (const pr of prs) { const touched = pr.files.find((file) => file.path === CONTRACT_DOCUMENT) ?? + pr.files.find((file) => file.path === CONTRACT_DOCUMENT_MONOLITH) ?? pr.files.find((file) => file.path === CONTRACT_DOCUMENT_PREDECESSOR); if (!touched) continue; if (touched.changeType === 'DELETED') { diff --git a/scripts/check-failsafe-reports.js b/scripts/check-failsafe-reports.js index a4a908aa5db..34cee6487dc 100644 --- a/scripts/check-failsafe-reports.js +++ b/scripts/check-failsafe-reports.js @@ -56,7 +56,7 @@ const simpleName = (className) => className.split('.').at(-1); // class here, and keep the pom in agreement — the check below fails if a name // is not bound to a surefire include and a failsafe exclude in that module. const SUREFIRE_BOUND = new Map([ - ['packages/managed-agent-server', ['ManagedAgentPackagingIT']], + ['packages/managed-agent-server/verification', ['ManagedAgentPackagingIT']], ]); // The module argument is whatever the job passed to Maven: relative on CI, @@ -89,7 +89,11 @@ const javaFiles = (dir, prefix = []) => : [[...prefix, entry.name].join('.')], ); let failed = false; -for (const module of modules) { +for (const requestedModule of modules) { + const module = + path.basename(path.resolve(requestedModule)) === 'managed-agent-server' + ? path.join(requestedModule, 'verification') + : requestedModule; const expected = javaFiles(path.join(module, 'src', 'test', 'java')) .filter((file) => file.endsWith('.java')) .map((file) => file.slice(0, -'.java'.length)) @@ -108,6 +112,10 @@ for (const module of modules) { ); } } + if (expected.length === 0) { + failed = true; + console.error(`::error::${module}: no ${family} test sources found`); + } if (optIn && expected.length !== 1) { failed = true; console.error( diff --git a/scripts/check-flyway-open-prs.js b/scripts/check-flyway-open-prs.js index cfdb1de4b88..4c6185d2ef1 100644 --- a/scripts/check-flyway-open-prs.js +++ b/scripts/check-flyway-open-prs.js @@ -183,10 +183,15 @@ function evaluate(modules, mainFiles, prs) { .map((file) => ({ ...file, path: relocated - ? file.path.replace( - /^packages\/sdk-java\/(?:managed-agent-server|runtime-broker)\/src\//, - `${currentModule}/src/`, - ) + ? file.path + .replace( + /^packages\/sdk-java\/(?:managed-agent-server|runtime-broker)\/src\//, + `${currentModule}/src/`, + ) + .replace( + /^packages\/managed-agent-server\/src\/main\/resources\/db\/managed-schema\//, + `${currentModule}/persistence/src/main/resources/db/managed-schema/`, + ) : file.path, })) .filter((file) => isTracked(modules, file.path)); diff --git a/scripts/contract-version-utils.js b/scripts/contract-version-utils.js index 0be1509cae2..e6a09210adb 100644 --- a/scripts/contract-version-utils.js +++ b/scripts/contract-version-utils.js @@ -13,6 +13,9 @@ // 2026-10-09, and the stall had already reached main once (#13163). export const CONTRACT_DOCUMENT = + 'packages/managed-agent-server/contracts/src/main/resources/openapi/managed-agent-public-api.openapi.json'; + +export const CONTRACT_DOCUMENT_MONOLITH = 'packages/managed-agent-server/src/main/resources/openapi/managed-agent-public-api.openapi.json'; // Open PRs based before the application move still expose the contract here. diff --git a/scripts/create-managed-runtime-package.js b/scripts/create-managed-runtime-package.js index 07c84ecf580..fe3a6aaca88 100644 --- a/scripts/create-managed-runtime-package.js +++ b/scripts/create-managed-runtime-package.js @@ -30,7 +30,7 @@ if (values.help) { 'Packages the server JAR and manifest, optionally with Java 21. Reuses an installed matching Qwen CLI.\n' + '--without-jre builds a lite archive requiring local Java 21.\n' + 'Targets: darwin-arm64, darwin-x64, linux-arm64, linux-x64.\n' + - 'Default output: packages/managed-agent-server/target/managed-runtime.', + 'Default output: packages/managed-agent-server/server/target/managed-runtime.', ); } else { for (const option of ['target', 'server-jar']) { @@ -50,6 +50,7 @@ if (values.help) { root, 'packages', 'managed-agent-server', + 'server', 'target', 'managed-runtime', ), diff --git a/scripts/flyway-migration-utils.js b/scripts/flyway-migration-utils.js index 36af90361a8..87d28952234 100644 --- a/scripts/flyway-migration-utils.js +++ b/scripts/flyway-migration-utils.js @@ -20,6 +20,18 @@ import path from 'node:path'; // passing on what is left. Its probes derive the db roots and source roots // from this table, so editing it cannot silently blind them. export const LOCATIONS = [ + { + dir: ['persistence', 'src', 'main', 'resources', 'db', 'migration'], + suffix: '.sql', + }, + { + dir: ['persistence', 'src', 'main', 'java', 'db', 'migration'], + suffix: '.java', + }, + { + dir: ['persistence', 'src', 'main', 'resources', 'db', 'managed-schema'], + suffix: '.sql', + }, { dir: ['src', 'main', 'resources', 'db', 'migration'], suffix: '.sql' }, { dir: ['src', 'main', 'java', 'db', 'migration'], suffix: '.java' }, { dir: ['src', 'main', 'resources', 'db', 'managed-schema'], suffix: '.sql' }, diff --git a/scripts/managed-agent-dev.js b/scripts/managed-agent-dev.js index 0e6d49ffd4b..09aa1f549f7 100644 --- a/scripts/managed-agent-dev.js +++ b/scripts/managed-agent-dev.js @@ -366,7 +366,7 @@ export function buildSpringRecipeLines({ " $env:SPRING_DATASOURCE_URL='jdbc:mysql://127.0.0.1:3306/qwen_managed_agent'", " $env:SPRING_DATASOURCE_USERNAME='qwen'", " $env:SPRING_DATASOURCE_PASSWORD='replace-me'", - ' Set-Location packages\\managed-agent-server; mvn spring-boot:run', + ' mvn -f packages/managed-agent-server/pom.xml -pl server -am -DskipTests install; mvn -f packages/managed-agent-server/server/pom.xml spring-boot:run', ); } else { lines.push( @@ -374,7 +374,7 @@ export function buildSpringRecipeLines({ " export SPRING_DATASOURCE_URL='jdbc:mysql://127.0.0.1:3306/qwen_managed_agent'", " export SPRING_DATASOURCE_USERNAME='qwen'", " export SPRING_DATASOURCE_PASSWORD='replace-me'", - ' cd packages/managed-agent-server && mvn spring-boot:run', + ' mvn -f packages/managed-agent-server/pom.xml -pl server -am -DskipTests install && mvn -f packages/managed-agent-server/server/pom.xml spring-boot:run', ); } return lines; diff --git a/scripts/package-managed-agent-reference.mjs b/scripts/package-managed-agent-reference.mjs index 8a0f1fe915b..022e386b9ea 100644 --- a/scripts/package-managed-agent-reference.mjs +++ b/scripts/package-managed-agent-reference.mjs @@ -37,11 +37,10 @@ const VERSION_PATTERN = new RegExp( `^(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)-${PRERELEASE_IDENTIFIER}(?:\\.${PRERELEASE_IDENTIFIER})*$`, ); const REVISION_PATTERN = /^[0-9a-f]{40}$/; -const MAIN_SOURCE_ROOT = 'packages/managed-agent-server/src/main/java'; -const PERSISTENCE = 'com.alibaba.qwen.code.managedagent.persistence'; +const MAIN_SOURCE_ROOT = 'packages/managed-agent-server/server/src/main/java'; +const PERSISTENCE = 'com.alibaba.qwen.code.managedagent.maintenance'; const PREFLIGHT = 'com.alibaba.qwen.code.managedagent.preflight'; -const WORKSPACE_RUNTIME = - 'com.alibaba.qwen.code.managedagent.workspace.runtime'; +const WORKSPACE_RUNTIME = 'com.alibaba.qwen.code.managedagent.maintenance'; // The offline maintenance entry points, each a main class in the server jar. // bin/qwen-managed-agent-admin runs them through Spring Boot's @@ -375,7 +374,7 @@ export async function createReferenceDistribution({ const serverPom = path.join(root, 'packages/managed-agent-server/pom.xml'); const serverVersion = await pomVersion( serverPom, - 'qwen-managed-agent-server', + 'qwen-managed-agent-parent', ); const packageJson = JSON.parse( await readFile(path.join(root, 'package.json')), @@ -397,7 +396,7 @@ export async function createReferenceDistribution({ const applicationConfiguration = await readFile( path.join( root, - 'packages/managed-agent-server/src/main/resources/application.yml', + 'packages/managed-agent-server/server/src/main/resources/application.yml', ), 'utf8', ); @@ -432,14 +431,14 @@ export async function createReferenceDistribution({ { name: 'qwen-managed-agent-server', version: serverVersion, - source: `packages/managed-agent-server/target/qwen-managed-agent-server-${serverVersion}-exec.jar`, + source: `packages/managed-agent-server/server/target/qwen-managed-agent-server-${serverVersion}-exec.jar`, destination: `lib/${serverJar}`, role: 'control-plane', }, { name: 'qwen-managed-agent-operator-recovery', version: serverVersion, - source: `packages/managed-agent-server/target/${recoveryJar}`, + source: `packages/managed-agent-server/server/target/${recoveryJar}`, destination: `lib/${recoveryJar}`, role: 'operator-recovery', }, diff --git a/scripts/run-managed-agent-server-e2e.ts b/scripts/run-managed-agent-server-e2e.ts index b187af69853..120948c3dce 100644 --- a/scripts/run-managed-agent-server-e2e.ts +++ b/scripts/run-managed-agent-server-e2e.ts @@ -246,7 +246,7 @@ if (!Number.isSafeInteger(runtimeDelayMs) || runtimeDelayMs < 0) { const cliBundle = path.join(root, 'dist', 'cli.js'); const springModule = path.join(root, 'packages', 'managed-agent-server'); -const springTarget = path.join(springModule, 'target'); +const springTarget = path.join(springModule, 'server', 'target'); // The pom attaches classified repackage executions alongside the // unclassified server jar, so a wildcard over target/ matches several // artifacts. Read the project version from the @@ -257,7 +257,7 @@ const springTarget = path.join(springModule, 'target'); // re-indenting the element cannot flow newlines into the jar path. const pomXml = readFileSync(path.join(springModule, 'pom.xml'), 'utf8'); const pomVersion = pomXml.match( - /qwen-managed-agent-server<\/artifactId>\s*\s*([^<]*?\S)\s*<\/version>/, + /qwen-managed-agent-parent<\/artifactId>\s*\s*([^<]*?\S)\s*<\/version>/, )?.[1]; if (!pomVersion) { throw new Error( @@ -1741,7 +1741,7 @@ try { [ '-cp', springJar, - '-Dloader.main=com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageRegistrationMain', + '-Dloader.main=com.alibaba.qwen.code.managedagent.maintenance.WorkspaceStorageRegistrationMain', 'org.springframework.boot.loader.launch.PropertiesLauncher', 'register', tenant, diff --git a/scripts/tests/check-failsafe-reports.test.js b/scripts/tests/check-failsafe-reports.test.js index 696819ae1da..aba5d2fcf87 100644 --- a/scripts/tests/check-failsafe-reports.test.js +++ b/scripts/tests/check-failsafe-reports.test.js @@ -152,7 +152,7 @@ describe('check-failsafe-reports', () => { // The real module name, because the exemption is keyed on it: the pom binds // ManagedAgentPackagingIT to a surefire execution and excludes it from the // mysql-integration failsafe, so it can never satisfy the non-hosted roster. - const name = 'packages/managed-agent-server'; + const name = 'packages/managed-agent-server/verification'; const sources = [ 'com.example.ManagedAgentPackagingIT', 'com.example.OrdersIT', @@ -293,7 +293,10 @@ describe('check-failsafe-reports', () => { expect(result.status).toBe(1); const noFamily = join(root, 'no-family'); mkdirSync(join(noFamily, 'src', 'test', 'java'), { recursive: true }); - expect(check('hosted', noFamily).status).toBe(0); + expect(check('hosted', noFamily).status).toBe(1); + expect(check('hosted', noFamily).output).toContain( + 'no hosted test sources found', + ); }); it('hosts admission gates as their own family and rejects drift in both directions', () => { diff --git a/scripts/tests/check-flyway-open-prs.test.js b/scripts/tests/check-flyway-open-prs.test.js index 8585976e24c..58815235cc8 100644 --- a/scripts/tests/check-flyway-open-prs.test.js +++ b/scripts/tests/check-flyway-open-prs.test.js @@ -283,7 +283,7 @@ describe('check-flyway-open-prs', () => { it('ignores paths outside the scanned locations, and names that claim no version', async () => { const { posts } = await check([ pr(10001, [ - 'packages/managed-agent-server/src/test/resources/db/migration/V53__fixture.sql', + 'packages/managed-agent-server/verification/src/test/resources/db/migration/V53__fixture.sql', sql('V53__client.sql', 'packages/sdk-java/client'), sql('R__repeatable_view.sql'), sql('V53__old.sql').replace('/migration/', '/migration_old/'), diff --git a/scripts/tests/event-transport-broker-ci.test.js b/scripts/tests/event-transport-broker-ci.test.js index 3e9caa8afd9..529f5fa694b 100644 --- a/scripts/tests/event-transport-broker-ci.test.js +++ b/scripts/tests/event-transport-broker-ci.test.js @@ -32,17 +32,20 @@ const step = (name) => { return found; }; const script = readFileSync('scripts/rocketmq-lite-broker.sh', 'utf8'); -const pom = readFileSync('packages/managed-agent-server/pom.xml', 'utf8'); +const pom = readFileSync( + 'packages/managed-agent-server/verification/pom.xml', + 'utf8', +); const profile = pom .split('event-transport-broker-gates')[1] .split('')[0]; -const server = 'packages/managed-agent-server/src'; +const server = 'packages/managed-agent-server'; const properties = readFileSync( - `${server}/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentProperties.java`, + `${server}/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentProperties.java`, 'utf8', ); const gate = readFileSync( - `${server}/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGate.java`, + `${server}/verification/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportBrokerGate.java`, 'utf8', ); @@ -72,7 +75,7 @@ describe('EventTransport broker gates CI', () => { expect(profile.match(//g)).toHaveLength(1); expect(profile).toContain('true'); expect(profile).toMatch( - /maven-surefire-plugin<\/artifactId>\s*true<\/skipTests>/, + /maven-surefire-plugin<\/artifactId>\s*\s*true<\/skipTests>/, ); }); @@ -231,7 +234,7 @@ describe('EventTransport broker gates CI', () => { propertyDefault('wakeBootstrapLiteTopic'), /CAPABILITY_PROBE_LITE = "([^"]+)"/.exec( readFileSync( - `${server}/main/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfiguration.java`, + `${server}/server/src/main/java/com/alibaba/qwen/code/managedagent/config/EventTransportConfiguration.java`, 'utf8', ), )[1], diff --git a/scripts/tests/event-transport-multi-node-ci.test.js b/scripts/tests/event-transport-multi-node-ci.test.js index 63d7c0b3423..c55b754fff3 100644 --- a/scripts/tests/event-transport-multi-node-ci.test.js +++ b/scripts/tests/event-transport-multi-node-ci.test.js @@ -21,17 +21,20 @@ const step = (name) => { return found; }; const verify = () => step('Verify the EventTransport multi-node gates'); -const pom = readFileSync('packages/managed-agent-server/pom.xml', 'utf8'); +const pom = readFileSync( + 'packages/managed-agent-server/verification/pom.xml', + 'utf8', +); const profile = pom .split('event-transport-multi-node-gates')[1] .split('')[0]; -const server = 'packages/managed-agent-server/src'; +const server = 'packages/managed-agent-server'; const properties = readFileSync( - `${server}/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentProperties.java`, + `${server}/server/src/main/java/com/alibaba/qwen/code/managedagent/config/ManagedAgentProperties.java`, 'utf8', ); const gate = readFileSync( - `${server}/test/java/com/alibaba/qwen/code/managedagent/EventTransportMultiNodeGate.java`, + `${server}/verification/src/test/java/com/alibaba/qwen/code/managedagent/EventTransportMultiNodeGate.java`, 'utf8', ); const script = readFileSync('scripts/rocketmq-lite-broker.sh', 'utf8'); @@ -73,7 +76,7 @@ describe('EventTransport multi-node gates CI', () => { expect(profile.match(//g)).toHaveLength(1); expect(profile).toContain('true'); expect(profile).toMatch( - /maven-surefire-plugin<\/artifactId>\s*true<\/skipTests>/, + /maven-surefire-plugin<\/artifactId>\s*\s*true<\/skipTests>/, ); }); @@ -123,7 +126,7 @@ describe('EventTransport multi-node gates CI', () => { '-Dqwen.gate.rocketmq.work-dir="${RUNNER_TEMP}/rocketmq"', ); expect(step('Upload multi-node gate reports').with.path).toContain( - 'packages/managed-agent-server/target/mq3-multi-node/', + 'packages/managed-agent-server/verification/target/mq3-multi-node/', ); }); diff --git a/scripts/tests/hosted-process-ci.test.js b/scripts/tests/hosted-process-ci.test.js index ac3fa3f7d3b..0640cb821a6 100644 --- a/scripts/tests/hosted-process-ci.test.js +++ b/scripts/tests/hosted-process-ci.test.js @@ -369,10 +369,9 @@ describe('Hosted real-process gates', () => { expect(job.steps[relay].shell).toBeUndefined(); expect(job.steps[relay]['working-directory']).toBeUndefined(); expect(relay).toBeLessThan(job.steps.indexOf(run)); - const pom = read('packages/managed-agent-server/pom.xml').replace( - //g, - '', - ); + const pom = read( + 'packages/managed-agent-server/verification/pom.xml', + ).replace(//g, ''); // The report check sees a test selection only when Maven passes it as a // user property. Set in the workflow (MAVEN_OPTS included) or in a POM, // it would narrow the run unrecorded. @@ -406,7 +405,7 @@ describe('Hosted real-process gates', () => { expect(pom.match(/qwen\.hosted\.mysql\.isolation = true/g)).toHaveLength(1); expect( read( - 'packages/managed-agent-server/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension', + 'packages/managed-agent-server/verification/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension', ).trim(), ).toBe('com.alibaba.qwen.code.managedagent.HostedMySqlIsolationExtension'); // The non-Hosted MySQL job must not narrow its selection either, or an IT outside @@ -424,7 +423,7 @@ describe('Hosted real-process gates', () => { const profile = (id) => pom.split(`${id}`)[1].split('')[0]; const skipsUnits = - /maven-surefire-plugin<\/artifactId>\s*true<\/skipTests><\/configuration>/; + /maven-surefire-plugin<\/artifactId>\s*\s*true<\/skipTests>\s*<\/configuration>/; for (const id of [ 'hosted-harness-mysql', 'hosted-harness-gates', @@ -460,7 +459,7 @@ describe('Hosted real-process gates', () => { expect(pom.match(//g)).toHaveLength(1); expect( read( - 'packages/managed-agent-server/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener', + 'packages/managed-agent-server/verification/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener', ).trim(), ).toBe('com.alibaba.qwen.code.managedagent.MySqlForkRoutingListener'); expect(mysqlRun.run).not.toMatch(/-DforkCount\b/); @@ -497,7 +496,7 @@ describe('Hosted real-process gates', () => { // test would move to non-Hosted MySQL, a renamed non-Hosted test to the Hosted job. const its = readdirSync( new URL( - '../../packages/managed-agent-server/src/test/java/', + '../../packages/managed-agent-server/verification/src/test/java/', import.meta.url, ), { recursive: true }, @@ -532,33 +531,33 @@ describe('Hosted real-process gates', () => { // module-relative directory precisely so an artifact can carry it; the // writer and the upload path must not drift. expect(upload.with.path).toContain( - 'packages/managed-agent-server/target/fg7-cases/', + 'packages/managed-agent-server/verification/target/fg7-cases/', ); // FG8 follows the same precedent: its per-case evidence lives in // target/fg8-cases/. expect(upload.with.path).toContain( - 'packages/managed-agent-server/target/fg8-cases/', + 'packages/managed-agent-server/verification/target/fg8-cases/', ); // FG9's session-relay fault gates keep theirs in target/fg9-cases/. expect(upload.with.path).toContain( - 'packages/managed-agent-server/target/fg9-cases/', + 'packages/managed-agent-server/verification/target/fg9-cases/', ); // FG10 follows the same precedent: its per-case evidence lives in // target/fg10-cases/. expect(upload.with.path).toContain( - 'packages/managed-agent-server/target/fg10-cases/', + 'packages/managed-agent-server/verification/target/fg10-cases/', ); // FG11a's automation lost-reply evidence lives in target/fg11-cases/. expect(upload.with.path).toContain( - 'packages/managed-agent-server/target/fg11-cases/', + 'packages/managed-agent-server/verification/target/fg11-cases/', ); // FG12a's MCP reply-loss evidence lives in target/fg12-cases/. expect(upload.with.path).toContain( - 'packages/managed-agent-server/target/fg12-cases/', + 'packages/managed-agent-server/verification/target/fg12-cases/', ); // FG13e's Hook stream-gap evidence lives in target/fg13-cases/. expect(upload.with.path).toContain( - 'packages/managed-agent-server/target/fg13-cases/', + 'packages/managed-agent-server/verification/target/fg13-cases/', ); // The writer side of the same path: the Java literal must stay under // the directory the step uploads, or the artifact is silently empty. @@ -773,7 +772,7 @@ describe('Hosted real-process gates', () => { // re-pointed -P flag must not leave the row reading the old profile. expect(run.run).toContain(`-P${profile}`); const forkSeconds = Number( - read('packages/managed-agent-server/pom.xml') + read('packages/managed-agent-server/verification/pom.xml') .split(`${profile}`)[1] .match(/(\d+) { const hostedForkHealthySeconds = 10844; const hostedForkSeconds = () => Number( - read('packages/managed-agent-server/pom.xml') + read('packages/managed-agent-server/verification/pom.xml') .split('hosted-harness-mysql')[1] .match(/(\d+) - 'packages/managed-agent-server/src/test/java/' + + 'packages/managed-agent-server/verification/src/test/java/' + `com/alibaba/qwen/code/managedagent/${className}.java`; // Every class the fork's **/Hosted*IT.java include picks up, in any // package. const hostedTestClasses = () => { - const root = 'packages/managed-agent-server/src/test/java/'; + const root = 'packages/managed-agent-server/verification/src/test/java/'; return readdirSync(new URL(`../../${root}`, import.meta.url), { recursive: true, }) diff --git a/scripts/tests/managed-agent-dev.test.js b/scripts/tests/managed-agent-dev.test.js index 8913cf396be..8a2f46b4c30 100644 --- a/scripts/tests/managed-agent-dev.test.js +++ b/scripts/tests/managed-agent-dev.test.js @@ -1488,7 +1488,7 @@ describe('buildSpringRecipeLines', () => { expect(joined).toContain(`source "${envPath}"`); expect(joined).toContain('export SPRING_DATASOURCE_URL='); expect(joined).toContain( - 'cd packages/managed-agent-server && mvn spring-boot:run', + 'mvn -f packages/managed-agent-server/pom.xml -pl server -am -DskipTests install && mvn -f packages/managed-agent-server/server/pom.xml spring-boot:run', ); expect(joined).toContain('packages/sdk-java/qwencode/pom.xml'); expect(joined).not.toContain('runtime-broker/pom.xml'); @@ -1505,7 +1505,7 @@ describe('buildSpringRecipeLines', () => { .map((line) => line.trim()) .filter((line) => line !== '' && !line.startsWith('#')); expect(actionable).toContain( - 'Set-Location packages\\managed-agent-server; mvn spring-boot:run', + 'mvn -f packages/managed-agent-server/pom.xml -pl server -am -DskipTests install; mvn -f packages/managed-agent-server/server/pom.xml spring-boot:run', ); const dotPointers = actionable.filter((line) => line.includes(ps1Path)); expect(dotPointers).toHaveLength(1); diff --git a/scripts/tests/managed-agent-server-e2e.test.js b/scripts/tests/managed-agent-server-e2e.test.js index e4578ec565b..2e9e8caf0ce 100644 --- a/scripts/tests/managed-agent-server-e2e.test.js +++ b/scripts/tests/managed-agent-server-e2e.test.js @@ -55,7 +55,7 @@ describe('managed-agent-server e2e runner', () => { it('queries tables from the shared fresh-database baseline', () => { const source = read('scripts/run-managed-agent-server-e2e.ts'); const baseline = read( - 'packages/managed-agent-server/src/main/resources/db/managed-schema/V1__managed_schema.sql', + 'packages/managed-agent-server/persistence/src/main/resources/db/managed-schema/V1__managed_schema.sql', ); const tables = new Set( [...baseline.matchAll(/CREATE TABLE (\w+)/g)].map((match) => match[1]), @@ -634,7 +634,7 @@ describe('managed-agent-server e2e runner', () => { expect(source).toContain(`'${refusal}'`); expect( read( - 'packages/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStore.java', + 'packages/managed-agent-server/persistence/src/main/java/com/alibaba/qwen/code/managedagent/persistence/ManagedSessionStore.java', ), ).toContain(`"${refusal}."`); const writerBefore = source.indexOf( @@ -873,7 +873,9 @@ describe('managed-agent-server e2e runner', () => { // Harness and find its workspace ID there: application.yml derives the // switch from the Harness, and Prerequisites exports the ID. expect( - read('packages/managed-agent-server/src/main/resources/application.yml'), + read( + 'packages/managed-agent-server/server/src/main/resources/application.yml', + ), 'the Session Store switch must follow the Harness by default', ).toContain( "enabled: '${QWEN_MANAGED_AGENT_SESSION_STORE_ENABLED:${qwen.managed-agent.harness.enabled:false}}'", @@ -916,7 +918,7 @@ describe('managed-agent-server e2e runner', () => { // document the current authority, so the false claim reaches // integrators in either language. const toolTurnIt = - 'packages/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceToolTurnIT.java'; + 'packages/managed-agent-server/verification/src/test/java/com/alibaba/qwen/code/managedagent/HostedWorkspaceToolTurnIT.java'; expect( existsSync(new URL(`../../${toolTurnIt}`, import.meta.url)) && read('.github/workflows/sdk-java.yml').includes( @@ -1451,7 +1453,7 @@ describe('managed-agent-server e2e runner', () => { const dockerfile = read('packages/managed-agent-server/Dockerfile'); expect(dockerfile).toContain('qwen-managed-agent-server-*-exec.jar'); expect(dockerfile).toContain('/tmp/qwen-managed-agent-server.jar'); - expect(read('packages/managed-agent-server/pom.xml')).toContain( + expect(read('packages/managed-agent-server/server/pom.xml')).toContain( 'exec', ); }); @@ -1477,7 +1479,7 @@ describe('managed-agent-server e2e runner', () => { const pattern = new Function(`return ${patternSource};`)(); const match = read('packages/managed-agent-server/pom.xml').match(pattern); expect(match?.[0]).toContain( - 'qwen-managed-agent-server', + 'qwen-managed-agent-parent', ); expect(match?.[1]).toBeTruthy(); expect(match?.[1]).not.toContain('${'); @@ -1489,10 +1491,10 @@ describe('managed-agent-server e2e runner', () => { // whitespace-tolerant, and an empty element still matches nothing, so // the runner's descriptive guard fires. const wrapped = - 'qwen-managed-agent-server\n \n 9.9.9\n '; + 'qwen-managed-agent-parent\n \n 9.9.9\n '; expect(wrapped.match(pattern)?.[1]).toBe('9.9.9'); expect( - 'qwen-managed-agent-server'.match( + 'qwen-managed-agent-parent'.match( pattern, ), ).toBeNull(); @@ -1521,7 +1523,7 @@ describe('managed-agent-server e2e runner', () => { // --server.address on the command line and the e2e runner passes none, // so a flipped yml default would otherwise ship silently. const applicationYml = read( - 'packages/managed-agent-server/src/main/resources/application.yml', + 'packages/managed-agent-server/server/src/main/resources/application.yml', ); expect(applicationYml).toMatch( /address: '\$\{QWEN_MANAGED_AGENT_SERVER_ADDRESS:127\.0\.0\.1\}'/, @@ -1654,7 +1656,7 @@ describe('managed-agent-server e2e runner', () => { () => { const dockerfile = read('packages/managed-agent-server/Dockerfile'); const stage = dockerfile.match( - /cd packages\/managed-agent-server\/target \\[\s\S]*?&& cp "\$main" \/tmp\/qwen-managed-agent-server\.jar/, + /cd packages\/managed-agent-server\/server\/target \\[\s\S]*?&& cp "\$main" \/tmp\/qwen-managed-agent-server\.jar/, ); expect( stage, @@ -1703,7 +1705,7 @@ describe('managed-agent-server e2e runner', () => { for (const jar of jars) writeFileSync(join(dir, jar), ''); const fixture = script .replace( - 'cd packages/managed-agent-server/target', + 'cd packages/managed-agent-server/server/target', `cd '${dir.replaceAll('\\', '/')}'`, ) .replace( diff --git a/scripts/tests/managed-runtime-package.test.js b/scripts/tests/managed-runtime-package.test.js index 5930ba2d3f0..6b2239f5135 100644 --- a/scripts/tests/managed-runtime-package.test.js +++ b/scripts/tests/managed-runtime-package.test.js @@ -220,6 +220,7 @@ describe.skipIf(process.platform === 'win32')( checkout, 'packages', 'managed-agent-server', + 'server', 'target', 'managed-runtime', 'qwen-code-managed-runtime-darwin-arm64.tar.gz', diff --git a/scripts/tests/managed-runtime-release.test.js b/scripts/tests/managed-runtime-release.test.js index d5143a32181..bafbfb9c9e6 100644 --- a/scripts/tests/managed-runtime-release.test.js +++ b/scripts/tests/managed-runtime-release.test.js @@ -558,8 +558,12 @@ describe('Managed runtime publication gates', () => { 'maven-gpg-plugin', ]) expect(profile).toContain(plugin); + const serverPom = fs.readFileSync( + path.join(root, 'packages/managed-agent-server/server/pom.xml'), + 'utf8', + ); expect( - pom.match(/\$\{managed.operator-tools.attach\}<\/attach>/g), + serverPom.match(/\$\{managed.operator-tools.attach\}<\/attach>/g), ).toHaveLength(6); }); }); @@ -606,7 +610,13 @@ describe.skipIf(process.platform === 'win32')('release gate execution', () => { const repairPatch = 'scripts/patches/managed-runtime-v0.26.3-tests.patch'; function repairFixture() { - const fixed = fs.readFileSync(path.join(root, repairedTest), 'utf8'); + const fixed = fs.readFileSync( + path.join( + root, + repairedTest.replace('/src/test/', '/verification/src/test/'), + ), + 'utf8', + ); const target = path.join(temporary, repairedTest); const patch = path.join( temporary, diff --git a/scripts/tests/package-managed-agent-reference.test.mjs b/scripts/tests/package-managed-agent-reference.test.mjs index e2c3f5e3334..d691f492c3f 100644 --- a/scripts/tests/package-managed-agent-reference.test.mjs +++ b/scripts/tests/package-managed-agent-reference.test.mjs @@ -39,7 +39,7 @@ import { } from '../package-managed-agent-reference.mjs'; function mainSource(main) { - return `packages/managed-agent-server/src/main/java/${main.replaceAll('.', '/')}.java`; + return `packages/managed-agent-server/server/src/main/java/${main.replaceAll('.', '/')}.java`; } async function fixture(root) { @@ -50,15 +50,15 @@ async function fixture(root) { }), LICENSE: 'license\n', 'packages/managed-agent-server/pom.xml': ` - qwen-managed-agent-server + qwen-managed-agent-parent 0.4.0-alpha 21 `, - 'packages/managed-agent-server/target/qwen-managed-agent-server-0.4.0-alpha-exec.jar': + 'packages/managed-agent-server/server/target/qwen-managed-agent-server-0.4.0-alpha-exec.jar': 'server', - 'packages/managed-agent-server/target/qwen-managed-agent-server-0.4.0-alpha-operator-recovery.jar': + 'packages/managed-agent-server/server/target/qwen-managed-agent-server-0.4.0-alpha-operator-recovery.jar': 'recovery', - 'packages/managed-agent-server/src/main/resources/application.yml': + 'packages/managed-agent-server/server/src/main/resources/application.yml': "workspace-shell-enabled: '${QWEN_MANAGED_AGENT_WORKSPACE_SHELL_ENABLED:false}'\n", 'packages/managed-agent-server/distribution/config/application.example.yml': 'template\n', @@ -81,9 +81,9 @@ async function fixture(root) { '#!/usr/bin/env sh\nupgrade\n', 'packages/managed-agent-server/distribution/bin/qwen-managed-agent-upgrade.mjs': 'export {};\n', - 'packages/managed-agent-server/src/main/resources/openapi/managed-agent-public-api.openapi.json': + 'packages/managed-agent-server/contracts/src/main/resources/openapi/managed-agent-public-api.openapi.json': JSON.stringify({ info: { version: '1.46.0' } }), - 'packages/managed-agent-server/src/main/resources/db/managed-schema/V1__baseline.sql': + 'packages/managed-agent-server/persistence/src/main/resources/db/managed-schema/V1__baseline.sql': 'select 1;', 'packages/managed-agent-server/src/main/java/db/migration/V9__latest.java': 'class Latest {}', @@ -302,7 +302,7 @@ test('launchers load the distribution config only when it exists', async () => { assert.deepEqual( registration.split('\n').filter((line) => line.startsWith('ARG=')), [ - 'ARG=-Dloader.main=com.alibaba.qwen.code.managedagent.persistence.WorkspaceStorageRegistrationMain', + 'ARG=-Dloader.main=com.alibaba.qwen.code.managedagent.maintenance.WorkspaceStorageRegistrationMain', 'ARG=-cp', `ARG=${bundle}/lib/qwen-managed-agent-server-0.4.0-alpha.jar`, 'ARG=org.springframework.boot.loader.launch.PropertiesLauncher', @@ -389,7 +389,7 @@ test('maps every admin command to an entry point in the server', async () => { // application is the one main the admin launcher does not run. const sources = path.join( root, - 'packages/managed-agent-server/src/main/java', + 'packages/managed-agent-server/server/src/main/java', ); const mains = []; for (const file of await readdir(sources, { recursive: true })) { @@ -994,7 +994,7 @@ test('rejects stale or ambiguous safety defaults', async (context) => { const cases = [ { name: 'commented public Shell default', - file: 'packages/managed-agent-server/src/main/resources/application.yml', + file: 'packages/managed-agent-server/server/src/main/resources/application.yml', contents: "# workspace-shell-enabled: '${QWEN_MANAGED_AGENT_WORKSPACE_SHELL_ENABLED:false}'\nworkspace-shell-enabled: '${QWEN_MANAGED_AGENT_WORKSPACE_SHELL_ENABLED:true}'\n", error: /Public Shell must default to false/, @@ -1009,7 +1009,7 @@ test('rejects stale or ambiguous safety defaults', async (context) => { }, { name: 'duplicate public Shell default', - file: 'packages/managed-agent-server/src/main/resources/application.yml', + file: 'packages/managed-agent-server/server/src/main/resources/application.yml', contents: "workspace-shell-enabled: '${QWEN_MANAGED_AGENT_WORKSPACE_SHELL_ENABLED:false}'\nworkspace-shell-enabled: '${QWEN_MANAGED_AGENT_WORKSPACE_SHELL_ENABLED:false}'\n", error: /Cannot uniquely read public Shell default/, diff --git a/scripts/tests/qwen-triage-workflow.test.js b/scripts/tests/qwen-triage-workflow.test.js index 08f2f494b47..140e3148e0e 100644 --- a/scripts/tests/qwen-triage-workflow.test.js +++ b/scripts/tests/qwen-triage-workflow.test.js @@ -7877,18 +7877,28 @@ describe('qwen-triage verify Java toolchain', () => { function moduleCoordinates() { return [ 'packages/managed-agent-server/pom.xml', + ...readdirSync('packages/managed-agent-server', { withFileTypes: true }) + .filter((entry) => entry.isDirectory()) + .map((entry) => + join('packages/managed-agent-server', entry.name, 'pom.xml'), + ), ...readdirSync('packages/sdk-java', { withFileTypes: true }) .filter((entry) => entry.isDirectory()) .map((entry) => join('packages/sdk-java', entry.name, 'pom.xml')), ] .filter((pom) => existsSync(pom)) .map((pom) => { - const xml = readFileSync(pom, 'utf8').replace( - /[\s\S]*?<\/parent>/g, - '', - ); + const source = readFileSync(pom, 'utf8'); + const parent = source.match(/([\s\S]*?)<\/parent>/)?.[1] ?? ''; + const xml = source.replace(/[\s\S]*?<\/parent>/g, ''); return { - groupId: xml.match(/([^<]+)<\/groupId>/)?.[1], + groupId: + xml + .split( + /<(?:dependencies|dependencyManagement|build|properties)>/, + )[0] + .match(/([^<]+)<\/groupId>/)?.[1] ?? + parent.match(/([^<]+)<\/groupId>/)?.[1], artifactId: xml.match(/([^<]+)<\/artifactId>/)?.[1], }; }); diff --git a/scripts/tests/sdk-java-workflow.test.js b/scripts/tests/sdk-java-workflow.test.js index e88cb6673ad..c7fd9dcd1d0 100644 --- a/scripts/tests/sdk-java-workflow.test.js +++ b/scripts/tests/sdk-java-workflow.test.js @@ -331,7 +331,17 @@ describe('SDK Java self-hosted workflow guards', () => { pruneMarkers.push([jobName, markersOf(prune)]); } } - const expected = ['qwen-managed-agent-server', 'qwencode-sdk']; + const expected = [ + 'qwen-managed-agent-adapters', + 'qwen-managed-agent-contracts', + 'qwen-managed-agent-control-plane', + 'qwen-managed-agent-parent', + 'qwen-managed-agent-persistence', + 'qwen-managed-agent-runtime-broker', + 'qwen-managed-agent-server', + 'qwen-managed-agent-verification', + 'qwencode-sdk', + ]; expect(drops.length).toBeGreaterThan(0); expect(prunes.length).toBeGreaterThan(0); for (const [jobName, set] of [...drops, ...prunes]) { @@ -458,6 +468,24 @@ describe('SDK Java self-hosted workflow guards', () => { mkdirSync(runnerTemp, { recursive: true }); const hostRepo = join(home, '.m2', 'repository'); const hostJars = [ + ...[ + 'qwen-managed-agent-parent', + 'qwen-managed-agent-contracts', + 'qwen-managed-agent-control-plane', + 'qwen-managed-agent-runtime-broker', + 'qwen-managed-agent-persistence', + 'qwen-managed-agent-adapters', + 'qwen-managed-agent-server', + 'qwen-managed-agent-verification', + ] + .filter((name) => name !== 'qwen-managed-agent-server') + .map((name) => [ + 'com', + 'alibaba', + name, + '0.1.0-alpha', + 'module.jar', + ]), ['com', 'alibaba', 'qwencode-sdk', '0.1.0-alpha', 'sdk.jar'], [ 'com', @@ -497,8 +525,15 @@ describe('SDK Java self-hosted workflow guards', () => { expect(result.error, name).toBeUndefined(); expect(result.status, name + result.stderr).toBe(0); for (const coordinate of [ - 'qwencode-sdk', + 'qwen-managed-agent-parent', + 'qwen-managed-agent-contracts', + 'qwen-managed-agent-control-plane', + 'qwen-managed-agent-runtime-broker', + 'qwen-managed-agent-persistence', + 'qwen-managed-agent-adapters', 'qwen-managed-agent-server', + 'qwen-managed-agent-verification', + 'qwencode-sdk', ]) { expect( existsSync( @@ -1103,8 +1138,8 @@ describe('SDK Java self-hosted workflow guards', () => { paths: [ 'packages/sdk-java/qwencode/target/surefire-reports/*.txt', 'packages/sdk-java/qwencode/target/surefire-reports/*.dumpstream', - 'packages/managed-agent-server/target/surefire-reports/*.txt', - 'packages/managed-agent-server/target/surefire-reports/*.dumpstream', + 'packages/managed-agent-server/verification/target/surefire-reports/*.txt', + 'packages/managed-agent-server/verification/target/surefire-reports/*.dumpstream', ], testStep: 'Run Runtime Broker state tests', }, @@ -1112,10 +1147,10 @@ describe('SDK Java self-hosted workflow guards', () => { name: 'mysql-integration', artifact: 'sdk-java-test-reports-mysql-integration-java-21', paths: [ - 'packages/managed-agent-server/target/surefire-reports/*.txt', - 'packages/managed-agent-server/target/surefire-reports/*.dumpstream', - 'packages/managed-agent-server/target/failsafe-reports/*.txt', - 'packages/managed-agent-server/target/failsafe-reports/*.dumpstream', + 'packages/managed-agent-server/verification/target/surefire-reports/*.txt', + 'packages/managed-agent-server/verification/target/surefire-reports/*.dumpstream', + 'packages/managed-agent-server/verification/target/failsafe-reports/*.txt', + 'packages/managed-agent-server/verification/target/failsafe-reports/*.dumpstream', ], testStep: 'Install and verify Managed Agent dependencies, tests, Checkstyle, and MySQL integration', @@ -1215,7 +1250,7 @@ describe('SDK Java self-hosted workflow guards', () => { ); it('keeps the Broker matrix selector matched to every default test including nested packages', () => { - const cwd = 'packages/managed-agent-server/src/test/java'; + const cwd = 'packages/managed-agent-server/verification/src/test/java'; const prefix = 'com/alibaba/qwen/code/runtimebroker'; const selected = [ ...new Set( @@ -1237,7 +1272,10 @@ describe('SDK Java self-hosted workflow guards', () => { true, ); expect( - readFileSync('packages/managed-agent-server/pom.xml', 'utf8'), + readFileSync( + 'packages/managed-agent-server/verification/pom.xml', + 'utf8', + ), ).toContain('true'); }); @@ -2074,7 +2112,7 @@ describe('SDK Java MySQL lane on the ECS pool', () => { // artifact in the shared cache the next seed hardlinks from. expect(step.run).toContain('cd "${RUNNER_TEMP}/m2-repo"'); expect(step.run).toContain( - "timeout 300 find . \\( -path ./com/alibaba/qwencode-sdk -o -path ./com/alibaba/qwen-managed-agent-server -o -name '*.lastUpdated' -o -name 'resolver-status.properties' -o -name '_remote.repositories' -o -name '*.tmp' \\) -prune -o -type f -links 1 -exec cp -ln --parents -t \"${HOME}/.m2/repository\" {} + 9>&- || write_back=$?", + "timeout 300 find . \\( -path ./com/alibaba/qwencode-sdk -o -path ./com/alibaba/qwen-managed-agent-parent -o -path ./com/alibaba/qwen-managed-agent-contracts -o -path ./com/alibaba/qwen-managed-agent-control-plane -o -path ./com/alibaba/qwen-managed-agent-runtime-broker -o -path ./com/alibaba/qwen-managed-agent-persistence -o -path ./com/alibaba/qwen-managed-agent-adapters -o -path ./com/alibaba/qwen-managed-agent-server -o -path ./com/alibaba/qwen-managed-agent-verification -o -name '*.lastUpdated' -o -name 'resolver-status.properties' -o -name '_remote.repositories' -o -name '*.tmp' \\) -prune -o -type f -links 1 -exec cp -ln --parents -t \"${HOME}/.m2/repository\" {} + 9>&- || write_back=$?", ); expect(step.run).not.toMatch(/tar [^\n]*-xf/); expect(step.run).toContain(