diff --git a/docs/architecture/bridge-runtime-routing-map.md b/docs/architecture/bridge-runtime-routing-map.md
index 64aa045a..1f71d189 100644
--- a/docs/architecture/bridge-runtime-routing-map.md
+++ b/docs/architecture/bridge-runtime-routing-map.md
@@ -4,7 +4,7 @@ Last Updated: 2026-04-21
本文记录 `xworkmate-app` 当前对 `xworkmate-bridge` 的运行时路由合同。UI 不直接承载这些路径;Assistant UI 仍由 `acp.capabilities` 返回的 `providerCatalog`、`gatewayProviders`、`availableExecutionTargets` 驱动。
-App 侧任务发送默认调用 bridge 主入口 `/acp/rpc`,不再拼接 provider-specific 直连 URL。OpenClaw `session.start` 和同一任务的 `session.message` 是唯一例外,使用 bridge 公开的 task submit 专用路径 `/gateway/openclaw`。该路径不是全局 ACP base endpoint。
+App 侧任务发送统一调用 bridge 主入口 `/acp/rpc`,不再拼接 provider-specific 或 gateway-specific 直连 URL。OpenClaw `session.start` 和同一任务的 `session.message` 也走 `/acp/rpc`,通过 `routing.explicitExecutionTarget=gateway` 与 `routing.preferredGatewayProviderId=openclaw` 表达目标。
## App Runtime Flow
@@ -22,11 +22,8 @@ flowchart TD
D --> J["OpenClaw"]
A --> P["POST https://xworkmate-bridge.svc.plus/acp/rpc"]
- A --> T["OpenClaw task POST /gateway/openclaw"]
P --> Q["Authorization: Bearer token"]
- T --> Q
- P --> R["provider / requestedExecutionTarget params"]
- T --> R
+ P --> R["provider / routing / requestedExecutionTarget params"]
R --> S["bridge-owned routing"]
S --> K["Hermes internal runtime"]
@@ -38,10 +35,10 @@ flowchart TD
## Routing Rules
-- App runtime control-plane requests, agent tasks, multi-agent tasks, `session.cancel`, and `session.close` use `https://xworkmate-bridge.svc.plus/acp/rpc`.
-- OpenClaw gateway `session.start` and follow-up `session.message` use `https://xworkmate-bridge.svc.plus/gateway/openclaw`.
+- App runtime control-plane requests, agent tasks, multi-agent tasks, OpenClaw gateway tasks, `session.cancel`, and `session.close` use `https://xworkmate-bridge.svc.plus/acp/rpc`.
+- OpenClaw gateway `session.start` and follow-up `session.message` are identified by routing metadata, not by a separate public path.
- Provider and gateway selection are passed as request params, including `provider`, `routing`, and `requestedExecutionTarget`.
- Bridge-owned internal routing is opaque to the App; it is not represented as public provider paths.
- The app must not route managed bridge tasks to local or LAN endpoints such as `127.0.0.1:*` or `192.168.*:*`.
- The app must not route managed bridge tasks by directly constructing `/acp-server/*` URLs.
-- `/gateway/openclaw` is allowed only for OpenClaw task submit; it must not be reused for capabilities, routing, gateway control-plane, cancel, close, or as an ACP base endpoint.
+- `/gateway/openclaw` must not participate in app runtime routing; it is neither a task submit path nor an ACP base endpoint.
diff --git a/docs/architecture/cross-repo-task-state-workflow.md b/docs/architecture/cross-repo-task-state-workflow.md
index 0caace65..26e38bc6 100644
--- a/docs/architecture/cross-repo-task-state-workflow.md
+++ b/docs/architecture/cross-repo-task-state-workflow.md
@@ -17,7 +17,7 @@ The core ownership split is:
```mermaid
flowchart LR
U["User input / follow-up"] --> APP["xworkmate-app
TaskThread + UI state"]
- APP -->|session.start / session.message| BR["xworkmate-bridge
/acp/rpc or /gateway/openclaw"]
+ APP -->|session.start / session.message| BR["xworkmate-bridge
/acp/rpc"]
BR -->|xworkmate.routing.resolve| ROUTE{"Execution target"}
ROUTE -->|single-agent| AG["codex / opencode / gemini / hermes"]
ROUTE -->|gateway=openclaw| OC["OpenClaw Gateway Runtime"]
@@ -108,7 +108,7 @@ flowchart TD
E --> G["drain queue"]
G --> C
- C --> H["Bridge /gateway/openclaw"]
+ C --> H["Bridge /acp/rpc
routing=gateway/openclaw"]
H --> I["OpenClaw execution"]
I --> J{"Result"}
J -->|success + output/files| K["APP ready
lastResultCode=success
sync artifacts"]
@@ -118,13 +118,12 @@ flowchart TD
## Bridge Session And Routing Workflow
-The bridge exposes one public session contract while keeping provider-specific behavior behind compatibility layers. `/gateway/openclaw` is a narrow OpenClaw task-submit lane, not a general ACP base endpoint.
+The bridge exposes one public session contract while keeping provider-specific behavior behind bridge-owned routing. OpenClaw task submit uses `/acp/rpc` with explicit gateway routing metadata, not a separate app-facing path.
```mermaid
flowchart TD
REQ["APP request"] --> EP{"HTTP / WS entry"}
EP -->|/acp or /acp/rpc| RPC["General JSON-RPC"]
- EP -->|/gateway/openclaw| GW["OpenClaw task-submit endpoint"]
RPC --> METHOD{"method"}
METHOD -->|acp.capabilities| CAP["Return agent providers + gatewayProviders=openclaw"]
@@ -133,11 +132,6 @@ flowchart TD
METHOD -->|session.message| MSG["Continue existing session"]
METHOD -->|session.cancel / session.close| CTRL["Cancel / close session"]
- GW --> GUARD{"method is session.start / session.message ?"}
- GUARD -->|no| REJECT["Reject: not a global ACP base"]
- GUARD -->|yes| FORCE["Force routing=gateway/openclaw
Reject multiAgent"]
- FORCE --> START
-
START --> ORCH["session_orchestrator"]
MSG --> ORCH
ORCH --> PROVIDER{"provider compat"}
@@ -190,7 +184,7 @@ flowchart LR
## Boundary Rules
- The app does not store OpenClaw URLs. It only consumes bridge capabilities where `gatewayProviders` includes `openclaw`.
-- `/gateway/openclaw` is only for OpenClaw `session.start` and `session.message`; it is not a global ACP endpoint.
+- OpenClaw `session.start` and `session.message` use `/acp/rpc` with explicit OpenClaw gateway routing metadata; `/gateway/openclaw` is not an app-facing endpoint.
- Follow-up conversation uses the same `sessionKey` / `threadId`. Bridge `session.message` must continue the provider session state or return a structured continuation error.
- Artifact ownership is enforced by `openclaw-multi-session-plugins` with `tasks//` scope. The app syncs only the current run's artifacts into the local thread workspace.
- Upgrade/install flows must preserve real local history. Cleanup must only remove explicitly known test-pollution session keys.
diff --git a/docs/architecture/unified-routing-architecture.md b/docs/architecture/unified-routing-architecture.md
index 18732515..73efc484 100644
--- a/docs/architecture/unified-routing-architecture.md
+++ b/docs/architecture/unified-routing-architecture.md
@@ -2,7 +2,7 @@
## 1. 架构概览 (Unified Routing Architecture)
-当前系统采用 `xworkmate-bridge.svc.plus` 作为统一入口。App 侧通过 managed bridge ACP 主入口处理能力发现、路由解析、agent / multi-agent 任务和会话控制;OpenClaw `session.start` / `session.message` 使用 bridge 暴露的 `/gateway/openclaw` task submit 专用入口。Provider runtime 地址仍由 bridge 后端内部拥有,不暴露为 App-facing public mapping。
+当前系统采用 `xworkmate-bridge.svc.plus` 作为统一入口。App 侧通过 managed bridge ACP 主入口 `/acp/rpc` 处理能力发现、路由解析、agent / multi-agent 任务、OpenClaw gateway 任务和会话控制。Provider runtime 与 gateway runtime 地址仍由 bridge 后端内部拥有,不暴露为 App-facing public mapping。
```mermaid
graph TD
@@ -16,7 +16,6 @@ graph TD
subgraph "Bridge-owned Routing"
ManagedBridge["Managed Bridge ACP
/acp/rpc"]
- OpenClawSubmit["OpenClaw task submit
/gateway/openclaw"]
CodexProvider["Codex internal runtime"]
OpenCodeProvider["OpenCode internal runtime"]
GeminiAdapter["Gemini internal runtime"]
@@ -27,11 +26,10 @@ graph TD
Client -->|HTTPS/WSS| Bridge_Domain
Bridge_Domain -->|/acp/rpc| ManagedBridge
- Bridge_Domain -->|/gateway/openclaw| OpenClawSubmit
ManagedBridge -->|provider routing| CodexProvider
ManagedBridge -->|provider routing| OpenCodeProvider
ManagedBridge -->|provider routing| GeminiAdapter
- OpenClawSubmit -->|forced openclaw routing| OpenClawGateway
+ ManagedBridge -->|gateway routing| OpenClawGateway
%% Service Connections
ManagedBridge -.->|Capabilities Discovery| Client
@@ -41,15 +39,14 @@ graph TD
| Bridge-owned mapping | App 侧行为 | 备注 |
| :--- | :--- | :--- |
-| `/acp/rpc` | 直接调用 | 能力发现、路由解析、agent / multi-agent 任务、cancel、close |
-| `/gateway/openclaw` | 仅 OpenClaw task submit | 只用于 OpenClaw `session.start` / `session.message`,不是 ACP base endpoint |
+| `/acp/rpc` | 直接调用 | 能力发现、路由解析、agent / multi-agent 任务、OpenClaw gateway 任务、cancel、close |
| provider runtime | 不直连 | Bridge 后端内部解析 provider |
| gateway runtime | 不直连 | Bridge 后端内部解析 gateway provider |
## 3. 运维配置优化
### 3.1 统一鉴权
-App 发往 `xworkmate-bridge.svc.plus/acp/rpc` 和 `xworkmate-bridge.svc.plus/gateway/openclaw` 的请求必须携带:
+App 发往 `xworkmate-bridge.svc.plus/acp/rpc` 的请求必须携带:
- **Header**: `Authorization: Bearer `
- **未授权响应**: `401 Unauthorized`
@@ -66,5 +63,5 @@ App 发往 `xworkmate-bridge.svc.plus/acp/rpc` 和 `xworkmate-bridge.svc.plus/ga
- App 不写入或拼接本地 provider endpoint。
- App 不直接调用 `/acp-server/*`。
-- App 仅可在 OpenClaw `session.start` / `session.message` task submit 中调用 `/gateway/openclaw`,不得把它作为全局 ACP base endpoint。
+- App 不调用 `/gateway/openclaw`;OpenClaw `session.start` / `session.message` task submit 通过 `/acp/rpc` 的 routing metadata 表达。
- `acp.capabilities` 是 provider catalog、gateway catalog、available execution targets 的唯一来源。
diff --git a/docs/testing/app-external-service-api-test-matrix.md b/docs/testing/app-external-service-api-test-matrix.md
index 6028eee4..0b7d4796 100644
--- a/docs/testing/app-external-service-api-test-matrix.md
+++ b/docs/testing/app-external-service-api-test-matrix.md
@@ -40,7 +40,7 @@ Last Updated: 2026-04-22
- 安全边界确认:
- `BRIDGE_AUTH_TOKEN` 是否只作为 Bearer token 使用
- `BRIDGE_SERVER_URL` 是否仅作为元数据
- - 会话接口是否按当前 bridge contract 选择 `/acp/rpc` 或 OpenClaw task submit 专用入口 `/gateway/openclaw`
+ - 会话接口是否按当前 bridge contract 统一选择 `/acp/rpc`,并用 routing metadata 表达 OpenClaw gateway 目标
## 2. 统一测试前提
@@ -76,8 +76,7 @@ Last Updated: 2026-04-22
| accounts | `/api/auth/login` | `POST` | 无 | 登录并拿 session token | Apple 审核只读账号使用 |
| accounts | `/api/auth/session` | `GET` | `Authorization: Bearer ` | 获取当前会话和用户信息 | APP 端登录态校验 |
| accounts | `/api/auth/xworkmate/profile/sync` | `GET` | `Authorization: Bearer ` | 拉取 bridge 同步元数据 | 返回 `BRIDGE_SERVER_URL` / `BRIDGE_AUTH_TOKEN` |
-| bridge | `/acp/rpc` | `POST` | `Authorization: Bearer ` | bridge JSON-RPC 主入口 | capabilities、routing、agent / multi-agent 任务、cancel、close |
-| bridge | `/gateway/openclaw` | `POST` | `Authorization: Bearer ` | OpenClaw task submit 专用入口 | 仅 OpenClaw `session.start` / `session.message`,不是全局 ACP base endpoint |
+| bridge | `/acp/rpc` | `POST` | `Authorization: Bearer ` | bridge JSON-RPC 主入口 | capabilities、routing、agent / multi-agent 任务、OpenClaw gateway 任务、cancel、close |
| bridge | `acp.capabilities` | JSON-RPC method | 同上 | 拉取 provider catalog / target catalog | capability 只读快照 |
| bridge | `xworkmate.routing.resolve` | JSON-RPC method | 同上 | 解析 provider / gateway / skills 路由 | 返回 resolved / unavailable 信息 |
| bridge | `session.start` | JSON-RPC method | 同上 | 开启新会话 | session 生命周期起点 |
@@ -581,10 +580,9 @@ Last Updated: 2026-04-22
### 8.3 建议断言
- `Authorization` 必须是 `Bearer `
-- capability、routing、agent / multi-agent 任务、`session.cancel`、`session.close` 请求路径必须是 `/acp/rpc`
-- OpenClaw gateway `session.start` / `session.message` 请求路径必须是 `/gateway/openclaw`
+- capability、routing、agent / multi-agent 任务、OpenClaw gateway `session.start` / `session.message`、`session.cancel`、`session.close` 请求路径必须是 `/acp/rpc`
- 不允许出现 `/acp-server/` 直连请求
-- `/gateway/openclaw` 不得用于 capabilities、routing、gateway control-plane、cancel、close,且不得作为全局 ACP base endpoint
+- 不允许出现 `/gateway/openclaw` app-facing 请求;OpenClaw gateway 目标必须通过 routing metadata 表达
- `session.cancel` / `session.close` 必须接受 `sessionId` + `threadId`
- `BRIDGE_SERVER_URL` 不可参与运行时路径拼接
@@ -612,7 +610,7 @@ Last Updated: 2026-04-22
- 账户侧负责登录与同步元数据
- bridge 侧负责 capability、路由解析和会话生命周期
-- capability、routing、agent / multi-agent 任务、cancel、close 经过 `/acp/rpc`
-- OpenClaw `session.start` / `session.message` 经过 `/gateway/openclaw`
+- capability、routing、agent / multi-agent 任务、OpenClaw gateway 任务、cancel、close 经过 `/acp/rpc`
+- OpenClaw `session.start` / `session.message` 通过 `/acp/rpc` routing metadata 表达 gateway/openclaw 目标
- `session.cancel` / `session.close` 的协议入口已验证可用
- 当前剩余风险集中在桥接后端下游 provider 连接,而不是 APP 侧接口拼接
diff --git a/docs/xworkmate-app-core-functional-test-plan-v1.md b/docs/xworkmate-app-core-functional-test-plan-v1.md
index bfef8370..3376125f 100644
--- a/docs/xworkmate-app-core-functional-test-plan-v1.md
+++ b/docs/xworkmate-app-core-functional-test-plan-v1.md
@@ -31,8 +31,8 @@
│ │ (SSE/HTTP) │ │ successfully via /acp/rpc │
│ /acp-server/opencode/ │ JSON-RPC │ PASS │ acp.capabilities returned │
│ │ (SSE/HTTP) │ │ successfully via /acp/rpc │
- │ /gateway/openclaw/ │ WSS / RPC │ PASS │ WebSocket handshake successful at │
- │ │ │ │ /acp; received connect.challenge
+ │ /acp/rpc openclaw │ JSON-RPC │ PASS │ OpenClaw routing is expressed via │
+ │ routing │ │ │ routing metadata on /acp/rpc │
### 2. Assistant 线程体验
diff --git a/lib/app/app_controller_desktop_runtime_helpers.dart b/lib/app/app_controller_desktop_runtime_helpers.dart
index ca3c6e4f..9e101e6b 100644
--- a/lib/app/app_controller_desktop_runtime_helpers.dart
+++ b/lib/app/app_controller_desktop_runtime_helpers.dart
@@ -1045,9 +1045,6 @@ extension AppControllerDesktopRuntimeHelpers on AppController {
if (bridgeEndpoint == null) {
return null;
}
- if (_usesOpenClawTaskSubmitEndpointInternal(request)) {
- return bridgeEndpoint.replace(path: '/gateway/openclaw');
- }
return resolveAcpHttpRpcEndpoint(bridgeEndpoint);
}
@@ -1148,22 +1145,6 @@ extension AppControllerDesktopRuntimeHelpers on AppController {
) => kGatewayRemoteProfileIndex;
}
-bool _usesOpenClawTaskSubmitEndpointInternal(GoTaskServiceRequest request) {
- if (request.isMultiAgentRequest || !request.target.isGateway) {
- return false;
- }
- final providerId = normalizeSingleAgentProviderId(
- request.provider.providerId,
- );
- if (providerId == kCanonicalGatewayProviderId) {
- return true;
- }
- return normalizeSingleAgentProviderId(
- request.effectiveRouting.preferredGatewayTarget,
- ) ==
- kCanonicalGatewayProviderId;
-}
-
String _normalizeAuthorizationHeaderInternal(String raw) {
final trimmed = raw.trim();
if (trimmed.isEmpty) {
diff --git a/lib/runtime/external_code_agent_acp_desktop_transport.dart b/lib/runtime/external_code_agent_acp_desktop_transport.dart
index d90f8e4e..01af6485 100644
--- a/lib/runtime/external_code_agent_acp_desktop_transport.dart
+++ b/lib/runtime/external_code_agent_acp_desktop_transport.dart
@@ -276,16 +276,6 @@ class ExternalCodeAgentAcpDesktopTransport
if (controlEndpoint != null) {
return controlEndpoint;
}
- final taskPath = taskEndpoint?.path.trim() ?? '';
- if (taskEndpoint != null &&
- (taskPath == '/gateway/openclaw' ||
- taskPath.endsWith('/gateway/openclaw'))) {
- return taskEndpoint.replace(
- path: '/acp/rpc',
- query: null,
- fragment: null,
- );
- }
return resolveAcpHttpRpcEndpoint(taskEndpoint);
}
diff --git a/lib/runtime/gateway_acp_client.dart b/lib/runtime/gateway_acp_client.dart
index d8931c17..cd27047b 100644
--- a/lib/runtime/gateway_acp_client.dart
+++ b/lib/runtime/gateway_acp_client.dart
@@ -1311,14 +1311,6 @@ class GatewayAcpClient {
Uri? _resolveHttpRpcEndpoint([Uri? endpointOverride, String method = '']) {
final endpoint = endpointOverride ?? endpointResolver();
- if (_isOpenClawTaskSubmitEndpoint(endpoint) &&
- _isOpenClawTaskSubmitMethod(method)) {
- return endpoint?.replace(
- path: '/gateway/openclaw',
- query: null,
- fragment: null,
- );
- }
return resolveAcpHttpRpcEndpoint(endpoint);
}
@@ -1470,15 +1462,6 @@ bool _isOpenClawTaskSubmitMethod(String method) {
return normalized == 'session.start' || normalized == 'session.message';
}
-bool _isOpenClawTaskSubmitEndpoint(Uri? endpoint) {
- var path = endpoint?.path.trim() ?? '';
- if (!path.startsWith('/')) {
- path = '/$path';
- }
- path = path.replaceFirst(RegExp(r'/+$'), '');
- return path == '/gateway/openclaw';
-}
-
Duration gatewayAcpHttpResponseTimeoutFor(
Uri endpoint,
String method, [
@@ -1487,15 +1470,6 @@ Duration gatewayAcpHttpResponseTimeoutFor(
if (!_isOpenClawTaskSubmitMethod(method)) {
return const Duration(seconds: 120);
}
- if (_isOpenClawTaskSubmitEndpoint(endpoint)) {
- return Duration(
- minutes: gatewayAcpTaskRuntimeBudgetMinutesForParams({
- 'requestedExecutionTarget':
- AssistantExecutionTarget.gateway.promptValue,
- ...params,
- }),
- );
- }
return Duration(minutes: gatewayAcpTaskRuntimeBudgetMinutesForParams(params));
}
diff --git a/test/features/app/app_shell_surface_test.dart b/test/features/app/app_shell_surface_test.dart
index a5d4fc88..758f012b 100644
--- a/test/features/app/app_shell_surface_test.dart
+++ b/test/features/app/app_shell_surface_test.dart
@@ -28,8 +28,13 @@ void main() {
);
await tester.pumpAndSettle();
- expect(find.text('助手'), findsOneWidget);
- expect(find.text('设置'), findsOneWidget);
+ expect(find.byKey(const Key('mobile-assistant-page')), findsOneWidget);
+ expect(find.byKey(const Key('mobile-settings-page')), findsNothing);
+
+ controller.openSettings();
+ await tester.pump();
+
+ expect(find.byKey(const Key('mobile-settings-page')), findsOneWidget);
expect(find.text('Mobile-safe'), findsNothing);
expect(find.text('安全审批'), findsNothing);
expect(find.byKey(const Key('mobile-safe-strip')), findsNothing);
diff --git a/test/runtime/gateway_acp_client_auth_test.dart b/test/runtime/gateway_acp_client_auth_test.dart
index 0d58026d..f1b04615 100644
--- a/test/runtime/gateway_acp_client_auth_test.dart
+++ b/test/runtime/gateway_acp_client_auth_test.dart
@@ -748,8 +748,7 @@ void main() {
final transport = ExternalCodeAgentAcpDesktopTransport(
client: GatewayAcpClient(endpointResolver: () => endpoint),
endpointResolver: (_) => endpoint,
- taskEndpointResolver: (_) =>
- endpoint.replace(path: '/gateway/openclaw'),
+ taskEndpointResolver: (_) => endpoint,
);
addTearDown(transport.dispose);
@@ -777,10 +776,7 @@ void main() {
expect(result.artifacts.single.relativePath, 'exports/snapshot.md');
expect(result.remoteWorkingDirectory, '/remote/openclaw/workspace');
expect(result.remoteWorkspaceRefKind, WorkspaceRefKind.remotePath);
- expect(
- requestPaths,
- containsAll(['/gateway/openclaw', '/acp/rpc']),
- );
+ expect(requestPaths, everyElement('/acp/rpc'));
},
);
@@ -842,8 +838,7 @@ void main() {
final transport = ExternalCodeAgentAcpDesktopTransport(
client: GatewayAcpClient(endpointResolver: () => endpoint),
endpointResolver: (_) => endpoint,
- taskEndpointResolver: (_) =>
- endpoint.replace(path: '/gateway/openclaw'),
+ taskEndpointResolver: (_) => endpoint,
recoveryPollDelay: Duration.zero,
recoveryMaxAttempts: 1,
);
@@ -871,10 +866,7 @@ void main() {
expect(result.success, isTrue);
expect(result.message, 'recovered after SSE no result');
- expect(
- requestPaths,
- containsAll(['/gateway/openclaw', '/acp/rpc']),
- );
+ expect(requestPaths, everyElement('/acp/rpc'));
},
);
@@ -942,8 +934,7 @@ void main() {
final transport = ExternalCodeAgentAcpDesktopTransport(
client: GatewayAcpClient(endpointResolver: () => endpoint),
endpointResolver: (_) => endpoint,
- taskEndpointResolver: (_) =>
- endpoint.replace(path: '/gateway/openclaw'),
+ taskEndpointResolver: (_) => endpoint,
recoveryPollDelay: Duration.zero,
recoveryMaxAttempts: 4,
);
@@ -1033,8 +1024,7 @@ void main() {
final transport = ExternalCodeAgentAcpDesktopTransport(
client: GatewayAcpClient(endpointResolver: () => endpoint),
endpointResolver: (_) => endpoint,
- taskEndpointResolver: (_) =>
- endpoint.replace(path: '/gateway/openclaw'),
+ taskEndpointResolver: (_) => endpoint,
recoveryPollDelay: Duration.zero,
recoveryMaxAttempts: 1,
);
@@ -1544,7 +1534,7 @@ void main() {
);
test(
- 'desktop task execution rejects OpenClaw gateway path for non-task methods',
+ 'desktop task execution rejects OpenClaw gateway path as bridge RPC base',
() async {
final capture = await _startAcpHttpServer();
addTearDown(capture.close);
@@ -1573,7 +1563,7 @@ void main() {
);
test(
- 'desktop task execution routes OpenClaw through required task endpoint',
+ 'desktop task execution routes OpenClaw through unified bridge RPC',
() async {
final capture = await _startAcpHttpServer(
streamResponse: true,
@@ -1606,8 +1596,7 @@ void main() {
final transport = ExternalCodeAgentAcpDesktopTransport(
client: client,
endpointResolver: (_) => capture.baseEndpoint,
- taskEndpointResolver: (_) =>
- capture.baseEndpoint.replace(path: '/gateway/openclaw'),
+ taskEndpointResolver: (_) => capture.baseEndpoint,
);
final result = await transport.executeTask(
@@ -1620,9 +1609,9 @@ void main() {
expect(capture.authorizationHeader, 'Bearer bridge-token');
expect(capture.acceptHeader, 'text/event-stream, application/json');
- expect(capture.requestPath, '/gateway/openclaw');
+ expect(capture.requestPath, '/acp/rpc');
expect(capture.requestPath, isNot(contains('/acp-server')));
- expect(capture.requestPath, isNot(contains('/acp-server/gateway')));
+ expect(capture.requestPath, isNot(contains('/gateway/openclaw')));
final params = _lastRequestParams(capture);
final routing = params['routing'] as Map;
expect(params.containsKey('gatewayProvider'), isFalse);
@@ -1706,18 +1695,17 @@ void main() {
'preferredGatewayProviderId': 'openclaw',
},
},
- endpointOverride: capture.baseEndpoint.replace(
- path: '/gateway/openclaw',
- ),
+ endpointOverride: capture.baseEndpoint,
onNotification: notifications.add,
);
final diagnostics = (response['_xworkmateDiagnostics'] as Map)
.cast();
- expect(capture.requestPath, '/gateway/openclaw');
+ expect(capture.requestPath, '/acp/rpc');
expect((response['result'] as Map)['output'], 'done');
expect(diagnostics['transport'], 'http-sse');
- expect(diagnostics['requestUrl'], contains('/gateway/openclaw'));
+ expect(diagnostics['requestUrl'], contains('/acp/rpc'));
+ expect(diagnostics['requestUrl'], isNot(contains('/gateway/openclaw')));
expect(diagnostics['bodyRead'], isTrue);
expect(diagnostics['sseKeepaliveReceived'], isTrue);
expect(diagnostics['sseLastEventAtMs'], isPositive);
@@ -1734,7 +1722,7 @@ void main() {
);
test(
- 'desktop OpenClaw follow-up routes through required task endpoint',
+ 'desktop OpenClaw follow-up routes through unified bridge RPC',
() async {
final capture = await _startAcpHttpServer();
addTearDown(capture.close);
@@ -1746,8 +1734,7 @@ void main() {
final transport = ExternalCodeAgentAcpDesktopTransport(
client: client,
endpointResolver: (_) => capture.baseEndpoint,
- taskEndpointResolver: (_) =>
- capture.baseEndpoint.replace(path: '/gateway/openclaw'),
+ taskEndpointResolver: (_) => capture.baseEndpoint,
);
await transport.executeTask(
@@ -1760,7 +1747,7 @@ void main() {
);
expect(capture.acceptHeader, 'text/event-stream, application/json');
- expect(capture.requestPath, '/gateway/openclaw');
+ expect(capture.requestPath, '/acp/rpc');
expect(capture.requestBody, contains('"method":"session.message"'));
},
);
@@ -1769,9 +1756,6 @@ void main() {
final openClawEndpoint = Uri.parse(
'https://xworkmate-bridge.svc.plus/acp/rpc',
);
- final openClawTaskEndpoint = Uri.parse(
- 'https://xworkmate-bridge.svc.plus/gateway/openclaw',
- );
final acpEndpoint = Uri.parse(
'https://xworkmate-bridge.svc.plus/acp/rpc',
);
@@ -1795,22 +1779,6 @@ void main() {
),
const Duration(minutes: 30),
);
- expect(
- gatewayAcpHttpResponseTimeoutFor(
- openClawTaskEndpoint,
- 'session.start',
- const {'taskPrompt': 'Reply after a long wait'},
- ),
- const Duration(minutes: 10),
- );
- expect(
- gatewayAcpHttpResponseTimeoutFor(
- openClawTaskEndpoint,
- 'session.message',
- const {'taskPrompt': '输出 PPTX 和 Markdown 文件'},
- ),
- const Duration(minutes: 30),
- );
expect(
gatewayAcpHttpResponseTimeoutFor(acpEndpoint, 'session.start'),
const Duration(minutes: 2),
@@ -1821,62 +1789,58 @@ void main() {
);
});
- test(
- 'desktop controller uses OpenClaw endpoint only for gateway task submit',
- () {
- final controller = AppController(
- environmentOverride: const {},
- );
- addTearDown(controller.dispose);
+ test('desktop controller uses unified bridge RPC for all task submits', () {
+ final controller = AppController(
+ environmentOverride: const {},
+ );
+ addTearDown(controller.dispose);
- final openClawStart = controller
- .resolveExternalAcpEndpointForRequestInternal(
- _taskRequest(
- target: AssistantExecutionTarget.gateway,
- provider: SingleAgentProvider.openclaw,
- ),
- );
- final openClawFollowUp = controller
- .resolveExternalAcpEndpointForRequestInternal(
- _taskRequest(
- target: AssistantExecutionTarget.gateway,
- provider: SingleAgentProvider.openclaw,
- resumeSession: true,
- ),
- );
- final unspecifiedGateway = controller
- .resolveExternalAcpEndpointForRequestInternal(
- _taskRequest(
- target: AssistantExecutionTarget.gateway,
- provider: SingleAgentProvider.unspecified,
- ),
- );
- final multiAgentGateway = controller
- .resolveExternalAcpEndpointForRequestInternal(
- _taskRequest(
- target: AssistantExecutionTarget.gateway,
- provider: SingleAgentProvider.openclaw,
- multiAgent: true,
- ),
- );
- final agentTask = controller
- .resolveExternalAcpEndpointForRequestInternal(
- _taskRequest(
- target: AssistantExecutionTarget.agent,
- provider: SingleAgentProvider.codex,
- ),
- );
+ final openClawStart = controller
+ .resolveExternalAcpEndpointForRequestInternal(
+ _taskRequest(
+ target: AssistantExecutionTarget.gateway,
+ provider: SingleAgentProvider.openclaw,
+ ),
+ );
+ final openClawFollowUp = controller
+ .resolveExternalAcpEndpointForRequestInternal(
+ _taskRequest(
+ target: AssistantExecutionTarget.gateway,
+ provider: SingleAgentProvider.openclaw,
+ resumeSession: true,
+ ),
+ );
+ final unspecifiedGateway = controller
+ .resolveExternalAcpEndpointForRequestInternal(
+ _taskRequest(
+ target: AssistantExecutionTarget.gateway,
+ provider: SingleAgentProvider.unspecified,
+ ),
+ );
+ final multiAgentGateway = controller
+ .resolveExternalAcpEndpointForRequestInternal(
+ _taskRequest(
+ target: AssistantExecutionTarget.gateway,
+ provider: SingleAgentProvider.openclaw,
+ multiAgent: true,
+ ),
+ );
+ final agentTask = controller.resolveExternalAcpEndpointForRequestInternal(
+ _taskRequest(
+ target: AssistantExecutionTarget.agent,
+ provider: SingleAgentProvider.codex,
+ ),
+ );
- expect(openClawStart?.path, '/gateway/openclaw');
- expect(openClawFollowUp?.path, '/gateway/openclaw');
- expect(unspecifiedGateway?.path, '/gateway/openclaw');
- expect(multiAgentGateway?.path, '/acp/rpc');
- expect(agentTask?.path, '/acp/rpc');
- },
- );
+ expect(openClawStart?.path, '/acp/rpc');
+ expect(openClawFollowUp?.path, '/acp/rpc');
+ expect(unspecifiedGateway?.path, '/acp/rpc');
+ expect(multiAgentGateway?.path, '/acp/rpc');
+ expect(agentTask?.path, '/acp/rpc');
+ });
test(
- 'desktop controller resolves OpenClaw gateway submit to required task endpoint',
+ 'desktop controller resolves OpenClaw gateway submit to unified bridge RPC',
() {
final controller = AppController(
environmentOverride: const {},
@@ -1893,10 +1857,10 @@ void main() {
expect(
endpoint.toString(),
- 'https://xworkmate-bridge.svc.plus/gateway/openclaw',
+ 'https://xworkmate-bridge.svc.plus/acp/rpc',
);
expect(endpoint, isNotNull);
- expect(endpoint!.path, isNot('/acp/rpc'));
+ expect(endpoint!.path, '/acp/rpc');
},
);