diff --git a/src/chrome/src/agent/adapters.js b/src/chrome/src/agent/adapters.js index c271c50c4..6eaf9421a 100644 --- a/src/chrome/src/agent/adapters.js +++ b/src/chrome/src/agent/adapters.js @@ -15942,6 +15942,21 @@ const ADAPTERS = [ - Login pages (\`wp-login.php\`) have a stable shape: \`#user_login\` (username/email), \`#user_pass\` (password), \`#wp-submit\` (submit). The password field is type=password — when the user provides credentials, do not echo them in any summary.`, }, + { + name: 'wechat-official-account', + category: 'general', + matches: (url) => /^https?:\/\/mp\.weixin\.qq\.com\/(?:$|[?#]|cgi-bin(?:[/?#]|$))/.test(url), + notes: ` +- Confirm the current official account name in the top bar before editing or publishing; an old tab or session-token URL may belong to a different account. +- The article editor has separate title and body editable regions. The body uses ProseMirror/contenteditable; focus each region separately and verify its text after writing so the title is not inserted into the body or vice versa. +- "保存为草稿" (Save as draft) and "发表" (Publish) are different outcomes. A saved draft is not publicly visible and must never be reported as published. +- Under "封面" (Cover), "从正文选择" (Select from article) can reuse an inline image. Confirm the intended image and crop instead of accepting the first thumbnail automatically. +- Before clicking "发表", re-check the account, title, author/byline, summary, and cover because publishing is externally visible. +- Publishing can open stacked confirmation dialogs followed by "微信验证" with a QR code. Pause for the user to complete verification; do not dismiss, bypass, or repeatedly retry the dialog. +- Treat publishing as successful only when "发表记录" shows the new article (or a final public article URL is available), not when the editor closes or a confirmation button was clicked. +- Dashboard links contain short-lived session tokens. If a deep link expires or redirects, navigate from the current dashboard instead of inventing or reusing a tokenized URL.`, + }, + // ─── Commerce ───────────────────────────────────────────────────────── { name: 'amazon', diff --git a/src/firefox/src/agent/adapters.js b/src/firefox/src/agent/adapters.js index 2f58e93a0..000ddf996 100644 --- a/src/firefox/src/agent/adapters.js +++ b/src/firefox/src/agent/adapters.js @@ -15940,6 +15940,21 @@ const ADAPTERS = [ - Login pages (\`wp-login.php\`) have a stable shape: \`#user_login\` (username/email), \`#user_pass\` (password), \`#wp-submit\` (submit). The password field is type=password — when the user provides credentials, do not echo them in any summary.`, }, + { + name: 'wechat-official-account', + category: 'general', + matches: (url) => /^https?:\/\/mp\.weixin\.qq\.com\/(?:$|[?#]|cgi-bin(?:[/?#]|$))/.test(url), + notes: ` +- Confirm the current official account name in the top bar before editing or publishing; an old tab or session-token URL may belong to a different account. +- The article editor has separate title and body editable regions. The body uses ProseMirror/contenteditable; focus each region separately and verify its text after writing so the title is not inserted into the body or vice versa. +- "保存为草稿" (Save as draft) and "发表" (Publish) are different outcomes. A saved draft is not publicly visible and must never be reported as published. +- Under "封面" (Cover), "从正文选择" (Select from article) can reuse an inline image. Confirm the intended image and crop instead of accepting the first thumbnail automatically. +- Before clicking "发表", re-check the account, title, author/byline, summary, and cover because publishing is externally visible. +- Publishing can open stacked confirmation dialogs followed by "微信验证" with a QR code. Pause for the user to complete verification; do not dismiss, bypass, or repeatedly retry the dialog. +- Treat publishing as successful only when "发表记录" shows the new article (or a final public article URL is available), not when the editor closes or a confirmation button was clicked. +- Dashboard links contain short-lived session tokens. If a deep link expires or redirects, navigate from the current dashboard instead of inventing or reusing a tokenized URL.`, + }, + // ─── Commerce ───────────────────────────────────────────────────────── { name: 'amazon', diff --git a/test/run.js b/test/run.js index 6ebdaa2c2..66dd4e7be 100644 --- a/test/run.js +++ b/test/run.js @@ -2330,6 +2330,36 @@ test('matches Xiaohongshu surfaces with mirrored regional guidance', () => { assert.match(chromeAdapter?.notes || '', /Creator Center.*final public note URL/s); }); +test('matches WeChat Official Account admin surfaces with safe publishing guidance', () => { + const urls = [ + 'https://mp.weixin.qq.com/', + 'https://mp.weixin.qq.com/cgi-bin/home?t=home/index&lang=zh_CN&token=123', + 'https://mp.weixin.qq.com/cgi-bin/appmsg?t=media/appmsg_edit_v2&action=edit&type=77&token=123&lang=zh_CN', + ]; + for (const url of urls) { + assert.equal(getActiveAdapter(url)?.name, 'wechat-official-account', `chrome did not match ${url}`); + assert.equal(getActiveAdapterFx(url)?.name, 'wechat-official-account', `firefox did not match ${url}`); + } + assert.notEqual( + getActiveAdapter('https://mp.weixin.qq.com.evil.example/cgi-bin/home')?.name, + 'wechat-official-account', + ); + assert.notEqual( + getActiveAdapter('https://example.com/?next=https://mp.weixin.qq.com/')?.name, + 'wechat-official-account', + ); + + const chromeAdapter = getActiveAdapter(urls[1]); + const firefoxAdapter = getActiveAdapterFx(urls[1]); + assert.equal(firefoxAdapter?.notes, chromeAdapter?.notes); + assert.match(chromeAdapter?.notes || '', /separate title and body.*ProseMirror\/contenteditable/s); + assert.match(chromeAdapter?.notes || '', /保存为草稿.*发表.*not publicly visible/s); + assert.match(chromeAdapter?.notes || '', /从正文选择.*intended image and crop/s); + assert.match(chromeAdapter?.notes || '', /微信验证.*QR code.*Pause for the user/s); + assert.match(chromeAdapter?.notes || '', /发表记录.*public article URL/s); + assert.match(chromeAdapter?.notes || '', /short-lived session tokens.*current dashboard/s); +}); + test('regional social action shims expose Bilibili and Xiaohongshu custom controls', () => { const axChrome = fs.readFileSync(path.join(ROOT, 'src/chrome/src/content/accessibility-tree.js'), 'utf8'); const axFirefox = fs.readFileSync(path.join(ROOT, 'src/firefox/src/content/accessibility-tree.js'), 'utf8');