diff --git a/docs/architecture/settings-integration-configuration-model.md b/docs/architecture/settings-integration-configuration-model.md index d1c70ffa..fd0c28cc 100644 --- a/docs/architecture/settings-integration-configuration-model.md +++ b/docs/architecture/settings-integration-configuration-model.md @@ -5,8 +5,7 @@ with the provider catalog aligned to the bridge-only design. ## Current Rule -- Settings only manages bridge connection parameters and upstream sync - definitions. +- Settings only manages bridge connection parameters and account sync metadata. - The provider picker is not derived from local endpoint presets. - `xworkmate-bridge` is the only source of truth for the provider catalog. @@ -16,15 +15,7 @@ with the provider catalog aligned to the bridge-only design. flowchart TD A["Settings UI 仅管理 Bridge 连接参数 - 与自定义 upstream sync 定义"] --> B["SettingsSnapshot.externalAcpEndpoints - 仅作为 sync 输入"] - - B --> C["buildExternalAcpSyncedProvidersInternal()"] - C --> D["syncExternalAcpProvidersInternal()"] - D --> E["xworkmate.providers.sync"] - E --> F["xworkmate-bridge providerCatalog"] - - F --> G["acp.capabilities"] + 与账号同步元数据"] --> G["acp.capabilities"] G --> H["providerCatalog[] singleAgent / multiAgent"] @@ -77,7 +68,7 @@ flowchart TD ## Notes -- `externalAcpEndpoints` still matters, but only as bridge sync input. +- Production cloud mode does not use app-side provider sync. - Provider visibility and picker contents come from `acp.capabilities.providerCatalog`. - Auto-provider resolution and unavailable messaging come from diff --git a/docs/architecture/task-control-plane-unification.md b/docs/architecture/task-control-plane-unification.md index 82540b26..7b42688f 100644 --- a/docs/architecture/task-control-plane-unification.md +++ b/docs/architecture/task-control-plane-unification.md @@ -53,15 +53,7 @@ Single-agent provider catalog and availability are owned by flowchart TD A["Settings UI 仅管理 Bridge 连接参数 - 与自定义 upstream sync 定义"] --> B["SettingsSnapshot.externalAcpEndpoints - 仅作为 sync 输入"] - - B --> C["buildExternalAcpSyncedProvidersInternal()"] - C --> D["syncExternalAcpProvidersInternal()"] - D --> E["xworkmate.providers.sync"] - E --> F["xworkmate-bridge providerCatalog"] - - F --> G["acp.capabilities"] + 与账号同步元数据"] --> G["acp.capabilities"] G --> H["providerCatalog[] singleAgent / multiAgent"] @@ -118,6 +110,8 @@ flowchart TD - Desktop App 直接桥接 Go 代码 - Desktop 正常执行链路不以“先启动一个本地 HTTP server,再由 Desktop 自己回连”作为目标架构 - Desktop 的 `sendMessage -> GoTaskService.executeTask -> ACP` 应理解为进程内或直接桥接语义 +- Production cloud mode does not call `xworkmate.providers.sync` +- Production provider upstreams are bridge-owned, not app-owned - 对 app 来说,bridge 是 discovery / config / connect / dialogue 的统一枢纽 ### Web / Mobile diff --git a/docs/plans/2026-04-11-settings-config-state-workflow-design.md b/docs/plans/2026-04-11-settings-config-state-workflow-design.md index 0159ebf6..642693b7 100644 --- a/docs/plans/2026-04-11-settings-config-state-workflow-design.md +++ b/docs/plans/2026-04-11-settings-config-state-workflow-design.md @@ -6,96 +6,108 @@ Date: 2026-04-11 Scope: - `xworkmate-app` -- Settings / account sync / local UI state / task thread persistence +- settings / account sync / cloud runtime state ## V1 Decision -This worktree implements the first app-side simplification: +Production cloud mode is bridge-only: -- keep a single persisted config file: `config/settings.yaml` -- move local recoverable UI state to `ui/state.json` -- keep task title/archive in `tasks/*.json` -- make account sync one-way overwrite for sync-owned fields -- keep bridge provider catalog / runtime capabilities runtime-only +- app-facing cloud endpoint is fixed to `https://xworkmate-bridge.svc.plus` +- production provider catalog is bridge-owned +- production gateway upstream is bridge-owned +- account sync is metadata-only for session state, status, and managed secret references +- account sync does not own executable ACP or gateway upstream endpoints -## Overview Workflow +## Production Routing Truth + +The app does not define or sync production upstreams. + +Bridge-owned production routing is: + +- `codex` -> `https://acp-server.svc.plus/codex/acp/rpc` +- `opencode` -> `https://acp-server.svc.plus/opencode/acp/rpc` +- `gemini` -> `https://acp-server.svc.plus/gemini/acp/rpc` +- gateway -> `wss://openclaw.svc.plus` + +The app only talks to: + +- `https://xworkmate-bridge.svc.plus` + +## App Responsibilities + +- sign in to `accounts.svc.plus` +- persist account session and sync metadata +- call bridge runtime methods: + - `acp.capabilities` + - `xworkmate.routing.resolve` + - `session.start` + - `session.message` + - `session.cancel` + - `session.close` + - bridge-owned gateway methods +- render bridge/provider/gateway status from bridge runtime results + +## Removed Responsibilities + +- no app-side direct-connect cloud path +- no production `xworkmate.providers.sync` +- no production provider catalog from `providerSyncDefinitions` +- no execution-time use of account-synced `openclawUrl` +- no execution-time use of account-synced `apisixUrl` +- no direct app calls to `acp-server.svc.plus/*` +- no direct app calls to `openclaw.svc.plus` + +## State Rules + +`settings.yaml` + +- stores current user settings and local editing state +- does not own production ACP upstream definitions +- does not get executable provider endpoints from account sync + +`account/sync_state.json` + +- stores synced account metadata only +- may retain `openclawUrl` / `apisixUrl` as account profile metadata +- does not overwrite executable cloud routing targets + +`acpBridgeServerModeConfig.cloudSynced.remoteServerSummary.endpoint` + +- represents bridge cloud entry only +- fixed to `https://xworkmate-bridge.svc.plus` while signed in and synced +- is not an upstream provider URL +- is not a gateway upstream URL + +## Workflow ```mermaid flowchart TD UI["Settings UI / App Startup"] --> INIT["SettingsController.initialize()"] - - subgraph LocalStores["APP Local Stores"] - YAML["config/settings.yaml"] - UISTATE["ui/state.json"] - SYNCJSON["account/sync_state.json"] - SECRET["secrets/*.secret\naccount session token / managed secrets"] - TASKS["tasks/*.json\nthread title / archived / thread-owned state"] - end - - INIT --> LOAD["SecureConfigStore.loadSettingsSnapshot()"] - LOAD --> YAML - - INIT --> LOADUI["SecureConfigStore.loadAppUiState()"] - LOADUI --> UISTATE - - INIT --> LOADTHREADS["loadTaskThreads()"] - LOADTHREADS --> TASKS - + INIT --> LOAD["load settings + UI state + task state"] INIT --> RESTORE["restoreAccountSession()"] - RESTORE --> TOKEN["loadAccountSessionToken()"] - TOKEN --> SECRET - - TOKEN --> CHECK{"baseUrl + session token ready?"} - CHECK -->|no| BLOCK["blocked\nAccount session is unavailable"] + RESTORE --> CHECK{"account session ready?"} + CHECK -->|no| BLOCK["blocked"] CHECK -->|yes| SYNC["syncAccountSettingsInternal(baseUrl)"] SYNC --> API["AccountRuntimeClient.loadProfile(token)"] - API --> SAVE_SYNC["saveAccountSyncState(nextState)"] - SAVE_SYNC --> SYNCJSON - - API --> MODECFG["saveSnapshot(\naccountLocalMode=false,\nacpBridgeServerModeConfig.cloudSynced=remote summary\n)"] - MODECFG --> YAML - + API --> SAVE_SYNC["save account sync metadata"] + API --> SAVE_SUMMARY["set cloud summary endpoint = bridge base URL"] API --> APPLY["applyAccountSyncedDefaultsSettingsInternal(state)"] - APPLY --> O1["overwrite remote gateway endpoint"] - APPLY --> O2["overwrite gateway tokenRef"] - APPLY --> O3["overwrite vault address / namespace"] - APPLY --> O4["overwrite aiGateway baseUrl / apiKeyRef"] - APPLY --> O5["overwrite ollamaCloud apiKeyRef"] - APPLY --> O6["update cloudSynced metadata"] + APPLY --> KEEP1["keep vault metadata"] + APPLY --> KEEP2["keep managed secret refs"] + APPLY --> SKIP1["do not overwrite gateway executable endpoint"] + APPLY --> SKIP2["do not overwrite ACP executable endpoint"] - O1 --> SAVE["saveSnapshot(next settings)"] - O2 --> SAVE - O3 --> SAVE - O4 --> SAVE - O5 --> SAVE - O6 --> SAVE - - SAVE --> YAML - SAVE --> DERIVED["reloadDerivedStateInternal()"] - DERIVED --> VIEW["Settings / Runtime ViewModel"] - - VIEW --> NOTE1["does not auto-connect gateway"] - APPLY -. not touched .-> NOTE2["providerSyncDefinitions\n(sync payload definitions)\nnot overwritten here"] - - UI --> LOCAL_EDIT["local settings edit"] - LOCAL_EDIT --> SAVE_LOCAL["saveSnapshot()"] - SAVE_LOCAL --> YAML - - UI --> UI_EDIT["local ui restore edit"] - UI_EDIT --> SAVE_UI["saveAppUiState()"] - SAVE_UI --> UISTATE - - UI --> THREAD_EDIT["rename / archive / restore thread"] - THREAD_EDIT --> SAVE_THREAD["saveTaskThreads()"] - SAVE_THREAD --> TASKS + UI --> BRIDGE_CAPS["acp.capabilities via bridge"] + UI --> BRIDGE_ROUTE["xworkmate.routing.resolve via bridge"] + UI --> BRIDGE_RUN["session.* via bridge"] + UI --> BRIDGE_GATEWAY["xworkmate.gateway.* via bridge"] ``` -## V1 Boundaries +## Invariants -- `settings.yaml` only stores current schema V1 config intent and sync-owned local snapshots. -- `ui/state.json` stores `assistantLastSessionKey`, `assistantNavigationDestinations`, and `savedGatewayTargets`. -- `tasks/*.json` stores thread-owned display facts such as `title` and `archived`. -- `account/sync_state.json` stores sync metadata only, not local override policy. -- bridge-advertised providers and ACP capability state stay runtime-only. +- `providerSyncDefinitions` is not a production truth source. +- account sync may update metadata, but not production execution targets. +- gateway runtime status shown in the app must come from bridge runtime results. +- bridge capability/provider availability shown in the app must come from `acp.capabilities`. diff --git a/lib/app/app_controller_desktop_core.dart b/lib/app/app_controller_desktop_core.dart index 0a09c776..31ec2e02 100644 --- a/lib/app/app_controller_desktop_core.dart +++ b/lib/app/app_controller_desktop_core.dart @@ -300,7 +300,8 @@ class AppController extends ChangeNotifier { late final MultiAgentOrchestrator multiAgentOrchestratorInternal; late final MultiAgentMountManager multiAgentMountManagerInternal; - GoTaskServiceClient get goTaskServiceClientForTest => goTaskServiceClientInternal; + GoTaskServiceClient get goTaskServiceClientForTest => + goTaskServiceClientInternal; Map singleAgentCapabilitiesByProviderInternal = @@ -654,10 +655,7 @@ class AppController extends ChangeNotifier { if (selection == SingleAgentProvider.auto) { return null; } - final resolvedSelection = settings.resolveSingleAgentProvider(selection); - return canUseSingleAgentProviderInternal(resolvedSelection) - ? resolvedSelection - : null; + return canUseSingleAgentProviderInternal(selection) ? selection : null; } List get aiGatewayConversationModelChoices { diff --git a/lib/app/app_controller_desktop_runtime_coordination_impl.dart b/lib/app/app_controller_desktop_runtime_coordination_impl.dart index a0af95e1..c727565f 100644 --- a/lib/app/app_controller_desktop_runtime_coordination_impl.dart +++ b/lib/app/app_controller_desktop_runtime_coordination_impl.dart @@ -57,7 +57,6 @@ Future refreshAcpCapabilitiesRuntimeInternal( controller.sessionsControllerInternal.currentSessionKey, ); if (target == AssistantExecutionTarget.singleAgent) { - await controller.syncExternalAcpProvidersInternal(); await controller.goTaskServiceClientInternal.loadExternalAcpCapabilities( target: AssistantExecutionTarget.singleAgent, forceRefresh: forceRefresh, @@ -94,7 +93,6 @@ Future refreshSingleAgentCapabilitiesRuntimeInternal( AppController controller, { bool forceRefresh = false, }) async { - await controller.syncExternalAcpProvidersInternal(); final capabilities = await controller.goTaskServiceClientInternal .loadExternalAcpCapabilities( target: AssistantExecutionTarget.singleAgent, @@ -221,40 +219,15 @@ String? resolveSingleAgentWorkingDirectoryForSessionRuntimeInternal( } bool singleAgentProviderRequiresLocalPathRuntimeInternal( - AppController controller, + AppController _, SingleAgentProvider provider, ) { - final configuredEndpoint = controller.settings - .providerSyncDefinitionForProvider(provider) - .endpoint - .trim(); - if (configuredEndpoint.isEmpty) { - return true; - } - final normalizedInput = configuredEndpoint.contains('://') - ? configuredEndpoint - : 'ws://$configuredEndpoint'; - final endpoint = Uri.tryParse(normalizedInput); - if (endpoint == null) { - return true; - } - final scheme = endpoint.scheme.trim().toLowerCase(); - if (scheme == 'wss' || scheme == 'https') { + if (provider == SingleAgentProvider.codex || + provider == SingleAgentProvider.opencode || + provider == SingleAgentProvider.gemini) { return false; } - final host = endpoint.host.trim(); - if (host.isEmpty) { - return true; - } - final address = InternetAddress.tryParse(host); - if (address != null) { - return !(address.isLoopback || address.type == InternetAddressType.unix); - } - final normalizedHost = host.toLowerCase(); - if (normalizedHost == 'localhost') { - return true; - } - return false; + return true; } CodeAgentNodeState buildCodeAgentNodeStateRuntimeInternal( diff --git a/lib/app/app_controller_desktop_runtime_helpers.dart b/lib/app/app_controller_desktop_runtime_helpers.dart index 319326f0..c3c0e33e 100644 --- a/lib/app/app_controller_desktop_runtime_helpers.dart +++ b/lib/app/app_controller_desktop_runtime_helpers.dart @@ -661,42 +661,6 @@ extension AppControllerDesktopRuntimeHelpers on AppController { notifyListeners(); } - Future> - buildExternalAcpSyncedProvidersInternal() async { - final providers = []; - for (final profile in settings.providerSyncDefinitions) { - final provider = settings.singleAgentProviderForId(profile.providerKey); - if (provider == SingleAgentProvider.auto) { - continue; - } - final endpoint = profile.endpoint.trim(); - if (!profile.enabled || endpoint.isEmpty) { - continue; - } - final authorizationHeader = profile.authRef.trim().isEmpty - ? '' - : await settingsControllerInternal.resolveSecretValueInternal( - refName: profile.authRef.trim(), - ); - providers.add( - ExternalCodeAgentAcpSyncedProvider( - providerId: provider.providerId, - label: provider.label, - endpoint: endpoint, - authorizationHeader: authorizationHeader, - enabled: true, - ), - ); - } - return providers; - } - - Future syncExternalAcpProvidersInternal() async { - await goTaskServiceClientInternal.syncExternalProviders( - await buildExternalAcpSyncedProvidersInternal(), - ); - } - Future persistGoTaskArtifactsForSessionInternal( String sessionKey, GoTaskServiceResult result, diff --git a/lib/app/app_controller_desktop_single_agent_go_task_flow.dart b/lib/app/app_controller_desktop_single_agent_go_task_flow.dart index a73fefe5..369717cd 100644 --- a/lib/app/app_controller_desktop_single_agent_go_task_flow.dart +++ b/lib/app/app_controller_desktop_single_agent_go_task_flow.dart @@ -152,7 +152,6 @@ Future sendSingleAgentMessageDesktopGoTaskFlowInternal( .map((item) => item.label.trim().isNotEmpty ? item.label : item.key) .where((item) => item.trim().isNotEmpty) .toList(growable: false); - await controller.syncExternalAcpProvidersInternal(); final result = await controller.goTaskServiceClientInternal.executeTask( GoTaskServiceRequest( sessionId: sessionKey, diff --git a/lib/app/app_controller_desktop_thread_actions.dart b/lib/app/app_controller_desktop_thread_actions.dart index ec3e7866..3e885f36 100644 --- a/lib/app/app_controller_desktop_thread_actions.dart +++ b/lib/app/app_controller_desktop_thread_actions.dart @@ -306,7 +306,6 @@ extension AppControllerDesktopThreadActions on AppController { try { final dispatch = await codeAgentNodeOrchestratorInternal .buildGatewayDispatch(buildCodeAgentNodeStateInternal()); - await syncExternalAcpProvidersInternal(); final result = await goTaskServiceClientInternal.executeTask( GoTaskServiceRequest( sessionId: sessionKey, diff --git a/lib/app/app_controller_desktop_thread_sessions_collaboration_impl.dart b/lib/app/app_controller_desktop_thread_sessions_collaboration_impl.dart index 3f621da8..652817a5 100644 --- a/lib/app/app_controller_desktop_thread_sessions_collaboration_impl.dart +++ b/lib/app/app_controller_desktop_thread_sessions_collaboration_impl.dart @@ -160,7 +160,6 @@ Future runMultiAgentCollaborationThreadSessionInternal( ); controller.recomputeTasksInternal(); try { - await controller.syncExternalAcpProvidersInternal(); final result = await controller.goTaskServiceClientInternal.executeTask( GoTaskServiceRequest( sessionId: sessionKey, diff --git a/lib/runtime/external_code_agent_acp_desktop_transport.dart b/lib/runtime/external_code_agent_acp_desktop_transport.dart index 9e326701..5c746c82 100644 --- a/lib/runtime/external_code_agent_acp_desktop_transport.dart +++ b/lib/runtime/external_code_agent_acp_desktop_transport.dart @@ -13,8 +13,6 @@ class ExternalCodeAgentAcpDesktopTransport : _bridge = bridge ?? GoAcpStdioBridge(); final GoAcpStdioBridge _bridge; - List _syncedProviders = - const []; @visibleForTesting GoAcpStdioBridge get bridgeForTest => _bridge; @@ -22,19 +20,13 @@ class ExternalCodeAgentAcpDesktopTransport @override Future syncExternalProviders( List providers, - ) async { - _syncedProviders = List.unmodifiable( - providers, - ); - await _syncProviders(); - } + ) async {} @override Future loadExternalAcpCapabilities({ required AssistantExecutionTarget target, bool forceRefresh = false, }) async { - await _syncProviders(); final response = await _bridge.request( method: 'acp.capabilities', params: const {}, @@ -66,7 +58,6 @@ class ExternalCodeAgentAcpDesktopTransport String aiGatewayBaseUrl = '', String aiGatewayApiKey = '', }) async { - await _syncProviders(); final response = await _bridge.request( method: 'xworkmate.routing.resolve', params: { @@ -89,7 +80,6 @@ class ExternalCodeAgentAcpDesktopTransport GoTaskServiceRequest request, { required void Function(GoTaskServiceUpdate update) onUpdate, }) async { - await _syncProviders(); late final StreamSubscription> subscription; var streamedText = ''; String? completedMessage; @@ -158,28 +148,6 @@ class ExternalCodeAgentAcpDesktopTransport @override Future dispose() => _bridge.dispose(); - Future _syncProviders() async { - if (_syncedProviders.isEmpty) { - return; - } - await _bridge.request( - method: 'xworkmate.providers.sync', - params: { - 'providers': _syncedProviders - .map( - (item) => { - 'providerId': item.providerId, - 'endpoint': item.endpoint, - 'label': item.label, - 'authorizationHeader': item.authorizationHeader, - 'enabled': item.enabled, - }, - ) - .toList(growable: false), - }, - ); - } - Map _castMap(Object? value) { if (value is Map) { return value; diff --git a/lib/runtime/runtime_controllers_settings_account.dart b/lib/runtime/runtime_controllers_settings_account.dart index 35b561fb..41aeef6d 100644 --- a/lib/runtime/runtime_controllers_settings_account.dart +++ b/lib/runtime/runtime_controllers_settings_account.dart @@ -25,10 +25,7 @@ extension SettingsControllerAccountExtension on SettingsController { if (local.isNotEmpty) { return local; } - if (!snapshotInternal.acpBridgeServerModeConfig.usesCloudSyncBase) { - return ''; - } - return accountSyncStateInternal?.syncedDefaults.apisixUrl.trim() ?? ''; + return ''; } List get effectiveAiGatewayAvailableModels { diff --git a/lib/runtime/runtime_controllers_settings_account_impl.dart b/lib/runtime/runtime_controllers_settings_account_impl.dart index 6bf0925c..d0e578f3 100644 --- a/lib/runtime/runtime_controllers_settings_account_impl.dart +++ b/lib/runtime/runtime_controllers_settings_account_impl.dart @@ -2,6 +2,8 @@ import 'account_runtime_client.dart'; import 'runtime_controllers_settings.dart'; import 'runtime_models.dart'; +const _kProductionBridgeEndpoint = 'https://xworkmate-bridge.svc.plus'; + Future loginAccountSettingsInternal( SettingsController controller, { required String baseUrl, @@ -270,9 +272,7 @@ Future syncAccountSettingsInternal( lastSyncAt: nextState.lastSyncAtMs, remoteServerSummary: currentModeConfig.cloudSynced.remoteServerSummary .copyWith( - endpoint: response.profile.openclawUrl.trim().isNotEmpty - ? response.profile.openclawUrl.trim() - : response.profile.apisixUrl.trim(), + endpoint: _kProductionBridgeEndpoint, hasAdvancedOverrides: false, ), ), @@ -352,37 +352,6 @@ Future applyAccountSyncedDefaultsSettingsInternal( final previous = controller.snapshotInternal; var next = previous; final defaults = state.syncedDefaults; - if (defaults.openclawUrl.trim().isNotEmpty) { - final remoteProfile = previous.gatewayProfiles[kGatewayRemoteProfileIndex]; - final normalized = normalizeGatewayManualEndpointInternal( - host: defaults.openclawUrl, - port: remoteProfile.port, - tls: remoteProfile.tls, - ); - next = next.copyWithGatewayProfileAt( - kGatewayRemoteProfileIndex, - remoteProfile.copyWith( - mode: RuntimeConnectionMode.remote, - useSetupCode: false, - setupCode: '', - host: normalized.host, - port: normalized.port, - tls: normalized.tls, - ), - ); - } - - final gatewayTokenLocator = defaults.locatorForTarget( - kAccountManagedSecretTargetOpenclawGatewayToken, - ); - if (gatewayTokenLocator != null) { - final remoteProfile = next.gatewayProfiles[kGatewayRemoteProfileIndex]; - next = next.copyWithGatewayProfileAt( - kGatewayRemoteProfileIndex, - remoteProfile.copyWith(tokenRef: gatewayTokenLocator.target), - ); - } - if (defaults.vaultUrl.trim().isNotEmpty) { next = next.copyWith( vault: next.vault.copyWith(address: defaults.vaultUrl.trim()), @@ -395,12 +364,6 @@ Future applyAccountSyncedDefaultsSettingsInternal( ); } - if (defaults.apisixUrl.trim().isNotEmpty) { - next = next.copyWith( - aiGateway: next.aiGateway.copyWith(baseUrl: defaults.apisixUrl.trim()), - ); - } - final aiGatewayLocator = defaults.locatorForTarget( kAccountManagedSecretTargetAIGatewayAccessToken, ); @@ -433,9 +396,7 @@ Future applyAccountSyncedDefaultsSettingsInternal( .cloudSynced .remoteServerSummary .copyWith( - endpoint: defaults.openclawUrl.trim().isNotEmpty - ? defaults.openclawUrl.trim() - : defaults.apisixUrl.trim(), + endpoint: _kProductionBridgeEndpoint, hasAdvancedOverrides: false, ), ), diff --git a/test/features/settings/settings_page_core_test.dart b/test/features/settings/settings_page_core_test.dart index ec84c740..2771abf9 100644 --- a/test/features/settings/settings_page_core_test.dart +++ b/test/features/settings/settings_page_core_test.dart @@ -208,7 +208,7 @@ SettingsSnapshot _buildCanonicalSettings() { accountIdentifier: 'canonical@svc.plus', lastSyncAt: 123456789, remoteServerSummary: const AcpBridgeServerRemoteServerSummary( - endpoint: 'wss://gateway.svc.plus', + endpoint: 'https://xworkmate-bridge.svc.plus', hasAdvancedOverrides: false, ), ), diff --git a/test/runtime/account_sync_overwrite_test.dart b/test/runtime/account_sync_overwrite_test.dart index 00fb7c6e..36e14608 100644 --- a/test/runtime/account_sync_overwrite_test.dart +++ b/test/runtime/account_sync_overwrite_test.dart @@ -73,20 +73,20 @@ void main() { expect(first.state, 'ready'); expect( controller.snapshot.gatewayProfiles[kGatewayRemoteProfileIndex].host, - 'remote.gateway.svc.plus', + 'local.example.com', ); expect( controller .snapshot .gatewayProfiles[kGatewayRemoteProfileIndex] .tokenRef, - kAccountManagedSecretTargetOpenclawGatewayToken, + 'local_ref', ); expect(controller.snapshot.vault.address, 'https://vault.svc.plus'); expect(controller.snapshot.vault.namespace, 'prod'); expect( controller.snapshot.aiGateway.baseUrl, - 'https://apisix.svc.plus', + 'https://local-apisix.example.com', ); expect( controller.snapshot.aiGateway.apiKeyRef, @@ -116,7 +116,16 @@ void main() { expect(controller.snapshot.vault.address, 'https://vault.svc.plus'); expect( controller.snapshot.aiGateway.baseUrl, - 'https://apisix.svc.plus', + 'https://edited-apisix.example.com', + ); + expect( + controller + .snapshot + .acpBridgeServerModeConfig + .cloudSynced + .remoteServerSummary + .endpoint, + 'https://xworkmate-bridge.svc.plus', ); final rawSyncState = await store.loadSupportJson( diff --git a/test/runtime/external_acp_bridge_sync_order_test.dart b/test/runtime/external_acp_bridge_sync_order_test.dart index bc0e325c..a624c87e 100644 --- a/test/runtime/external_acp_bridge_sync_order_test.dart +++ b/test/runtime/external_acp_bridge_sync_order_test.dart @@ -46,48 +46,22 @@ class _FakeGoAcpStdioBridgeWithSyncOrder extends GoAcpStdioBridge { } void main() { - group('External ACP bridge sync order', () { - test('syncs providers before capabilities requests', () async { + group('External ACP bridge routing order', () { + test('loads capabilities without app-side provider sync', () async { final bridge = _FakeGoAcpStdioBridgeWithSyncOrder(); final transport = ExternalCodeAgentAcpDesktopTransport(bridge: bridge); - await transport - .syncExternalProviders(const [ - ExternalCodeAgentAcpSyncedProvider( - providerId: 'codex', - label: 'Codex', - endpoint: 'https://acp-server.svc.plus/codex/acp/rpc', - authorizationHeader: '', - enabled: true, - ), - ]); - await transport.loadExternalAcpCapabilities( target: AssistantExecutionTarget.singleAgent, ); - expect(bridge.methods, [ - 'xworkmate.providers.sync', - 'xworkmate.providers.sync', - 'acp.capabilities', - ]); + expect(bridge.methods, ['acp.capabilities']); }); - test('syncs providers before session start requests', () async { + test('starts sessions without app-side provider sync', () async { final bridge = _FakeGoAcpStdioBridgeWithSyncOrder(); final transport = ExternalCodeAgentAcpDesktopTransport(bridge: bridge); - await transport - .syncExternalProviders(const [ - ExternalCodeAgentAcpSyncedProvider( - providerId: 'codex', - label: 'Codex', - endpoint: 'https://acp-server.svc.plus/codex/acp/rpc', - authorizationHeader: '', - enabled: true, - ), - ]); - await transport.executeTask( const GoTaskServiceRequest( sessionId: 's1', @@ -108,11 +82,7 @@ void main() { onUpdate: (_) {}, ); - expect(bridge.methods, [ - 'xworkmate.providers.sync', - 'xworkmate.providers.sync', - 'session.start', - ]); + expect(bridge.methods, ['session.start']); }); }); } diff --git a/test/runtime/external_code_agent_acp_desktop_transport_test.dart b/test/runtime/external_code_agent_acp_desktop_transport_test.dart index 703808b2..d29dde91 100644 --- a/test/runtime/external_code_agent_acp_desktop_transport_test.dart +++ b/test/runtime/external_code_agent_acp_desktop_transport_test.dart @@ -77,26 +77,23 @@ void main() { }, ); - test( - 'only syncs when app has explicit provider overrides to send', - () async { - final bridge = _FakeGoAcpStdioBridge(); - final transport = ExternalCodeAgentAcpDesktopTransport(bridge: bridge); + test('ignores app-side provider sync in bridge-only mode', () async { + final bridge = _FakeGoAcpStdioBridge(); + final transport = ExternalCodeAgentAcpDesktopTransport(bridge: bridge); - await transport - .syncExternalProviders(const [ - ExternalCodeAgentAcpSyncedProvider( - providerId: 'codex', - label: 'Codex', - endpoint: 'https://acp-server.svc.plus/codex/acp/rpc', - authorizationHeader: '', - enabled: true, - ), - ]); + await transport + .syncExternalProviders(const [ + ExternalCodeAgentAcpSyncedProvider( + providerId: 'codex', + label: 'Codex', + endpoint: 'https://acp-server.svc.plus/codex/acp/rpc', + authorizationHeader: '', + enabled: true, + ), + ]); - expect(bridge.methods, ['xworkmate.providers.sync']); - }, - ); + expect(bridge.methods, isEmpty); + }); test( 'uses bridge routing resolve for preflight provider selection', diff --git a/test/runtime/runtime_controllers_settings_account_test.dart b/test/runtime/runtime_controllers_settings_account_test.dart index b0773c13..02999824 100644 --- a/test/runtime/runtime_controllers_settings_account_test.dart +++ b/test/runtime/runtime_controllers_settings_account_test.dart @@ -79,7 +79,7 @@ void main() { lastSyncAt: 123456789, remoteServerSummary: const AcpBridgeServerRemoteServerSummary( - endpoint: 'wss://gateway.svc.plus', + endpoint: 'https://xworkmate-bridge.svc.plus', hasAdvancedOverrides: false, ), ), diff --git a/test/runtime/settings_account_auth_flow_test.dart b/test/runtime/settings_account_auth_flow_test.dart index 23d4abbd..05edba4e 100644 --- a/test/runtime/settings_account_auth_flow_test.dart +++ b/test/runtime/settings_account_auth_flow_test.dart @@ -58,6 +58,15 @@ void main() { expect(controller.accountSyncState?.profileScope, 'tenant-shared'); expect(controller.accountSyncState?.tokenConfigured.apisix, isTrue); expect(await store.loadAccountSessionToken(), 'session-token'); + expect( + controller + .snapshot + .acpBridgeServerModeConfig + .cloudSynced + .remoteServerSummary + .endpoint, + 'https://xworkmate-bridge.svc.plus', + ); }, );