From 1fdb3f76b0b71f9bdb9b32fedbad7ea90457fd04 Mon Sep 17 00:00:00 2001 From: linruitao <296659110@qq.com> Date: Fri, 29 May 2026 11:49:39 +0800 Subject: [PATCH] fix(yuanbao): correct AuthBind plugin_version / bot_version mapping (#2804) --- .../channels/providers/yuanbao/config.rs | 35 +++++++++++++++++-- .../channels/providers/yuanbao/connection.rs | 8 +++-- .../channels/providers/yuanbao/proto.rs | 20 ++++++----- .../providers/yuanbao/proto_constants.rs | 4 +-- .../channels/providers/yuanbao/sign.rs | 4 +-- 5 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/openhuman/channels/providers/yuanbao/config.rs b/src/openhuman/channels/providers/yuanbao/config.rs index 3f79888d6..660f9e6fb 100644 --- a/src/openhuman/channels/providers/yuanbao/config.rs +++ b/src/openhuman/channels/providers/yuanbao/config.rs @@ -9,6 +9,22 @@ use serde::{Deserialize, Serialize}; use super::errors::YuanbaoError; +/// Default value for `DeviceInfo.app_version` (server-side `plugin_version`). +/// +/// In `yuanbao-openclaw-plugin`'s wire mapping, `plugin_version` represents +/// the *plugin-layer* (npm package) version while `bot_version` represents +/// the *framework runtime* version. OpenHuman has no separate plugin layer; +/// we reuse this slot for the **yuanbao channel provider's** own iteration +/// version. Bump it when the provider's protocol adapter changes in a way +/// the server might care about. The framework runtime version is reported +/// separately via `CARGO_PKG_VERSION` in `DeviceInfo.bot_version`. +pub(crate) const DEFAULT_PLUGIN_VERSION: &str = "0.1.0"; + +/// Strip legacy `openhuman/` prefix from version strings in config/TOML. +pub(crate) fn strip_version_prefix(version: &str) -> &str { + version.strip_prefix("openhuman/").unwrap_or(version) +} + /// Production environment endpoints (default). const PROD_API_DOMAIN: &str = "https://bot.yuanbao.tencent.com"; const PROD_WS_URL: &str = "wss://bot-wss.yuanbao.tencent.com/wss/connection"; @@ -46,8 +62,18 @@ pub struct YuanbaoConfig { /// `api_domain/api/token/sign` with `(app_key, app_secret)` to fetch one. #[serde(default)] pub token: String, - /// Plugin/bot version reported in `AuthBindReq.DeviceInfo.bot_version`. - #[serde(default = "default_bot_version")] + /// Yuanbao channel-provider iteration version, reported in + /// `AuthBindReq.DeviceInfo.app_version` (server-side `plugin_version`). + /// + /// **NOTE:** Despite the legacy `bot_version` TOML key name, this value + /// fills the *plugin*-version slot on the wire — see + /// [`DEFAULT_PLUGIN_VERSION`] for the semantic mapping. The framework + /// runtime version (`DeviceInfo.bot_version` / server `bot_version`) + /// is sourced from `CARGO_PKG_VERSION` and is **not** user-configurable; + /// operators who previously set this key intending to override the + /// framework version should drop it. New configs may use the clearer + /// `plugin_version` alias. + #[serde(default = "default_bot_version", alias = "plugin_version")] pub bot_version: String, /// Optional bot display name — used by the `@bot` mention guard. #[serde(default)] @@ -161,8 +187,11 @@ impl YuanbaoConfig { } } +/// Default value for [`YuanbaoConfig::bot_version`] — populates +/// `DeviceInfo.app_version` (server `plugin_version`) when the user config +/// omits the field. fn default_bot_version() -> String { - "openhuman/0.1.0".into() + DEFAULT_PLUGIN_VERSION.into() } fn default_env() -> String { diff --git a/src/openhuman/channels/providers/yuanbao/connection.rs b/src/openhuman/channels/providers/yuanbao/connection.rs index def6725ed..423a1065c 100644 --- a/src/openhuman/channels/providers/yuanbao/connection.rs +++ b/src/openhuman/channels/providers/yuanbao/connection.rs @@ -401,15 +401,19 @@ impl YuanbaoConnection { // "bot" when missing (matches the plugin's static-token branch // and `data.source || "bot"` resolution). let resolved_source = if source.is_empty() { "bot" } else { source }; + // Align with yuanbao-openclaw-plugin: app_version → plugin_version, + // DeviceInfo field 24 → bot_version (OpenHuman framework / CARGO_PKG_VERSION). + let plugin_version = super::config::strip_version_prefix(&cfg.bot_version); + let framework_version = env!("CARGO_PKG_VERSION"); let frame = encode_auth_bind( "ybBot", &uid, resolved_source, token, &msg_id, - env!("CARGO_PKG_VERSION"), + plugin_version, std::env::consts::OS, - &cfg.bot_version, + framework_version, &cfg.route_env, ); self.send_frame(frame).await diff --git a/src/openhuman/channels/providers/yuanbao/proto.rs b/src/openhuman/channels/providers/yuanbao/proto.rs index e678f7422..e30f72574 100644 --- a/src/openhuman/channels/providers/yuanbao/proto.rs +++ b/src/openhuman/channels/providers/yuanbao/proto.rs @@ -599,23 +599,25 @@ mod tests { assert_eq!(frame.data, b"payload"); } + /// Smoke-test that [`encode_auth_bind`] produces a frame round-trippable + /// via [`decode_conn_msg`] and that the `app_version` / `bot_version` + /// arguments land in the expected `DeviceInfo` fields (regression guard + /// for the plugin_version/bot_version swap). #[test] fn auth_bind_smoke() { let buf = encode_auth_bind( - "biz", - "uid", - "openclaw", - "tok", - "mid", - "1.0", - "linux", - "openhuman/0.1.0", - "", + "biz", "uid", "openclaw", "tok", "mid", "0.1.0", "linux", "1.0", "", ); let frame = decode_conn_msg(&buf).unwrap(); assert_eq!(frame.cmd, cmd::AUTH_BIND); assert_eq!(frame.module, module::CONN_ACCESS); assert!(!frame.data.is_empty()); + + let req_fields = parse_fields(&frame.data).unwrap(); + let dev_buf = get_bytes(&req_fields, 3); + let dev_fields = parse_fields(&dev_buf).unwrap(); + assert_eq!(get_string(&dev_fields, 1), "0.1.0", "app_version"); + assert_eq!(get_string(&dev_fields, 24), "1.0", "bot_version"); } #[test] diff --git a/src/openhuman/channels/providers/yuanbao/proto_constants.rs b/src/openhuman/channels/providers/yuanbao/proto_constants.rs index bde9fdb58..33c54f01a 100644 --- a/src/openhuman/channels/providers/yuanbao/proto_constants.rs +++ b/src/openhuman/channels/providers/yuanbao/proto_constants.rs @@ -65,11 +65,11 @@ pub mod tim { } /// Fixed instance id reported in `AuthBindReq.DeviceInfo.instance_id` and -/// the `X-Instance-Id` HTTP header. Mirrors `OPENCLAW_ID = 16` used by +/// the `X-Instance-Id` HTTP header. Mirrors `OPENCLAW_ID = 20` used by /// `yuanbao-openclaw-plugin` (`src/access/ws/conn-codec.ts`) — the server /// keys some checks off this value, so it must match the value the sign /// endpoint sees when the token is minted. -pub const OPENHUMAN_INSTANCE_ID: &str = "16"; +pub const OPENHUMAN_INSTANCE_ID: &str = "20"; /// Reconnect backoff schedule (seconds). Mirrors hermes-agent. pub const RECONNECT_DELAYS: &[u64] = &[1, 2, 5, 10, 30, 60]; diff --git a/src/openhuman/channels/providers/yuanbao/sign.rs b/src/openhuman/channels/providers/yuanbao/sign.rs index 03791c6b1..0bf5033d0 100644 --- a/src/openhuman/channels/providers/yuanbao/sign.rs +++ b/src/openhuman/channels/providers/yuanbao/sign.rs @@ -211,13 +211,13 @@ impl SignManager { .post(&url) .timeout(Duration::from_secs(HTTP_TIMEOUT_SECS)) .header("Content-Type", "application/json") - .header("X-AppVersion", "openhuman/0.1.0") + .header("X-AppVersion", super::config::DEFAULT_PLUGIN_VERSION) .header("X-OperationSystem", "linux") .header( "X-Instance-Id", super::proto_constants::OPENHUMAN_INSTANCE_ID, ) - .header("X-Bot-Version", "openhuman/0.1.0"); + .header("X-Bot-Version", env!("CARGO_PKG_VERSION")); if !route_env.is_empty() { req = req.header("X-Route-Env", route_env); }