From b5378500ca26a44fd05c881b29f558e1227b6b40 Mon Sep 17 00:00:00 2001 From: C3H3-AI Date: Tue, 1 Sep 2026 17:42:10 +0800 Subject: [PATCH] fix(feishu): bind card decisions to the actor and reject stale cards Address review feedback on the interaction cards: 1. Bind card decisions to the initiating actor. The card-action operator (operatorOpenId) is now passed into the approval and question handlers: - submitByApprovalId receives { actor } so another allowed group member cannot approve/reject someone else's approval; - the answer branch requires pending.actor === operator. 2. Include the question index in the answer button action and validate it against the current pending question, so a stale card from an earlier question in a multi-question interaction cannot be applied to the next one. 3. When both the card send and the plain-text fallback fail, the error is no longer swallowed: it propagates so the interaction is not marked as presented and the existing retry/reconnect behaviour can run. Adds regression tests for all three cases. --- lib/index.js | 6 +- src/channels/feishu/bridge.mjs | 44 ++++-- src/channels/feishu/feishu-cards.mjs | 4 +- test/channels/feishu/bridge.test.mjs | 211 ++++++++++++++++++++++++++- 4 files changed, 244 insertions(+), 21 deletions(-) diff --git a/lib/index.js b/lib/index.js index 1b0f24f..639fbc9 100644 --- a/lib/index.js +++ b/lib/index.js @@ -351,18 +351,18 @@ ${s.join(` \`${n}\``)},{tag:"div",text:$A(w("**\u72B6\u6001**\uFF1A{reason}",{reason:i}))},{tag:"hr"},Rc(w("\u{1F4CB} \u4F1A\u8BDD\u5217\u8868"),"sessions",w("\u{1F519} \u8FD4\u56DE\u83DC\u5355"),"back_to_menu"),{tag:"div",text:$A(w("\u7ED1\u5B9A\u8BE5\u4F1A\u8BDD\u540E\u53EF\u7EE7\u7EED\u8FFD\u95EE\uFF0C\u8F93\u5165\u6587\u5B57\u5373\u53EF\u3002"))}])}var y6=["\u7EE7\u7EED","\u52A0\u901F\u8FD0\u884C","\u603B\u7ED3\u5F53\u524D\u8FDB\u5C55","\u66F4\u7B80\u6D01\u4E9B","\u66F4\u8BE6\u7EC6\u4E9B"],wu="__steer_custom__";function m6({hasSession:n}){let t=n?w("\u4ECE\u4E0B\u65B9\u4E0B\u62C9\u9009\u62E9\u8865\u5145\u6307\u4EE4\uFF1B\u6700\u540E\u4E00\u9879\u53EF\u81EA\u5B9A\u4E49\u8F93\u5165\u3002"):w("\u5F53\u524D\u6CA1\u6709\u7ED1\u5B9A\u4F1A\u8BDD\uFF0C\u8BF7\u5148\u7ED1\u5B9A\u4F1A\u8BDD\u518D\u8865\u5145\u6307\u4EE4\u3002"),o=y6.map(e=>({text:{tag:"plain_text",content:w(e)},value:w(e)}));o.push({text:{tag:"plain_text",content:w("\u270F\uFE0F \u66F4\u591A / \u81EA\u5B9A\u4E49\u2026")},value:wu});let i=[{tag:"div",text:$A(t)},{tag:"hr"},{tag:"select_static",name:"steer_quick",placeholder:{tag:"plain_text",content:w("\u9009\u62E9\u8865\u5145\u6307\u4EE4")},initial_index:0,options:o,behaviors:[{type:"callback",value:{action:"steer",source:"quick"}}]},{tag:"hr"},bn(w("\u{1F519} \u8FD4\u56DE\u83DC\u5355"),"back_to_menu")];return Ki(w("\u2795 \u8865\u5145\u6307\u4EE4"),i)}function IP(){let n=[{tag:"div",text:$A(w("\u8F93\u5165\u8865\u5145\u6307\u4EE4\u540E\u70B9\u300C\u63D0\u4EA4\u300D\uFF0C\u53D1\u9001\u7ED9\u5F53\u524D\u8FD0\u884C\u7684\u4EFB\u52A1\u3002"))},{tag:"hr"},{tag:"form",name:"steer_form",elements:[{tag:"input",name:"steer_text",placeholder:{tag:"plain_text",content:w("\u8F93\u5165\u4F60\u7684\u8865\u5145\u6307\u4EE4")}},{tag:"button",name:"steer_submit",text:{tag:"plain_text",content:w("\u63D0\u4EA4")},type:"primary",width:"fill",form_action_type:"submit",behaviors:[{type:"callback",value:{action:"steer",source:"form"}}]}]},{tag:"hr"},bn(w("\u{1F519} \u8FD4\u56DE\u83DC\u5355"),"back_to_menu")];return Ki(w("\u2795 \u81EA\u5B9A\u4E49\u6307\u4EE4"),n)}function S6({toolName:n,operation:t,reason:o,approvalId:i}){let e=[];if(n&&e.push({tag:"div",text:$A(w("\u5DE5\u5177\uFF1A{tool}",{tool:String(n)}))}),t){let A=String(t),r=A.length>6e3?`${A.slice(0,6e3)} \u2026\uFF08\u64CD\u4F5C\u53C2\u6570\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD\uFF09`:A;e.push({tag:"div",text:$A(w(`\u64CD\u4F5C\u53C2\u6570\uFF1A {operation}`,{operation:r}))})}return o&&e.push({tag:"div",text:$A(w("\u539F\u56E0\uFF1A{reason}",{reason:String(o)}))}),e.push({tag:"hr"},Rc(w("\u2705 \u6279\u51C6"),`approve:${i}`,w("\u274C \u62D2\u7EDD"),`reject:${i}`)),Ki(w("\u{1F510} \u5DE5\u5177\u5BA1\u6279"),e)}function w6({interactionId:n,header:t,question:o,detail:i,options:e,index:a,total:A}){let r=[],s=A>1?`\uFF08${a+1}/${A}\uFF09`:"";t&&r.push({tag:"div",text:$A(String(t))});let g=typeof o=="string"&&o.trim()?o:w("\u8BF7\u8F93\u5165\u4F60\u7684\u56DE\u7B54\u3002");if(r.push({tag:"div",text:$A(String(g))}),i&&r.push({tag:"div",text:$A(String(i))}),Array.isArray(e)&&e.length>0){r.push({tag:"hr"});for(let c of e){let I=typeof c?.label=="string"?c.label:"";if(!I)continue;let d=typeof c?.description=="string"&&c.description.trim()?c.description.trim():"",B=d?`${I} -${d}`:I;r.push(bn(B,`answer:${n}:${I}`))}}return Ki(w("\u2753 \u8BF7\u8865\u5145\u4FE1\u606F{progress}",{progress:s}),r)}import{mkdir as qRe,readFile as zRe,rename as VRe,writeFile as WRe}from"node:fs/promises";import{dirname as ZRe}from"node:path";var XRe=Object.freeze({version:1,sessions:{},seenMessageIds:[],watches:{},includeArchivedSessions:!1}),nS=20;function lP(n){return n&&typeof n=="object"&&typeof n.sessionId=="string"&&n.sessionId.length>0&&typeof n.chatId=="string"&&n.chatId.length>0}var oS=class{#e;#A=structuredClone(XRe);#t=Promise.resolve();constructor(t){this.#e=t}async load(){try{let t=JSON.parse(await zRe(this.#e,"utf8"));this.#A={version:1,sessions:t.sessions&&typeof t.sessions=="object"?t.sessions:{},seenMessageIds:Array.isArray(t.seenMessageIds)?t.seenMessageIds.slice(-1e3):[],watches:t.watches&&typeof t.watches=="object"?t.watches:{},includeArchivedSessions:typeof t.includeArchivedSessions=="boolean"?t.includeArchivedSessions:!1}}catch(t){if(t?.code!=="ENOENT")throw t;await this.#r()}return this}sessionFor(t){return this.#A.sessions[t]??null}async setSession(t,o){this.#A.sessions[t]=o,await this.#r()}async clearSession(t){delete this.#A.sessions[t],await this.#r()}async clearSessions(){this.#A.sessions={},await this.#r()}hasSeen(t){return this.#A.seenMessageIds.includes(t)}async markSeen(t){this.hasSeen(t)||(this.#A.seenMessageIds.push(t),this.#A.seenMessageIds.length>1e3&&this.#A.seenMessageIds.splice(0,this.#A.seenMessageIds.length-1e3),await this.#r())}snapshot(){return structuredClone(this.#A)}watchEntries(t){let o=this.#A.watches[t];return Array.isArray(o)?o.filter(lP):[]}watchEntry(t,o){return this.watchEntries(t).find(i=>i.sessionId===o)??null}async setWatch(t,o){let i=this.#A.watches[t]??[],e=i.findIndex(a=>a.sessionId===o.sessionId);e===-1?(i.length>=nS&&i.shift(),i.push(o)):i[e]=o,this.#A.watches[t]=i,await this.#r()}async removeWatch(t,o){let i=this.#A.watches[t]??[];this.#A.watches[t]=i.filter(e=>e.sessionId!==o),await this.#r()}async clearWatches(t){delete this.#A.watches[t],await this.#r()}keysWatching(t){return Object.entries(this.#A.watches).filter(([,o])=>Array.isArray(o)&&o.some(i=>lP(i)&&i.sessionId===t)).map(([o])=>o)}watchedSessionIds(){let t=new Set;for(let o of Object.values(this.#A.watches))if(Array.isArray(o))for(let i of o)lP(i)&&t.add(i.sessionId);return[...t]}includesArchivedSessions(){return this.#A.includeArchivedSessions===!0}async setIncludeArchivedSessions(t){this.#A.includeArchivedSessions=t===!0,await this.#r()}async#r(){let t=JSON.stringify(this.#A,null,2)+` +${d}`:I;r.push(bn(B,`answer:${n}:${a}:${I}`))}}return Ki(w("\u2753 \u8BF7\u8865\u5145\u4FE1\u606F{progress}",{progress:s}),r)}import{mkdir as qRe,readFile as zRe,rename as VRe,writeFile as WRe}from"node:fs/promises";import{dirname as ZRe}from"node:path";var XRe=Object.freeze({version:1,sessions:{},seenMessageIds:[],watches:{},includeArchivedSessions:!1}),nS=20;function lP(n){return n&&typeof n=="object"&&typeof n.sessionId=="string"&&n.sessionId.length>0&&typeof n.chatId=="string"&&n.chatId.length>0}var oS=class{#e;#A=structuredClone(XRe);#t=Promise.resolve();constructor(t){this.#e=t}async load(){try{let t=JSON.parse(await zRe(this.#e,"utf8"));this.#A={version:1,sessions:t.sessions&&typeof t.sessions=="object"?t.sessions:{},seenMessageIds:Array.isArray(t.seenMessageIds)?t.seenMessageIds.slice(-1e3):[],watches:t.watches&&typeof t.watches=="object"?t.watches:{},includeArchivedSessions:typeof t.includeArchivedSessions=="boolean"?t.includeArchivedSessions:!1}}catch(t){if(t?.code!=="ENOENT")throw t;await this.#r()}return this}sessionFor(t){return this.#A.sessions[t]??null}async setSession(t,o){this.#A.sessions[t]=o,await this.#r()}async clearSession(t){delete this.#A.sessions[t],await this.#r()}async clearSessions(){this.#A.sessions={},await this.#r()}hasSeen(t){return this.#A.seenMessageIds.includes(t)}async markSeen(t){this.hasSeen(t)||(this.#A.seenMessageIds.push(t),this.#A.seenMessageIds.length>1e3&&this.#A.seenMessageIds.splice(0,this.#A.seenMessageIds.length-1e3),await this.#r())}snapshot(){return structuredClone(this.#A)}watchEntries(t){let o=this.#A.watches[t];return Array.isArray(o)?o.filter(lP):[]}watchEntry(t,o){return this.watchEntries(t).find(i=>i.sessionId===o)??null}async setWatch(t,o){let i=this.#A.watches[t]??[],e=i.findIndex(a=>a.sessionId===o.sessionId);e===-1?(i.length>=nS&&i.shift(),i.push(o)):i[e]=o,this.#A.watches[t]=i,await this.#r()}async removeWatch(t,o){let i=this.#A.watches[t]??[];this.#A.watches[t]=i.filter(e=>e.sessionId!==o),await this.#r()}async clearWatches(t){delete this.#A.watches[t],await this.#r()}keysWatching(t){return Object.entries(this.#A.watches).filter(([,o])=>Array.isArray(o)&&o.some(i=>lP(i)&&i.sessionId===t)).map(([o])=>o)}watchedSessionIds(){let t=new Set;for(let o of Object.values(this.#A.watches))if(Array.isArray(o))for(let i of o)lP(i)&&t.add(i.sessionId);return[...t]}includesArchivedSessions(){return this.#A.includeArchivedSessions===!0}async setIncludeArchivedSessions(t){this.#A.includeArchivedSessions=t===!0,await this.#r()}async#r(){let t=JSON.stringify(this.#A,null,2)+` `;this.#t=this.#t.then(async()=>{await qRe(ZRe(this.#e),{recursive:!0,mode:448});let o=`${this.#e}.tmp`;await WRe(o,t,{encoding:"utf8",mode:384}),await VRe(o,this.#e)}),await this.#t}};var kc=Object.freeze({MENTION:"mention",ALL:"all"});function lo(n){return n===kc.ALL?kc.ALL:kc.MENTION}function sS(n){return n===kc.MENTION||n===kc.ALL}var Du=()=>w("\u8FD9\u4E2A\u95EE\u9898\u5DF2\u5728\u5176\u4ED6\u5BA2\u6237\u7AEF\u5904\u7406\uFF0C\u65E0\u9700\u518D\u6B21\u56DE\u7B54\u3002"),eke=30*6e4,T6=/^\/m(?:enu)?$/i,N6=/^\/repair(?:\s|$)/i,Ake=/^\/repair(?:\s+(qr|status|cancel|verify))?\s*$/i,dP=/^\/watch(?:\s+([^\s]+))?$/i,hP=/^\/unwatch(?:\s+([^\s]+))?$/i,R6=/^\/watchlist$/i,tke=/^\/(?:sessionlist|sessions)(?:\s|$)/i,rke=/^\/workspacelist$/i,D6=/^\d{1,2}$/,ake=10*6e4,ike=50,oke=8,nke=8,ske=10*6e4,O6=400,gke=10*6e4,cke=100,Ike=50,lke=500,pke=5e3,dke=5e3,hke=15e3,Eke=1e3,k6=new Set(["starting","qr_ready","polling","slow_down","domain_switched"]),pP=new Set([...k6,"saving"]),gS=new Set(["succeeded","expired","cancelled","error"]),Cke=new Set(["accounts.feishu.cn","open.feishu.cn","accounts.larksuite.com","open.larksuite.com"]),EP=/^\/archived(?:\s+(on|off))?$/i,uke=/^\/(?:m(?:enu)?|new|help|status|compact|(?:sessionlist|sessions)(?:\s|$)|workspacelist|watchlist|archived(?:\s+(on|off))?)$/i;function Bke(n){let t=n?.arguments;if(t!==null&&typeof t=="object")try{return JSON.stringify(t,null,2)}catch{return null}if(typeof t!="string")return null;let o=fke(t);if(!o)return t===""?"{}":null;try{return JSON.stringify(JSON.parse(o),null,2)}catch{return o}}function fke(n){return String(n??"").replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g,"")}function Qke(n,{hasImages:t=!1,hasFiles:o=!1}={}){if(t||o||typeof n!="string")return!1;let i=n.trim();return T6.test(i)||N6.test(i)||dP.test(i)||hP.test(i)||R6.test(i)||EP.test(i)}var yke=["/workspace \u5DE5\u4F5C\u533A\u5E8F\u53F7\u6216\u7EDD\u5BF9\u8DEF\u5F84 \u5207\u6362\u5DE5\u4F5C\u533A","/session Session ID \u6216\u5F53\u524D\u5DE5\u4F5C\u533A\u5E8F\u53F7 \u5C06\u5F53\u524D\u804A\u5929\u7ED1\u5B9A\u5230\u6307\u5B9A\u4F1A\u8BDD","/workspacelist \u5217\u51FA\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84","/sessionlist \u6216 /sessions [\u5DE5\u4F5C\u533A\u5E8F\u53F7\u6216\u7EDD\u5BF9\u8DEF\u5F84] \u5217\u51FA\u4F1A\u8BDD ID \u548C\u6807\u9898"];function Ou(n){switch(n?.code){case"workspace-not-absolute":return w("\u5DE5\u4F5C\u533A\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\u3002");case"workspace-not-found":return w("\u5DE5\u4F5C\u533A\u8DEF\u5F84\u4E0D\u5B58\u5728\u3002");case"workspace-not-directory":return w("\u5DE5\u4F5C\u533A\u8DEF\u5F84\u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u76EE\u5F55\u3002");case"workspace-bot-not-found":return w("\u673A\u5668\u4EBA\u6B63\u5728\u79FB\u9664\u6216\u5DF2\u91CD\u65B0\u63A5\u5165\uFF0C\u65E0\u6CD5\u64CD\u4F5C\u539F\u4F1A\u8BDD\u7684\u5DE5\u4F5C\u533A\u3002");default:return w("\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002")}}function mke(n,t){let o=String(n??w("\u7ED3\u679C\u6587\u4EF6")).replace(/[\r\n]+/g," ").trim()||w("\u7ED3\u679C\u6587\u4EF6");switch(t?.code){case"artifact-permission-required":return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46\u673A\u5668\u4EBA\u7F3A\u5C11\u98DE\u4E66\u6587\u4EF6\u4E0A\u4F20\u6743\u9650 im:resource\u3002\u8BF7\u79C1\u804A\u673A\u5668\u4EBA\u6267\u884C /repair \u547D\u4EE4\uFF0C\u6216\u8005\u5728\u300CIM\u673A\u5668\u4EBA\u300D\u8BBE\u7F6E\u9875\u70B9\u51FB\u201C\u8865\u5168\u6743\u9650\u201D\u6309\u94AE\u5E76\u626B\u7801\u3002\u5B8C\u6210\u98DE\u4E66\u8981\u6C42\u7684\u53D1\u5E03\u5BA1\u6279\u540E\u91CD\u8BD5\u3002",{name:o});case"artifact-too-large":return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u8D85\u8FC7\u98DE\u4E66 30 MB \u4E0A\u9650\uFF0C\u672A\u53D1\u9001\u3002",{name:o});case"artifact-empty":return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u4E3A\u7A7A\uFF0C\u98DE\u4E66\u4E0D\u5141\u8BB8\u53D1\u9001\u7A7A\u6587\u4EF6\u3002",{name:o});case"artifact-changed":case"artifact-invalid":case"artifact-unavailable":return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\u6216\u51C6\u5907\u53D1\u9001\uFF0C\u8BF7\u786E\u8BA4\u6587\u4EF6\u4ECD\u53EF\u8BBF\u95EE\u540E\u91CD\u8BD5\u3002",{name:o});case"artifact-rate-limited":return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u6682\u65F6\u88AB\u98DE\u4E66\u9650\u6D41\uFF0C\u672A\u80FD\u53D1\u9001\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",{name:o});case"artifact-delivery-uncertain":return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u53D1\u9001\u7ED3\u679C\u672A\u80FD\u786E\u8BA4\uFF0C\u8BF7\u5148\u68C0\u67E5\u804A\u5929\u5185\u662F\u5426\u5DF2\u6536\u5230\uFF0C\u4E0D\u8981\u7ACB\u5373\u91CD\u8BD5\u3002",{name:o});default:return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46\u6682\u65F6\u672A\u80FD\u53D1\u9001\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",{name:o})}}function cS(n,t){return typeof n=="string"&&n.trim()?n:t.length>0?w("\u7ED3\u679C\u6587\u4EF6\u5DF2\u751F\u6210\u3002"):n}function lA(n){return typeof n=="string"&&n.trim()?n.trim():null}function b6(n){if(n&&typeof n=="object"&&!Array.isArray(n))return n;if(typeof n!="string"||!n.trim())return{};try{let t=JSON.parse(n);return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch{return{}}}function Mu(n){if(n==null)return null;if(Array.isArray(n)){for(let o of n){let i=Mu(o);if(i!==null)return i}return null}if(n&&typeof n=="object")return"value"in n?Mu(n.value):"option"in n?Mu(n.option):null;if(typeof n!="string")return null;let t=n.trim();if(!t)return null;if(t.startsWith("[")||t.startsWith("{")||t.startsWith('"'))try{let o=JSON.parse(t);if(o!==n)return Mu(o)}catch{}return t}function _g(n){if(n==null)return[];if(Array.isArray(n))return n.flatMap(o=>_g(o));if(n&&typeof n=="object")return"value"in n?_g(n.value):"option"in n?_g(n.option):[];if(typeof n!="string")return[];let t=n.trim();if(!t)return[];if(t.startsWith("[")||t.startsWith("{")||t.startsWith('"'))try{let o=JSON.parse(t);if(o!==n)return _g(o)}catch{}return t.split(",").map(o=>o.trim()).filter(Boolean)}function Mn(n){return Number.isSafeInteger(n)&&n>=-1}function _u(n){return Number.isSafeInteger(n)&&n>=0}function lS(n){return Number.isSafeInteger(n?.watchStartedAt)&&n.watchStartedAt>=0?n.watchStartedAt:null}function IS(n){return n&&(lS(n)!==null||!Mn(n.lastSeq))}function Ske(n){return(Array.isArray(n?.events)?n.events:[]).map(t=>t?.event??t).filter(t=>t&&typeof t=="object"&&_u(t.seq)).sort((t,o)=>t.seq-o.seq)}function xd(n){return lA(n?.sender?.sender_id?.open_id)??lA(n?.sender?.sender_id?.user_id)}function wke(n){return lA(n?.sender?.sender_id?.open_id)}function M6(n,t){return new Promise((o,i)=>{if(t?.aborted){i(t.reason??new DOMException("Aborted","AbortError"));return}let e=setTimeout(a,n);e.unref?.();function a(){t?.removeEventListener("abort",A),o()}function A(){clearTimeout(e),i(t.reason??new DOMException("Aborted","AbortError"))}t?.addEventListener("abort",A,{once:!0})})}function bu(n,{botId:t}={}){let o=n&&typeof n=="object"?n:{},i=o.registration&&typeof o.registration=="object"?o.registration:o,e=lA(i.operation)??lA(o.operation);if(e&&e!=="callback_repair")throw new Error("The active Feishu operation is not a callback repair");let a=lA(i.botId)??lA(o.botId);if(t&&a&&a!==t)throw new Error("The Feishu repair belongs to another bot");let A=lA(i.state),r=i.attemptId??i.attempt,s=typeof r=="string"||Number.isFinite(r)?String(r):null;if(!A||!s)throw new Error("Feishu returned an invalid repair status");let g=lA(i.verificationUrl)??lA(i.qrCodeUrl),c=Number(i.expiresAt),I=Number(i.remainingSeconds),d=Number(i.pollIntervalMs)||Number(i.pollIntervalSeconds)*1e3;return{state:A,attemptId:s,botId:a,verificationUrl:g,expiresAt:Number.isFinite(c)?c:null,remainingSeconds:Number.isFinite(I)?I:null,pollIntervalMs:Number.isFinite(d)&&d>0?d:null,error:i.error&&typeof i.error=="object"?{code:lA(i.error.code),message:lA(i.error.message)}:null}}function _6(n,t){let o=new URL(n);if(o.protocol!=="https:"||!Cke.has(o.hostname))throw new Error("Feishu returned an untrusted repair URL");if(o.searchParams.get("tp")!=="sdk"||o.searchParams.get("clientID")!==t||o.searchParams.has("createOnly"))throw new Error("Feishu returned an invalid existing-app repair URL");if(o.toString().includes("{{client_id}}")||o.toString().includes("%7B%7Bclient_id%7D%7D"))throw new Error("Feishu returned an unresolved client id placeholder");return o.toString()}function Dke(n,t){return t.needsPresentation!==!0&&t.questions[t.index]&&xd(n)===t.actor&&n?.message?.message_type==="text"&&lA(Hd(n))}function Oke(n){for(let t of["messagesReceived","messagesReplied","messagesRejected"])n[t]??=0;n.lastMessageAt??=null,n.lastReplyAt??=null,n.lastRejectedAt??=null,n.lastError??=null,n.lastMessageError??=null}var pS=class{#e;#A;#t;#r;#l;#o;#i=new Map;#a=new Ti;#n=new Map;#s=new Map;#p=new Map;#c=new Map;#g=new Set;#I=new Set;#d=new Set;#h=new Map;#C=new Map;#u=new Map;#B=new Map;#f=new Map;#Q=new Map;#S=new Map;#y=new Map;#m=new Map;#D;#E;#N;#T;#O;#w;#R;#_;#G;#k;#F;#P=null;#H=0;#U;#L;#v=new Map;#K=new Map;#q=null;#V=new Map;#z=new Map;#M=new Map;#Y=new Map;#ee;#Ae=!0;constructor({client:t,channel:o,harness:i,state:e,contextEnhancement:a,accessPolicy:A,status:r,allowedSenderOpenIds:s=new Set,botId:g,appId:c,botOpenId:I,groupResponseMode:d=kc.ALL,repair:B,repairPollIntervalMs:f=Eke,repairLinkWaitMs:Q=hke,cardDataTimeoutMs:y=dke,replyTimeoutMs:m=6e5,interactionCards:_=!0,logger:T=console,signal:R}){if(!t||!i||!e||!r)throw new TypeError("Feishu bridge dependencies are required");if(B!=null){if(!B||typeof B.start!="function"||typeof B.status!="function"||typeof B.cancel!="function")throw new TypeError("Feishu repair capability requires start/status/cancel");if(!lA(g)||!lA(c))throw new TypeError("Feishu repair capability requires botId and appId")}if(!Number.isFinite(f)||f<=0||!Number.isFinite(Q)||Q<=0||!Number.isFinite(y)||y<=0)throw new TypeError("Feishu timing values must be positive numbers");this.#e=t,this.#A=o,this.#t=i,this.#r=e,this.#l=a,this.#o=A,this.#E=r,this.#N=s,this.#R=lA(g),this.#_=lA(c),this.#G=lA(I),this.#k=lo(d),this.#F=B??null,this.#U=f,this.#L=Q,this.#ee=y,this.#T=m,this.#Ae=_===!0,this.#O=T,this.#D=new Di({label:"Feishu",logger:T}),this.#w=R,Oke(this.#E),typeof this.#t?.watchHarnessEvents=="function"&&queueMicrotask(()=>this.#ve())}setGroupResponseMode(t){this.#k=lo(t)}#W(t){if(t?.message?.chat_type==="p2p")return!0;let o=Array.isArray(t?.message?.mentions)?t.message.mentions:[];return this.#G?o.some(i=>i?.id?.open_id===this.#G||i?.open_id===this.#G):o.length>0}accept(t){if(this.#w?.aborted)return Promise.resolve();let o=lA(t?.message?.message_id);if(!o||g6(t))return Promise.resolve();if(!this.#o&&!c6(t,this.#N))return this.#E.messagesRejected+=1,this.#E.lastRejectedAt=new Date().toISOString(),this.#O.warn?.("[dsh-feishu] ignored a message from a sender outside the legacy allowlist"),Promise.resolve();let i=this.#W(t);if(t?.message?.chat_type!=="p2p"&&this.#k===kc.MENTION&&!i||this.#r.hasSeen(o)||this.#c.has(o))return Promise.resolve();let e;try{e=n6(t)}catch{return this.#E.messagesRejected+=1,this.#E.lastRejectedAt=new Date().toISOString(),Promise.resolve()}let a=cP(t,this.#e),A=lA(a.content)??"",r=Tt(a),s=wt(a),g=t.message.chat_type==="p2p"?"direct":t.message.chat_type==="group"?"group":null,c=Qa(this.#o,{conversationType:g,senderIds:xd(t),text:A,hasImages:r,hasFiles:s,isCommand:_Q(A,{hasImages:r,hasFiles:s})||Qke(A,{hasImages:r,hasFiles:s})||!r&&!s&&D6.test(A)&&this.#v.has(e)});if(!c.allowed)return this.#c.set(o,null),this.#Z(t,o,c);if(t.message.chat_type==="p2p"){let _=lA(t.message.chat_id);_&&wo(this.#r,{chatId:_})}this.#c.set(o,Aa(this.#l,g));let I=this.#QA(o),d=t.message.message_type==="text"?lA(Hd(t))??"":"",B=t.message.message_type==="text"&&Ni(d),f=this.#n.get(e),Q=this.#a.status(e);if(B&&t.message.chat_type!=="p2p")return this.#te(t,o,I,{message:As()});if(t.message.chat_type==="p2p"&&(B||Q.phase==="collecting")){let T=/^\/batch$/iu.test(d)&&Q.phase==="idle"&&(this.#i.has(e)||f||this.#D.hasPending(e))?{handled:!0,kind:"busy",message:Ro()}:this.#a.handle(e,d,{plainText:t.message.message_type==="text"&&!!d});if(T.handled){if(T.kind==="submit"){let R={...t,batchSubmission:{token:T.token},message:{...t.message,message_type:"text",content:JSON.stringify({text:T.prompt}),mentions:[]}};return this.#x(R,o,e,I)}return this.#te(t,o,I,T)}}if(uke.test(A))return Promise.resolve().then(()=>this.#Me(t,e,{alreadyRecorded:!1})).then(()=>this.#re(o,I,"DONE")).catch(T=>this.#ie(t,o,I,T)).finally(()=>this.#c.delete(o));let y=Fa(A)?to:wt(a)?null:si(A)?Mi:gi(A)?ro:ci(A)?_i:null;if(y&&(i||y===to)){let _=this.#xe(t,o,e,a,y),T;return T=_.then(()=>this.#re(o,I,"DONE")).catch(R=>this.#ie(t,o,I,R)).finally(()=>{this.#c.delete(o),this.#I.delete(T)}),this.#I.add(T),T}if(this.#we(t,e))return Promise.resolve().then(()=>this.#De(t,o)).then(()=>this.#re(o,I,"DONE")).catch(T=>this.#ie(t,o,I,T)).finally(()=>this.#c.delete(o));let m=this.#D.claimReply({key:e,actor:xd(t),messageId:o,text:Hd(t)??"",addressed:i,hasPendingQuestion:!!f,questionCompletion:f?.submitting||f?.claimedReplyMessageId?f.queue:null,isQuestionPending:()=>this.#n.has(e),send:_=>this.#b(t.message.chat_id,_,{replyTo:t.message.message_id})});if(m){let _=m.process(async()=>this.#r.hasSeen(o)?!1:(await this.#r.markSeen(o),this.#E.lastMessageAt=new Date().toISOString(),this.#E.messagesReceived+=1,!0)),T;return T=_.then(()=>this.#re(o,I,"DONE")).catch(R=>this.#ie(t,o,I,R)).finally(()=>{this.#c.delete(o),this.#g.delete(T)}),this.#g.add(T),T}if(f&&xd(t)!==f.actor)return this.#x(t,o,e,I);if(f?.submitting||f?.claimedReplyMessageId)return this.#x(t,o,e,I);if(f){Dke(t,f)&&(f.claimedReplyMessageId=o);let T=(f.queue??Promise.resolve()).catch(()=>{}).then(()=>this.#CA(t,o,e,f,I));f.queue=T;let R=()=>{f.claimedReplyMessageId===o&&(f.claimedReplyMessageId=null),f.queue===T&&(f.queue=null)},K;return K=T.then(()=>(R(),this.#re(o,I,"DONE")),L=>(R(),this.#ie(t,o,I,L))).finally(()=>{R(),this.#c.delete(o),this.#g.delete(K)}),this.#g.add(K),K}return this.#x(t,o,e,I)}#te(t,o,i,e){let a;return a=Promise.resolve().then(async()=>{this.#r.hasSeen(o)||(await this.#r.markSeen(o),this.#E.lastMessageAt=new Date().toISOString(),this.#E.messagesReceived+=1,e?.message&&await this.#b(t.message.chat_id,e.message,{replyTo:t.message.message_id}),this.#E.lastError=null)}).then(()=>this.#re(o,i,"DONE")).catch(A=>this.#ie(t,o,i,A)).finally(()=>{this.#c.delete(o),this.#I.delete(a)}),this.#I.add(a),a}#Z(t,o,i){let e;return e=Promise.resolve().then(async()=>{this.#r.hasSeen(o)||(await this.#r.markSeen(o),i.reason==="command-not-allowed"?(this.#E.lastMessageAt=new Date().toISOString(),this.#E.messagesReceived+=1,await this.#b(t.message.chat_id,w(io),{replyTo:t.message.message_id}),this.#E.messagesReplied+=1,this.#E.lastReplyAt=new Date().toISOString()):(this.#E.messagesRejected+=1,this.#E.lastRejectedAt=new Date().toISOString()),this.#E.lastError=null)}).catch(a=>{this.#w?.aborted||(this.#E.lastError=a?.message??String(a),this.#O.warn?.("[dsh-feishu] failed to apply inbound access policy"))}).finally(()=>{this.#c.delete(o),this.#I.delete(e)}),this.#I.add(e),e}#x(t,o,i,e,{releaseMessageId:a=!0,alreadyRecorded:A=!1,finalize:r=!0}={}){let g=(this.#i.get(i)??Promise.resolve()).catch(()=>{}).then(()=>this.#Me(t,i,{alreadyRecorded:A})),c=r?g.then(async d=>(await this.#re(o,e,"DONE"),d)).catch(d=>this.#ie(t,o,e,d)):g,I;return I=c.finally(()=>{a&&this.#c.delete(o),this.#i.get(i)===I&&this.#i.delete(i)}),this.#i.set(i,I),I}#ae(t,{logLabel:o="operation",logLevel:i="warn",userMessage:e,reason:a}={}){this.#E.lastError=t?.message??String(t);let A=tt(this.#E,t,{userMessage:e,reason:a});return this.#O?.[i]?.(`[dsh-feishu] ${o} failed [${A.referenceId}]:`,t?.message??String(t)),A}async#j(t,o,i={}){let e=this.#ae(o,i),a=i.appendMessage?`${ht(e)} ${i.appendMessage}`:ht(e);return await this.#b(t,a,{replyTo:i.replyTo}).catch(()=>{}),e}async#ie(t,o,i,e){if(e?.code==="turn-stopped"){await this.#Ke(o,i),e?.batchInputMessage&&await this.#b(t.message.chat_id,e.batchInputMessage,{replyTo:t.message.message_id}).catch(()=>{});return}if(this.#w?.aborted){await this.#Ke(o,i);return}let a=vo(e)??Mo(e),A=this.#ae(e,{logLabel:"message handling",logLevel:"error",userMessage:a,reason:Ao(e)?.reason});await this.#re(o,i,"ERROR");let r=e?.batchInputMessage?`${ht(A)} ${e.batchInputMessage}`:ht(A);await this.#b(t.message.chat_id,r,{replyTo:t.message.message_id}).catch(()=>{})}async waitForIdle(){for(;;){let t=[...this.#i.values(),...[...this.#n.values()].flatMap(o=>o.queue?[o.queue]:[]),...this.#g,...this.#I,...this.#d,...this.#V.values(),...[...this.#z.values()].map(o=>o.promise)];if(t.length===0||(await Promise.allSettled(t),this.#i.size===0&&this.#g.size===0&&this.#I.size===0&&this.#d.size===0&&this.#V.size===0&&this.#z.size===0&&![...this.#n.values()].some(o=>o.queue)))return}}#$(){let t=AbortSignal.timeout(this.#ee);return this.#w?AbortSignal.any([this.#w,t]):t}#ne(t){return this.#n.has(t)||this.#D.hasPending(t)}async#xe(t,o,i,e,a){if(this.#w?.throwIfAborted(),this.#r.hasSeen(o))return;await this.#r.markSeen(o),this.#E.lastMessageAt=new Date().toISOString(),this.#E.messagesReceived+=1;let A=await a(lA(e.content)??"",this.#t,this.#r,i,{signal:this.#w,isDirect:t.message.chat_type==="p2p",hasImages:Tt(e),hasFiles:wt(e),pendingInteraction:this.#ne(i),control:{owner:this,key:i}});A?.stopped&&await Promise.allSettled([this.#be(i),this.#D.closeRoute(i)]);for(let r of A?.messages??[A?.message])r&&await this.#b(t.message.chat_id,r,{replyTo:t.message.message_id});this.#E.lastError=null}async#Me(t,o,{alreadyRecorded:i=!1}={}){this.#w?.throwIfAborted();let e=t.message.message_id;if(!i){if(this.#r.hasSeen(e))return;await this.#r.markSeen(e),this.#E.lastMessageAt=new Date().toISOString(),this.#E.messagesReceived+=1}let a=cP(t,this.#e),A=a.content,r=Tt(a),s=wt(a),g=!r&&!s&&A?A.trim():null;if(!A&&!r&&!s){await this.#b(t.message.chat_id,w("\u76EE\u524D\u652F\u6301\u6587\u5B57\u3001\u56FE\u7247\u548C\u6587\u4EF6\u6D88\u606F\u3002"),{replyTo:t.message.message_id});return}if(g!==null&&N6.test(g)){await this.#_e(t,g);return}if(g==="/help"){await this.#b(t.message.chat_id,h6(),{replyTo:t.message.message_id});return}if(T6.test(g)){await this.#X(o,t.message.chat_id,{replyTo:t.message.message_id});return}if(g==="/new"){if(this.#i.has(o)||this.#ne(o)){await this.#b(t.message.chat_id,w("\u5F53\u524D\u4EFB\u52A1\u4ECD\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u505C\u6B62\u4EFB\u52A1\u6216\u7B49\u5F85\u4EFB\u52A1\u5B8C\u6210\u540E\u518D\u5F00\u542F\u65B0\u4F1A\u8BDD\u3002"),{replyTo:t.message.message_id});return}await this.#r.clearSession(o),await this.#b(t.message.chat_id,w("\u5DF2\u5F00\u542F\u5168\u65B0 Harness \u4F1A\u8BDD\u3002"),{replyTo:t.message.message_id}),await this.#X(o,t.message.chat_id,{replyTo:t.message.message_id});return}if(g==="/status"){await this.#rA(o,t.message.chat_id,t.message.message_id);return}if(g==="/compact"){let f=await bi(g,this.#t,this.#r,o,{signal:this.#w});f&&await this.#b(t.message.chat_id,f.message,{replyTo:t.message.message_id});return}if(tke.test(g)){let f=g.replace(/^\/(?:sessionlist|sessions)/i,"").trim()||null;await this.#pe({chatId:t.message.chat_id,key:o,replyTo:t.message.message_id},f,0);return}if(rke.test(g)){await this.#Re({chatId:t.message.chat_id,key:o,replyTo:t.message.message_id});return}if(dP.test(g)){let f=(dP.exec(g)?.[1]??"").trim()||null;await this.#fe(o,t.message.chat_id,f,{replyTo:t.message.message_id});return}if(hP.test(g)){let f=(hP.exec(g)?.[1]??"").trim()||null;await this.#Qe(o,t.message.chat_id,f,{replyTo:t.message.message_id});return}if(R6.test(g)){await this.#ye(o,t.message.chat_id,{replyTo:t.message.message_id});return}if(EP.test(g)){let Q=EP.exec(g)[1]?.toLowerCase();if(Q!=="on"&&Q!=="off"){await this.#b(t.message.chat_id,w("\u7528\u6CD5\uFF1A/archived on\uFF08\u5305\u542B\u5F52\u6863\u4F1A\u8BDD\uFF09\u6216 /archived off\uFF08\u9690\u85CF\u5F52\u6863\u4F1A\u8BDD\uFF09"),{replyTo:t.message.message_id});return}typeof this.#r?.setIncludeArchivedSessions=="function"&&await this.#r.setIncludeArchivedSessions(Q==="on"),await this.#b(t.message.chat_id,Q==="on"?w("\u5DF2\u5F00\u542F\uFF1A\u4F1A\u8BDD\u5217\u8868\u5305\u542B\u5F52\u6863\u4F1A\u8BDD\u3002"):w("\u5DF2\u5173\u95ED\uFF1A\u4F1A\u8BDD\u5217\u8868\u9690\u85CF\u5F52\u6863\u4F1A\u8BDD\u3002"),{replyTo:t.message.message_id});return}if(D6.test(g)){let f=this.#Ze(o);if(f){await this.#Xe(f,Number(g),{chatId:t.message.chat_id,key:o,event:t});return}}let c=g===null?null:await _o(A,this.#t,o);if(c){for(let f of c.messages??[c.message])await this.#b(t.message.chat_id,f,{replyTo:t.message.message_id});return}let I=g===null?null:await bi(g,this.#t,this.#r,o,{signal:this.#w});if(I){await this.#b(t.message.chat_id,I.message,{replyTo:t.message.message_id});return}this.#O.info?.(`[dsh-feishu] processing ${t.message.chat_type} message ${e}`);let d=t.batchSubmission??null,B=!1;try{let{receipt:f,artifactSendErrors:Q,textDeliveryErrors:y=0}=await this.#EA(t,o,a,{onAskComplete:d?()=>{B=this.#a.complete(o,d.token).completed}:void 0});return this.#E.messagesReplied+=1,this.#E.lastReplyAt=new Date().toISOString(),this.#E.lastError=null,y===0&&Q===0&&Do(this.#E),f}catch(f){if(d&&!B){if(f?.code==="turn-stopped")throw this.#a.complete(o,d.token),f;let Q=this.#a.fail(o,d.token);if(Q.retained){let y=new Error(f?.message??String(f),{cause:f});throw y.code=f?.code,y.providerCode=f?.providerCode,y.method=f?.method,Number.isInteger(f?.status)&&(y.status=f.status),y.batchInputMessage=Q.message,y}}throw f}finally{await this.#be(o),await this.#D.closeRoute(o)}}async#_e(t,o){if(t?.message?.chat_type!=="p2p"){await this.#b(t.message.chat_id,w("\u4E3A\u907F\u514D\u6388\u6743\u94FE\u63A5\u66B4\u9732\uFF0C\u8BF7\u79C1\u804A\u673A\u5668\u4EBA\u53D1\u9001 /repair\u3002"));return}let i=wke(t);if(!i){await this.#b(t.message.chat_id,w("\u65E0\u6CD5\u8BC6\u522B\u5F53\u524D\u53D1\u9001\u8005\uFF0C\u672A\u53D1\u8D77\u4FEE\u590D\u3002"));return}if(!this.#F){await this.#b(t.message.chat_id,w("\u5F53\u524D Host \u7248\u672C\u6682\u4E0D\u652F\u6301\u804A\u5929\u5185\u4FEE\u590D\uFF0C\u8BF7\u5148\u66F4\u65B0\u63D2\u4EF6\u3002"));return}let e=Ake.exec(o);if(!e){await this.#b(t.message.chat_id,w("\u7528\u6CD5\uFF1A/repair\u3001/repair qr\u3001/repair status\u3001/repair cancel \u6216 /repair verify"));return}let a=e[1]?.toLowerCase()??"start",A=t.message.chat_id;if(a==="start"){await this.#Ye({actorOpenId:i,chatId:A});return}let r=this.#P;if(!r){await this.#b(A,w("\u5F53\u524D Runtime \u6CA1\u6709\u53EF\u6062\u590D\u7684\u4FEE\u590D\u4EFB\u52A1\u8BB0\u5F55\uFF08\u673A\u5668\u4EBA\u53EF\u80FD\u521A\u5B8C\u6210\u5BC6\u94A5\u66F4\u65B0\u5E76\u91CD\u542F\uFF09\u3002\u672C\u547D\u4EE4\u4E0D\u4F1A\u542F\u52A8\u65B0\u7684\u6388\u6743\uFF1B\u8BF7\u67E5\u770B\u673A\u5668\u4EBA\u53D1\u9001\u7684\u9A8C\u8BC1\u7ED3\u679C\uFF0C\u786E\u8BA4\u4E0A\u4E00\u6B21\u4EFB\u52A1\u5DF2\u7ED3\u675F\u540E\u518D\u53D1\u9001 /repair\u3002"));return}if(r.actorOpenId!==i){await this.#b(A,w("\u53E6\u4E00\u4F4D\u7528\u6237\u6B63\u5728\u4FEE\u590D\u8BE5\u673A\u5668\u4EBA\uFF0C\u672C\u6B21\u4E0D\u4F1A\u663E\u793A\u5176\u6388\u6743\u4FE1\u606F\u3002"));return}if(a==="cancel"){let g;try{let c=await this.#F.cancel(this.#le(r));g=bu(c,{botId:this.#R}),r.snapshot=g}catch{await this.#b(A,w("\u6682\u65F6\u65E0\u6CD5\u53D6\u6D88\u4FEE\u590D\u4EFB\u52A1\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));return}g.state==="cancelled"&&(r.stopped=!0,this.#H+=1),await this.#b(A,this.#oe(g));return}let s;try{s=await this.#Ee(r)}catch{await this.#b(A,w("\u6682\u65F6\u65E0\u6CD5\u67E5\u8BE2\u4FEE\u590D\u72B6\u6001\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));return}if(a==="qr"){if(!pP.has(s.state)||!r.verificationUrl){await this.#b(A,this.#oe(s,{verificationFocused:!0}));return}await this.#ze(A,r.verificationUrl,s);return}await this.#b(A,this.#oe(s,{verificationFocused:a==="verify"}))}#le(t){return{botId:this.#R,attemptId:t.attemptId,actorOpenId:t.actorOpenId,chatId:t.chatId}}async#Ye({actorOpenId:t,chatId:o}){let i=this.#P,e=!1;if(i&&pP.has(i.snapshot.state)){if(i.actorOpenId!==t){await this.#b(o,w("\u53E6\u4E00\u4F4D\u7528\u6237\u6B63\u5728\u4FEE\u590D\u8BE5\u673A\u5668\u4EBA\uFF0C\u672C\u6B21\u4E0D\u4F1A\u663E\u793A\u5176\u6388\u6743\u4FE1\u606F\u3002"));return}let r;try{r=await this.#Ee(i)}catch{await this.#b(o,w("\u6682\u65F6\u65E0\u6CD5\u67E5\u8BE2\u4FEE\u590D\u72B6\u6001\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));return}if(r.state==="saving"){await this.#b(o,this.#oe(r));return}if(k6.has(r.state)){let s;try{s=bu(await this.#F.cancel(this.#le(i)),{botId:this.#R}),i.snapshot=s}catch{await this.#b(o,w("\u6682\u65F6\u65E0\u6CD5\u53D6\u6D88\u65E7\u4FEE\u590D\u4EFB\u52A1\uFF0C\u672A\u751F\u6210\u65B0\u94FE\u63A5\uFF1B\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));return}if(!gS.has(s.state)){await this.#b(o,this.#oe(s));return}i.stopped=!0,this.#H+=1,e=s.state==="cancelled"}else gS.has(r.state)&&(i.stopped=!0,this.#H+=1)}let a;try{a=bu(await this.#F.start({botId:this.#R,actorOpenId:t,chatId:o}),{botId:this.#R}),a=await this.#je(a,{actorOpenId:t,chatId:o})}catch{await this.#b(o,w("\u4FEE\u590D\u6D41\u7A0B\u6682\u65F6\u5931\u8D25\uFF0C\u73B0\u6709\u673A\u5668\u4EBA\u8FDE\u63A5\u4E0D\u53D7\u5F71\u54CD\uFF1B\u8BF7\u7A0D\u540E\u53D1\u9001 /repair \u91CD\u8BD5\u3002"));return}let A={attemptId:a.attemptId,actorOpenId:t,chatId:o,snapshot:a,verificationUrl:null,stopped:!1,announcedSaving:!1,announcedTerminal:!1};if(this.#P=A,a.verificationUrl)try{A.verificationUrl=_6(a.verificationUrl,this.#_)}catch{A.stopped=!0,await this.#F.cancel(this.#le(A)).catch(()=>{}),await this.#b(o,w("\u98DE\u4E66\u8FD4\u56DE\u4E86\u65E0\u6CD5\u5B89\u5168\u9A8C\u8BC1\u7684\u6388\u6743\u94FE\u63A5\uFF0C\u5DF2\u4E2D\u6B62\u672C\u6B21\u4FEE\u590D\u3002"));return}if(gS.has(a.state)){A.announcedTerminal=!0,a.state!=="succeeded"&&await this.#b(o,this.#oe(a));return}if(!A.verificationUrl){A.stopped=!0,await this.#b(o,w("\u98DE\u4E66\u672A\u8FD4\u56DE\u6388\u6743\u94FE\u63A5\uFF0C\u5DF2\u4E2D\u6B62\u672C\u6B21\u4FEE\u590D\u3002"));return}await this.#qe(o,A.verificationUrl,a,{restarted:e}),this.#Je(A)}async#je(t,o){let i=t,e=Date.now()+this.#L;for(;!i.verificationUrl&&pP.has(i.state);){if(Date.now()>=e)throw new Error("Feishu repair link timed out");await M6(Math.min(100,this.#U),this.#w),i=bu(await this.#F.status({botId:this.#R,attemptId:i.attemptId,actorOpenId:o.actorOpenId,chatId:o.chatId}),{botId:this.#R})}return i}async#Ee(t){let o=bu(await this.#F.status(this.#le(t)),{botId:this.#R});if(o.attemptId!==t.attemptId)throw new Error("Feishu repair attempt changed unexpectedly");return t.snapshot=o,o.verificationUrl&&(t.verificationUrl=_6(o.verificationUrl,this.#_)),o}#Je(t){let o=++this.#H;(async()=>{for(;!t.stopped&&this.#P===t&&this.#H===o&&!this.#w?.aborted;){let i=Math.max(250,Math.min(1e4,t.snapshot.pollIntervalMs??this.#U));if(await M6(i,this.#w),t.stopped||this.#P!==t||this.#H!==o)return;let e=await this.#Ee(t);if(e.state==="saving"&&!t.announcedSaving&&(t.announcedSaving=!0,await this.#b(t.chatId,w("\u6388\u6743\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u53D1\u9001\u5E76\u7B49\u5F85\u6D4B\u8BD5\u6309\u94AE\u56DE\u8C03\uFF1B\u6536\u5230\u771F\u5B9E\u56DE\u8C03\u540E\u624D\u4F1A\u5B8C\u6210\u3002"))),gS.has(e.state)){t.stopped=!0,e.state!=="succeeded"&&!t.announcedTerminal&&(t.announcedTerminal=!0,await this.#b(t.chatId,this.#oe(e)));return}}})().catch(async()=>{this.#w?.aborted||t.stopped||this.#P!==t||(t.stopped=!0,this.#O.warn?.("[dsh-feishu] callback repair status monitoring failed"),await this.#b(t.chatId,w("\u4FEE\u590D\u72B6\u6001\u67E5\u8BE2\u4E2D\u65AD\uFF0C\u73B0\u6709\u673A\u5668\u4EBA\u8FDE\u63A5\u4E0D\u53D7\u5F71\u54CD\uFF1B\u53D1\u9001 /repair status \u91CD\u8BD5\u67E5\u8BE2\u3002")).catch(()=>{}))})}async#qe(t,o,i,{restarted:e=!1}={}){let a=i.remainingSeconds??(i.expiresAt?Math.max(0,Math.ceil((i.expiresAt-Date.now())/1e3)):null),A=a===null?w("\u94FE\u63A5\u4E3A\u77ED\u671F\u6709\u6548"):w("\u94FE\u63A5\u7EA6 {minutes} \u5206\u949F\u540E\u8FC7\u671F",{minutes:Math.max(1,Math.ceil(a/60))});await this.#b(t,[e?w("\u65E7\u6388\u6743\u94FE\u63A5\u5DF2\u4F5C\u5E9F\uFF0C\u5DF2\u751F\u6210\u65B0\u7684\u4FEE\u590D\u94FE\u63A5\u3002"):w("\u{1F527} \u51C6\u5907\u8865\u5168\u6743\u9650\u4E0E\u56DE\u8C03\u3002"),w("\u672C\u6B21\u4F1A\u589E\u91CF\u6DFB\u52A0\u5F53\u524D\u7F3A\u5C11\u9879\uFF1A\u5361\u7247\u56DE\u8C03 card.action.trigger\uFF1B\u98DE\u4E66\u663E\u793A\u4E3A\u201C\u83B7\u53D6\u5355\u804A\u3001\u7FA4\u7EC4\u6D88\u606F\u201D\u7684\u79DF\u6237\u6743\u9650 im:message:readonly\uFF08\u7528\u4E8E\u8BFB\u53D6\u7528\u6237\u6D88\u606F\u4E2D\u7684\u56FE\u7247\u6216\u6587\u4EF6\uFF09\uFF1Bim:resource\uFF08\u7528\u4E8E\u4E0A\u4F20\u673A\u5668\u4EBA\u53D1\u9001\u7684\u56FE\u7247\u6216\u6587\u4EF6\uFF09\uFF1B\u4EE5\u53CA\u539F\u751F\u547D\u4EE4\u9762\u677F\u6240\u9700\u7684 application:app_slash_command:read / write\u3002\u786E\u8BA4\u9875\u53EA\u4F1A\u663E\u793A\u5F53\u524D\u7F3A\u5C11\u7684\u9879\uFF1B\u82E5\u51FA\u73B0\u4E0A\u8FF0\u8303\u56F4\u4E4B\u5916\u7684\u914D\u7F6E\uFF0C\u8BF7\u53D6\u6D88\u3002"),"",w("\u5F53\u524D\u8BBE\u5907\u76F4\u63A5\u6253\u5F00\uFF1A"),o,"",w("\u82E5\u8981\u7528\u53E6\u4E00\u53F0\u8BBE\u5907\u626B\u7801\uFF0C\u53D1\u9001 /repair qr\u3002{expiry}\u3002",{expiry:A})].join(` `))}async#ze(t,o,i){try{let e=await $Re.toBuffer(o,{errorCorrectionLevel:"M",margin:1,width:480,type:"png"}),a=await this.#e.im.v1.image.create({data:{image_type:"message",image:e}}),A=lA(a?.image_key)??lA(a?.data?.image_key);if(!A)throw new Error("Feishu QR upload returned no image key");let r=i.remainingSeconds??(i.expiresAt?Math.max(0,Math.ceil((i.expiresAt-Date.now())/1e3)):null),s=r===null?"":w("\uFF08\u5269\u4F59\u7EA6 {minutes} \u5206\u949F\uFF09",{minutes:Math.max(1,Math.ceil(r/60))});await this.#b(t,w("\u8BF7\u7528\u53E6\u4E00\u53F0\u8BBE\u5907\u626B\u7801\u5B8C\u6210\u6388\u6743{remaining}\u3002",{remaining:s}));let g=await this.#e.im.v1.message.create({params:{receive_id_type:"chat_id"},data:{receive_id:t,msg_type:"image",content:JSON.stringify({image_key:A})}});if(g?.code&&g.code!==0)throw new Error("Feishu QR message send failed")}catch{await this.#b(t,w(`\u4E8C\u7EF4\u7801\u6682\u65F6\u65E0\u6CD5\u53D1\u9001\uFF0C\u8BF7\u76F4\u63A5\u6253\u5F00\u6388\u6743\u94FE\u63A5\uFF1A -{url}`,{url:o}))}}#oe(t,{verificationFocused:o=!1}={}){if(t.state==="succeeded")return w("\u2705 \u4FEE\u590D\u5B8C\u6210\uFF1A\u5DF2\u5B9E\u6D4B\u6536\u5230 card.action.trigger\uFF0C\u83DC\u5355\u6309\u94AE\u73B0\u5728\u53EF\u7528\u3002");if(t.state==="expired"||t.error?.code==="expired_token")return w("\u6388\u6743\u94FE\u63A5\u5DF2\u8FC7\u671F\uFF1B\u5E73\u53F0\u672A\u8FD4\u56DE\u6210\u529F\u7ED3\u679C\uFF0C\u65E0\u6CD5\u786E\u8BA4\u5DF2\u4FEE\u590D\u3002\u53D1\u9001 /repair \u751F\u6210\u65B0\u94FE\u63A5\u3002");if(t.state==="cancelled"||t.error?.code==="abort")return w("\u5DF2\u53D6\u6D88\u672C\u6B21\u4FEE\u590D\u6388\u6743\uFF0C\u672A\u786E\u8BA4\u5B8C\u6210\u4FEE\u590D\u3002");if(t.error?.code==="access_denied")return w("\u4F60\u5DF2\u53D6\u6D88\u6216\u62D2\u7EDD\u6388\u6743\uFF0C\u6CA1\u6709\u786E\u8BA4\u4FEE\u590D\uFF1B\u53D1\u9001 /repair \u53EF\u91CD\u8BD5\u3002");if(t.error?.code==="card_action_probe_timeout"||t.error?.code==="card-action-probe-timeout")return w("\u6388\u6743\u5DF2\u63D0\u4EA4\uFF0C\u4F46\u672A\u6536\u5230\u6D4B\u8BD5\u6309\u94AE\u56DE\u8C03\u3002\u53EF\u80FD\u5C1A\u672A\u70B9\u51FB\u6216\u914D\u7F6E\u4ECD\u5728\u4F20\u64AD\uFF1B\u7A0D\u540E\u53D1\u9001 /repair verify \u67E5\u8BE2\uFF0C\u4E0D\u8981\u76F2\u76EE\u91CD\u590D\u6388\u6743\u3002");if(t.state==="error")return w("\u4FEE\u590D\u6D41\u7A0B\u6682\u65F6\u5931\u8D25\uFF0C\u73B0\u6709\u673A\u5668\u4EBA\u8FDE\u63A5\u4E0D\u53D7\u5F71\u54CD\uFF1B\u53D1\u9001 /repair \u53EF\u91CD\u8BD5\u3002");if(t.state==="saving")return w("\u6388\u6743\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u7B49\u5F85\u4E13\u7528\u6D4B\u8BD5\u6309\u94AE\u7684\u771F\u5B9E\u56DE\u8C03\uFF1B\u56DE\u8C03\u5230\u8FBE\u524D\u4E0D\u4F1A\u5BA3\u544A\u6210\u529F\u3002");if(o)return w("\u6388\u6743\u5C1A\u672A\u5B8C\u6210\uFF0C\u6682\u65F6\u4E0D\u80FD\u9A8C\u8BC1\u5361\u7247\u6309\u94AE\u3002\u8BF7\u5148\u6253\u5F00\u6388\u6743\u94FE\u63A5\u5E76\u786E\u8BA4\u3002");let i=t.remainingSeconds===null?"":w("\uFF0C\u5269\u4F59\u7EA6 {minutes} \u5206\u949F",{minutes:Math.max(1,Math.ceil(t.remainingSeconds/60))});return w("\u4FEE\u590D\u4EFB\u52A1\u6B63\u5728\u7B49\u5F85\u6388\u6743{remaining}\u3002\u53D1\u9001 /repair qr \u53EF\u83B7\u53D6\u4E8C\u7EF4\u7801\uFF0C/repair cancel \u53EF\u53D6\u6D88\u3002",{remaining:i})}onCardAction(t){let o=lA(t?.operator?.open_id)??lA(t?.operator?.user_id)??lA(t?.operator?.operator_id?.open_id)??lA(t?.operator?.operator_id?.user_id)??lA(t?.open_id)??lA(t?.user_id);if(!o)return Promise.resolve();if(!this.#o&&!this.#N.has("*")&&!this.#N.has(o))return this.#O.warn?.("[dsh-feishu] ignoring card action from an unallowed legacy sender"),Promise.resolve();let i=b6(t?.action?.value),e=b6(t?.action?.form_value),a=lA(i.action)??lA(t?.action?.action);if(!a)return Promise.resolve();let A=Mu(t?.action?.option)??(a.endsWith("_pick")?_g(t?.action?.options)[0]:null),r=[...new Set([..._g(i.options),..._g(t?.action?.options),..._g(e[a]),...a==="watch_add"||a==="watch_remove"?_g(t?.action?.option):[]])],s=a==="workspace_pick"&&typeof A=="string"?`workspace:${A}`:a==="session_pick"&&typeof A=="string"?`use:${A}`:a==="preset_pick"&&typeof A=="string"?`preset:select:${A}`:a==="model_pick"&&typeof A=="string"?`model:select:${A}`:a==="archive_pick"&&typeof A=="string"?`archive:${A}`:a==="steer_pick"&&typeof A=="string"?`steer:${A}`:a,g=lA(t?.context?.open_message_id)??lA(t?.open_message_id)??lA(t?.message_id),c=g?this.#K.get(g):null;if(!c){if(this.#o)return Promise.resolve();let L=lA(t?.context?.open_chat_id)??lA(t?.open_chat_id)??lA(t?.chat_id);return L&&this.#b(L,w("\u8FD9\u4E2A\u83DC\u5355\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u56DE\u590D /m \u91CD\u65B0\u6253\u5F00\u3002"),{replyTo:g}).catch(()=>{}),Promise.resolve()}let I=c.key.startsWith("p2p:")?"direct":c.key.startsWith("group:")?"group":null,d=Qa(this.#o,{conversationType:I,senderIds:o,isCommand:!0});if(!d.allowed)return d.reason==="command-not-allowed"?this.#b(c.chatId,w(io)).catch(()=>{}):(this.#O.warn?.("[dsh-feishu] ignoring card action blocked by access policy"),Promise.resolve());this.#K.delete(g),this.#K.set(g,c);let B={...c,messageId:g,selections:r,operatorOpenId:o},f=lA(i.source),Q=lA(e.steer_text),y=lA(t?.event_id)??lA(t?.header?.event_id)??lA(t?.uuid)??lA(t?.header?.uuid),m=JSON.stringify({messageId:g,resolvedAction:s,option:A,multiValues:r,source:f,formText:Q}),_=s==="stop",T=s.startsWith("steer:")?s.slice(6):null,K=a==="steer"&&f==="quick"&&A!==null&&A!==wu||a==="steer"&&f==="form"&&Q!==null||T!==null&&T!==""&&!(T==="custom"||T===wu);return this.#We(B,m,async()=>{if(a==="steer"){if(f==="quick"&&A){if(A===wu){await this.#J(B.chatId,IP(),{key:B.key,updateMessageId:B.messageId});return}await this.#ue(B,A);return}if(f==="form"){if(Q){await this.#ue(B,Q);return}await this.#b(B.chatId,w("\u8BF7\u8F93\u5165\u8865\u5145\u6307\u4EE4\u540E\u518D\u63D0\u4EA4\u3002"),{replyTo:B.messageId??null});return}}await this.#se(s,B)},{lane:_||K?"control":"regular",coalesceStop:_,eventId:y,operatorOpenId:o})}#Te(t=Date.now()){for(let[o,i]of this.#y)i<=t&&this.#y.delete(o);for(;this.#y.size>O6;){let o=this.#y.keys().next().value;if(o===void 0)break;this.#y.delete(o)}}#Ne(t,o=Date.now()){t&&(this.#y.delete(t),this.#y.set(t,o+ske),this.#Te(o))}#Ve(t){let o=t.key,i=Date.now(),e=this.#m.get(o);if(e?.task||e&&i-e.at{}).finally(()=>{this.#d.delete(a);let A=this.#m.get(o);A?.task===a&&this.#m.set(o,{at:A.at,task:null})}),this.#m.delete(o),this.#m.set(o,{at:i,task:a}),this.#d.add(a);this.#m.size>200;){let A=this.#m.keys().next().value;if(A===void 0)break;this.#m.delete(A)}return a}#We(t,o,i,{lane:e="regular",coalesceStop:a=!1,eventId:A=null,operatorOpenId:r=null}={}){let s=t.key,g=A?`${s}\0${r??""}\0event:${A}`:null,c=g??`${s}\0${r??""}\0action:${o}`,I=Date.now();if(this.#Te(I),g&&(this.#y.get(g)??0)>I)return Promise.resolve();let d=this.#f.get(c);if(d)return d;if(a){let L=this.#Q.get(s);if(L){if(g)for(this.#S.delete(g),this.#S.set(g,L);this.#S.size>O6;){let U=this.#S.keys().next().value;if(U===void 0)break;this.#S.delete(U)}return L}}let B=e==="control",f=B?this.#C:this.#h,Q=B?this.#B:this.#u,y=B?nke:oke,m=Q.get(s)??0,_=a?y+1:y;if(m>=_)return this.#Ve(t);let T=f.get(s)??Promise.resolve();Q.set(s,m+1);let R,K=!1;return R=T.catch(()=>{}).then(async()=>{this.#w?.throwIfAborted(),K=!0,await i()}).catch(async L=>{this.#w?.aborted||await this.#j(t.chatId,L,{logLabel:"card action",replyTo:t.messageId})}).finally(()=>{if(this.#f.get(c)===R&&this.#f.delete(c),K&&g&&this.#Ne(g),a&&this.#Q.get(s)===R&&this.#Q.delete(s),a){let U=Date.now();for(let[z,q]of this.#S)q===R&&(this.#S.delete(z),this.#Ne(z,U))}f.get(s)===R&&f.delete(s);let L=(Q.get(s)??1)-1;L>0?Q.set(s,L):Q.delete(s),this.#d.delete(R)}),this.#f.set(c,R),a&&this.#Q.set(s,R),this.#d.add(R),f.set(s,R),R}async#se(t,{chatId:o,key:i,messageId:e=null,sessionWorkspace:a=null,sessionPage:A=0,selections:r=[]}){let s=g=>this.#b(o,g,{replyTo:e});if(t.startsWith("approve:")||t.startsWith("reject:")){let g=t.indexOf(":"),c=t.slice(g+1),I=t.startsWith("approve:")?"allowed-once":"rejected";await this.#D.submitByApprovalId(c,I)||await s(w("\u8BE5\u5BA1\u6279\u5DF2\u5904\u7406\u6216\u4E0D\u5B58\u5728\uFF0C\u65E0\u9700\u91CD\u590D\u64CD\u4F5C\u3002")).catch(()=>{});return}if(t.startsWith("answer:")){let g=t.slice(7),c=g.indexOf(":");if(c!==-1){let I=g.slice(0,c),d=g.slice(c+1),B=this.#s.get(I),f=B?this.#n.get(B):null;f&&f.kind==="question"&&!f.submitting?await this.#Ue(f,d,{chatId:o}):await s(Du()).catch(()=>{})}return}if(t==="sessions"||/^sessions:\d+$/.test(t)){let g=t==="sessions"?0:Number(t.slice(9));await this.#pe({chatId:o,key:i,replyTo:e},a,g,{updateMessageId:e});return}if(t==="workspaces"){await this.#Re({chatId:o,key:i,replyTo:e},{updateMessageId:e});return}if(t==="watchlist"){await this.#ye(i,o,{updateMessageId:e,replyTo:e});return}if(t==="watch_add"||t==="watch_remove"){if(r.length===0){await s(w("\u8BF7\u5148\u9009\u62E9\u81F3\u5C11\u4E00\u4E2A\u4F1A\u8BDD\u3002"));return}let g=0,c=0;if(t==="watch_add"){let d=new Map;try{d=await this.#pA(a)}catch(B){this.#O.warn?.("[dsh-feishu] batch watch validation failed:",B.message)}for(let B of r){let f=d.get(B);if(!f){c+=1;continue}let Q=await this.#fe(i,o,B,{notify:!1,validatedTarget:f,replyTo:e});Q.changed?g+=1:Q.ok||(c+=1)}}else for(let d of r){let B=await this.#Qe(i,o,d,{notify:!1});B.changed?g+=1:B.ok||(c+=1)}let I=g>0&&c>0?t==="watch_add"?w("\u5DF2\u6279\u91CF\u5173\u6CE8 {count} \u4E2A\u4F1A\u8BDD\uFF0C\u53E6\u6709 {failed} \u4E2A\u672A\u6210\u529F\u3002",{count:g,failed:c}):w("\u5DF2\u53D6\u6D88\u5173\u6CE8 {count} \u4E2A\u4F1A\u8BDD\uFF0C\u53E6\u6709 {failed} \u4E2A\u672A\u6210\u529F\u3002",{count:g,failed:c}):g>0?t==="watch_add"?w("\u5DF2\u6279\u91CF\u5173\u6CE8 {count} \u4E2A\u4F1A\u8BDD\u3002",{count:g}):w("\u5DF2\u53D6\u6D88\u5173\u6CE8 {count} \u4E2A\u4F1A\u8BDD\u3002",{count:g}):c>0?w("\u6240\u9009\u4F1A\u8BDD\u5747\u672A\u5904\u7406\u6210\u529F\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"):t==="watch_add"?w("\u6240\u9009\u4F1A\u8BDD\u5DF2\u5728\u5173\u6CE8\u5217\u8868\u4E2D\u3002"):w("\u6240\u9009\u4F1A\u8BDD\u5DF2\u4E0D\u5728\u5173\u6CE8\u5217\u8868\u4E2D\u3002");try{await this.#ye(i,o,{updateMessageId:e,replyTo:e})}catch(d){this.#O.warn?.("[dsh-feishu] watch list refresh failed:",d.message)}await s(I).catch(d=>{this.#O.warn?.("[dsh-feishu] watch batch summary failed:",d.message)});return}if(t==="new"){if(this.#i.has(i)||this.#ne(i)){await s(w("\u5F53\u524D\u4EFB\u52A1\u4ECD\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u505C\u6B62\u4EFB\u52A1\u6216\u7B49\u5F85\u4EFB\u52A1\u5B8C\u6210\u540E\u518D\u5F00\u542F\u65B0\u4F1A\u8BDD\u3002"));return}await this.#r.clearSession(i),await s(w("\u5DF2\u5F00\u542F\u5168\u65B0 Harness \u4F1A\u8BDD\u3002")),await this.#X(i,o,{updateMessageId:e,replyTo:e});return}if(t==="use:current"){let g=this.#r.sessionFor(i);if(typeof g!="string"||!g){await s(w("\u5F53\u524D\u6CA1\u6709\u7ED1\u5B9A\u7684\u4F1A\u8BDD\uFF0C\u8BF7\u5148\u4ECE\u4F1A\u8BDD\u5217\u8868\u9009\u62E9\u3002"));return}await s(w("\u5DF2\u5C31\u7EEA\uFF0C\u76F4\u63A5\u53D1\u6D88\u606F\u5373\u53EF\u7EE7\u7EED\u5F53\u524D\u4F1A\u8BDD\u3002"));return}if(t==="archive_toggle"||t==="archive:on"||t==="archive:off"){let g=t==="archive:on"?!0:t==="archive:off"?!1:!(this.#r?.includesArchivedSessions?.()??!1);await this.#r?.setIncludeArchivedSessions?.(g),await s(g?w("\u5DF2\u5F00\u542F\uFF1A\u4F1A\u8BDD\u5217\u8868\u5305\u542B\u5F52\u6863\u4F1A\u8BDD\u3002"):w("\u5DF2\u5173\u95ED\uFF1A\u4F1A\u8BDD\u5217\u8868\u9690\u85CF\u5F52\u6863\u4F1A\u8BDD\u3002")),await this.#X(i,o,{updateMessageId:e,replyTo:e});return}if(t==="repair"){await s(w("\u4FEE\u590D\u9700\u5728\u79C1\u804A\u4E2D\u9A8C\u8BC1\u63A5\u5165\u8005\u8EAB\u4EFD\uFF0C\u8BF7\u76F4\u63A5\u53D1\u9001 /repair \u5F00\u59CB\u3002"));return}if(t==="compact"){await this.#oA(i,o,e);return}if(t==="stop"){await this.#nA(i,o,e);return}if(t==="steer"){await this.#sA(i,o,{updateMessageId:e});return}if(t.startsWith("steer:")){let g=t.slice(6);if(g==="custom"){await this.#J(o,IP(),{key:i,updateMessageId:e,replyTo:e});return}await this.#ue({key:i,chatId:o,messageId:e},g);return}if(t==="presets"){await this.#AA(i,o,{updateMessageId:e});return}if(t==="models"){await this.#tA(i,o,{updateMessageId:e});return}if(t==="status"){await this.#aA(i,o,{updateMessageId:e});return}if(t==="help"){await this.#iA(i,o,{updateMessageId:e});return}if(t==="back_to_menu"){await this.#X(i,o,{updateMessageId:e,replyTo:e});return}if(t==="preset_default"){await this.#ke(i,o,{updateMessageId:e});return}if(t.startsWith("preset:select:")){let g=t.slice(14);if(g===rS){await this.#ke(i,o,{updateMessageId:e});return}await this.#gA(i,o,g,{updateMessageId:e});return}if(t.startsWith("model:select:")){let g=t.slice(13);await this.#cA(i,o,g,{updateMessageId:e});return}if(t.startsWith("use:")){await this.#$e(i,o,t.slice(4),{updateMessageId:e,replyTo:e});return}if(t.startsWith("workspace:")){await this.#eA(i,o,t.slice(10),{updateMessageId:e,replyTo:e});return}if(t.startsWith("unwatch:")){(await this.#Qe(i,o,t.slice(8),{replyTo:e})).ok&&e&&await this.#pe({chatId:o,key:i,replyTo:e},a,A,{updateMessageId:e});return}if(t.startsWith("watch:")){let g=t.slice(6);(await this.#fe(i,o,g,{workspaceHint:a,replyTo:e})).ok&&e&&await this.#pe({chatId:o,key:i,replyTo:e},a,A,{updateMessageId:e})}}#ge(t,o){if(this.#v.size>=ike){let i=this.#v.keys().next().value;i!==void 0&&this.#v.delete(i)}this.#v.delete(t),this.#v.set(t,{...o,expiresAt:Date.now()+ake})}#Ze(t){let o=this.#v.get(t);return o?o.expiresAtthis.#b(i,s,{replyTo:A});if(t.kind==="menu"){let g=["workspaces","new","sessions","status","repair","help"][o-1];if(!g){await r(w("\u83DC\u5355\u6CA1\u6709\u8FD9\u4E2A\u7F16\u53F7\uFF0C\u56DE\u590D /m \u91CD\u65B0\u6253\u5F00\u3002"));return}if(g==="repair"){await this.#_e(a,"/repair");return}await this.#se(g,{chatId:i,key:e,messageId:A});return}if(t.kind==="sessions"){let s=t.sessions[o-1];if(!s?.sessionId){await r(w("\u672C\u9875\u53EA\u6709 {count} \u4E2A\u4F1A\u8BDD\uFF0C\u56DE\u590D /sessionlist \u91CD\u65B0\u67E5\u770B\u3002",{count:t.sessions.length}));return}await this.#se(`use:${s.sessionId}`,{chatId:i,key:e,messageId:A});return}if(t.kind==="workspaces"){let s=t.paths[o-1];if(!s){await r(w("\u53EA\u6709 {count} \u4E2A\u5DE5\u4F5C\u533A\uFF0C\u56DE\u590D /workspacelist \u91CD\u65B0\u67E5\u770B\u3002",{count:t.paths.length}));return}await this.#se(`workspace:${s}`,{chatId:i,key:e,messageId:A});return}if(t.kind==="watches"){let s=t.entries[o-1];if(!s?.sessionId){await r(w("\u5173\u6CE8\u5217\u8868\u53EA\u6709 {count} \u4E2A\u4F1A\u8BDD\u3002",{count:t.entries.length}));return}await this.#se(`unwatch:${s.sessionId}`,{chatId:i,key:e,messageId:A})}}#ce(t){return this.#r?.includesArchivedSessions?.()===!1?t.filter(o=>o.archived!==!0):t}async#pe({chatId:t,key:o,replyTo:i=null},e,a=0,{updateMessageId:A=null}={}){try{let r=this.#$(),s=await i_(e??"",this.#t,{signal:r});if(s.error){await this.#b(t,s.error,{replyTo:i});return}let g=await this.#t.listWorkspaceSessions(s.workspace,{signal:r}),c=this.#ce(Array.isArray(g?.sessions)?g.sessions:[]),I=g?.workspace??s.workspace;if(c.length===0){await this.#b(t,w(`\u5DE5\u4F5C\u533A\uFF1A{workspace} +{url}`,{url:o}))}}#oe(t,{verificationFocused:o=!1}={}){if(t.state==="succeeded")return w("\u2705 \u4FEE\u590D\u5B8C\u6210\uFF1A\u5DF2\u5B9E\u6D4B\u6536\u5230 card.action.trigger\uFF0C\u83DC\u5355\u6309\u94AE\u73B0\u5728\u53EF\u7528\u3002");if(t.state==="expired"||t.error?.code==="expired_token")return w("\u6388\u6743\u94FE\u63A5\u5DF2\u8FC7\u671F\uFF1B\u5E73\u53F0\u672A\u8FD4\u56DE\u6210\u529F\u7ED3\u679C\uFF0C\u65E0\u6CD5\u786E\u8BA4\u5DF2\u4FEE\u590D\u3002\u53D1\u9001 /repair \u751F\u6210\u65B0\u94FE\u63A5\u3002");if(t.state==="cancelled"||t.error?.code==="abort")return w("\u5DF2\u53D6\u6D88\u672C\u6B21\u4FEE\u590D\u6388\u6743\uFF0C\u672A\u786E\u8BA4\u5B8C\u6210\u4FEE\u590D\u3002");if(t.error?.code==="access_denied")return w("\u4F60\u5DF2\u53D6\u6D88\u6216\u62D2\u7EDD\u6388\u6743\uFF0C\u6CA1\u6709\u786E\u8BA4\u4FEE\u590D\uFF1B\u53D1\u9001 /repair \u53EF\u91CD\u8BD5\u3002");if(t.error?.code==="card_action_probe_timeout"||t.error?.code==="card-action-probe-timeout")return w("\u6388\u6743\u5DF2\u63D0\u4EA4\uFF0C\u4F46\u672A\u6536\u5230\u6D4B\u8BD5\u6309\u94AE\u56DE\u8C03\u3002\u53EF\u80FD\u5C1A\u672A\u70B9\u51FB\u6216\u914D\u7F6E\u4ECD\u5728\u4F20\u64AD\uFF1B\u7A0D\u540E\u53D1\u9001 /repair verify \u67E5\u8BE2\uFF0C\u4E0D\u8981\u76F2\u76EE\u91CD\u590D\u6388\u6743\u3002");if(t.state==="error")return w("\u4FEE\u590D\u6D41\u7A0B\u6682\u65F6\u5931\u8D25\uFF0C\u73B0\u6709\u673A\u5668\u4EBA\u8FDE\u63A5\u4E0D\u53D7\u5F71\u54CD\uFF1B\u53D1\u9001 /repair \u53EF\u91CD\u8BD5\u3002");if(t.state==="saving")return w("\u6388\u6743\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u7B49\u5F85\u4E13\u7528\u6D4B\u8BD5\u6309\u94AE\u7684\u771F\u5B9E\u56DE\u8C03\uFF1B\u56DE\u8C03\u5230\u8FBE\u524D\u4E0D\u4F1A\u5BA3\u544A\u6210\u529F\u3002");if(o)return w("\u6388\u6743\u5C1A\u672A\u5B8C\u6210\uFF0C\u6682\u65F6\u4E0D\u80FD\u9A8C\u8BC1\u5361\u7247\u6309\u94AE\u3002\u8BF7\u5148\u6253\u5F00\u6388\u6743\u94FE\u63A5\u5E76\u786E\u8BA4\u3002");let i=t.remainingSeconds===null?"":w("\uFF0C\u5269\u4F59\u7EA6 {minutes} \u5206\u949F",{minutes:Math.max(1,Math.ceil(t.remainingSeconds/60))});return w("\u4FEE\u590D\u4EFB\u52A1\u6B63\u5728\u7B49\u5F85\u6388\u6743{remaining}\u3002\u53D1\u9001 /repair qr \u53EF\u83B7\u53D6\u4E8C\u7EF4\u7801\uFF0C/repair cancel \u53EF\u53D6\u6D88\u3002",{remaining:i})}onCardAction(t){let o=lA(t?.operator?.open_id)??lA(t?.operator?.user_id)??lA(t?.operator?.operator_id?.open_id)??lA(t?.operator?.operator_id?.user_id)??lA(t?.open_id)??lA(t?.user_id);if(!o)return Promise.resolve();if(!this.#o&&!this.#N.has("*")&&!this.#N.has(o))return this.#O.warn?.("[dsh-feishu] ignoring card action from an unallowed legacy sender"),Promise.resolve();let i=b6(t?.action?.value),e=b6(t?.action?.form_value),a=lA(i.action)??lA(t?.action?.action);if(!a)return Promise.resolve();let A=Mu(t?.action?.option)??(a.endsWith("_pick")?_g(t?.action?.options)[0]:null),r=[...new Set([..._g(i.options),..._g(t?.action?.options),..._g(e[a]),...a==="watch_add"||a==="watch_remove"?_g(t?.action?.option):[]])],s=a==="workspace_pick"&&typeof A=="string"?`workspace:${A}`:a==="session_pick"&&typeof A=="string"?`use:${A}`:a==="preset_pick"&&typeof A=="string"?`preset:select:${A}`:a==="model_pick"&&typeof A=="string"?`model:select:${A}`:a==="archive_pick"&&typeof A=="string"?`archive:${A}`:a==="steer_pick"&&typeof A=="string"?`steer:${A}`:a,g=lA(t?.context?.open_message_id)??lA(t?.open_message_id)??lA(t?.message_id),c=g?this.#K.get(g):null;if(!c){if(this.#o)return Promise.resolve();let L=lA(t?.context?.open_chat_id)??lA(t?.open_chat_id)??lA(t?.chat_id);return L&&this.#b(L,w("\u8FD9\u4E2A\u83DC\u5355\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u56DE\u590D /m \u91CD\u65B0\u6253\u5F00\u3002"),{replyTo:g}).catch(()=>{}),Promise.resolve()}let I=c.key.startsWith("p2p:")?"direct":c.key.startsWith("group:")?"group":null,d=Qa(this.#o,{conversationType:I,senderIds:o,isCommand:!0});if(!d.allowed)return d.reason==="command-not-allowed"?this.#b(c.chatId,w(io)).catch(()=>{}):(this.#O.warn?.("[dsh-feishu] ignoring card action blocked by access policy"),Promise.resolve());this.#K.delete(g),this.#K.set(g,c);let B={...c,messageId:g,selections:r,operatorOpenId:o},f=lA(i.source),Q=lA(e.steer_text),y=lA(t?.event_id)??lA(t?.header?.event_id)??lA(t?.uuid)??lA(t?.header?.uuid),m=JSON.stringify({messageId:g,resolvedAction:s,option:A,multiValues:r,source:f,formText:Q}),_=s==="stop",T=s.startsWith("steer:")?s.slice(6):null,K=a==="steer"&&f==="quick"&&A!==null&&A!==wu||a==="steer"&&f==="form"&&Q!==null||T!==null&&T!==""&&!(T==="custom"||T===wu);return this.#We(B,m,async()=>{if(a==="steer"){if(f==="quick"&&A){if(A===wu){await this.#J(B.chatId,IP(),{key:B.key,updateMessageId:B.messageId});return}await this.#ue(B,A);return}if(f==="form"){if(Q){await this.#ue(B,Q);return}await this.#b(B.chatId,w("\u8BF7\u8F93\u5165\u8865\u5145\u6307\u4EE4\u540E\u518D\u63D0\u4EA4\u3002"),{replyTo:B.messageId??null});return}}await this.#se(s,{...B,actor:B.operatorOpenId})},{lane:_||K?"control":"regular",coalesceStop:_,eventId:y,operatorOpenId:o})}#Te(t=Date.now()){for(let[o,i]of this.#y)i<=t&&this.#y.delete(o);for(;this.#y.size>O6;){let o=this.#y.keys().next().value;if(o===void 0)break;this.#y.delete(o)}}#Ne(t,o=Date.now()){t&&(this.#y.delete(t),this.#y.set(t,o+ske),this.#Te(o))}#Ve(t){let o=t.key,i=Date.now(),e=this.#m.get(o);if(e?.task||e&&i-e.at{}).finally(()=>{this.#d.delete(a);let A=this.#m.get(o);A?.task===a&&this.#m.set(o,{at:A.at,task:null})}),this.#m.delete(o),this.#m.set(o,{at:i,task:a}),this.#d.add(a);this.#m.size>200;){let A=this.#m.keys().next().value;if(A===void 0)break;this.#m.delete(A)}return a}#We(t,o,i,{lane:e="regular",coalesceStop:a=!1,eventId:A=null,operatorOpenId:r=null}={}){let s=t.key,g=A?`${s}\0${r??""}\0event:${A}`:null,c=g??`${s}\0${r??""}\0action:${o}`,I=Date.now();if(this.#Te(I),g&&(this.#y.get(g)??0)>I)return Promise.resolve();let d=this.#f.get(c);if(d)return d;if(a){let L=this.#Q.get(s);if(L){if(g)for(this.#S.delete(g),this.#S.set(g,L);this.#S.size>O6;){let U=this.#S.keys().next().value;if(U===void 0)break;this.#S.delete(U)}return L}}let B=e==="control",f=B?this.#C:this.#h,Q=B?this.#B:this.#u,y=B?nke:oke,m=Q.get(s)??0,_=a?y+1:y;if(m>=_)return this.#Ve(t);let T=f.get(s)??Promise.resolve();Q.set(s,m+1);let R,K=!1;return R=T.catch(()=>{}).then(async()=>{this.#w?.throwIfAborted(),K=!0,await i()}).catch(async L=>{this.#w?.aborted||await this.#j(t.chatId,L,{logLabel:"card action",replyTo:t.messageId})}).finally(()=>{if(this.#f.get(c)===R&&this.#f.delete(c),K&&g&&this.#Ne(g),a&&this.#Q.get(s)===R&&this.#Q.delete(s),a){let U=Date.now();for(let[z,q]of this.#S)q===R&&(this.#S.delete(z),this.#Ne(z,U))}f.get(s)===R&&f.delete(s);let L=(Q.get(s)??1)-1;L>0?Q.set(s,L):Q.delete(s),this.#d.delete(R)}),this.#f.set(c,R),a&&this.#Q.set(s,R),this.#d.add(R),f.set(s,R),R}async#se(t,{chatId:o,key:i,messageId:e=null,sessionWorkspace:a=null,sessionPage:A=0,selections:r=[],actor:s=null}){let g=c=>this.#b(o,c,{replyTo:e});if(t.startsWith("approve:")||t.startsWith("reject:")){let c=t.indexOf(":"),I=t.slice(c+1),d=t.startsWith("approve:")?"allowed-once":"rejected";await this.#D.submitByApprovalId(I,d,{actor:s})||await g(w("\u8BE5\u5BA1\u6279\u5DF2\u5904\u7406\u6216\u4E0D\u5B58\u5728\uFF0C\u65E0\u9700\u91CD\u590D\u64CD\u4F5C\u3002")).catch(()=>{});return}if(t.startsWith("answer:")){let c=t.slice(7),I=c.indexOf(":");if(I!==-1){let d=c.slice(0,I),B=c.slice(I+1),f=B.indexOf(":"),Q=f===-1?B:B.slice(0,f),y=f===-1?"":B.slice(f+1),m=this.#s.get(d),_=m?this.#n.get(m):null;_&&_.kind==="question"&&!_.submitting&&_.actor===s&&Number(Q)===_.index?await this.#Ue(_,y,{chatId:o}):await g(Du()).catch(()=>{})}return}if(t==="sessions"||/^sessions:\d+$/.test(t)){let c=t==="sessions"?0:Number(t.slice(9));await this.#pe({chatId:o,key:i,replyTo:e},a,c,{updateMessageId:e});return}if(t==="workspaces"){await this.#Re({chatId:o,key:i,replyTo:e},{updateMessageId:e});return}if(t==="watchlist"){await this.#ye(i,o,{updateMessageId:e,replyTo:e});return}if(t==="watch_add"||t==="watch_remove"){if(r.length===0){await g(w("\u8BF7\u5148\u9009\u62E9\u81F3\u5C11\u4E00\u4E2A\u4F1A\u8BDD\u3002"));return}let c=0,I=0;if(t==="watch_add"){let B=new Map;try{B=await this.#pA(a)}catch(f){this.#O.warn?.("[dsh-feishu] batch watch validation failed:",f.message)}for(let f of r){let Q=B.get(f);if(!Q){I+=1;continue}let y=await this.#fe(i,o,f,{notify:!1,validatedTarget:Q,replyTo:e});y.changed?c+=1:y.ok||(I+=1)}}else for(let B of r){let f=await this.#Qe(i,o,B,{notify:!1});f.changed?c+=1:f.ok||(I+=1)}let d=c>0&&I>0?t==="watch_add"?w("\u5DF2\u6279\u91CF\u5173\u6CE8 {count} \u4E2A\u4F1A\u8BDD\uFF0C\u53E6\u6709 {failed} \u4E2A\u672A\u6210\u529F\u3002",{count:c,failed:I}):w("\u5DF2\u53D6\u6D88\u5173\u6CE8 {count} \u4E2A\u4F1A\u8BDD\uFF0C\u53E6\u6709 {failed} \u4E2A\u672A\u6210\u529F\u3002",{count:c,failed:I}):c>0?t==="watch_add"?w("\u5DF2\u6279\u91CF\u5173\u6CE8 {count} \u4E2A\u4F1A\u8BDD\u3002",{count:c}):w("\u5DF2\u53D6\u6D88\u5173\u6CE8 {count} \u4E2A\u4F1A\u8BDD\u3002",{count:c}):I>0?w("\u6240\u9009\u4F1A\u8BDD\u5747\u672A\u5904\u7406\u6210\u529F\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"):t==="watch_add"?w("\u6240\u9009\u4F1A\u8BDD\u5DF2\u5728\u5173\u6CE8\u5217\u8868\u4E2D\u3002"):w("\u6240\u9009\u4F1A\u8BDD\u5DF2\u4E0D\u5728\u5173\u6CE8\u5217\u8868\u4E2D\u3002");try{await this.#ye(i,o,{updateMessageId:e,replyTo:e})}catch(B){this.#O.warn?.("[dsh-feishu] watch list refresh failed:",B.message)}await g(d).catch(B=>{this.#O.warn?.("[dsh-feishu] watch batch summary failed:",B.message)});return}if(t==="new"){if(this.#i.has(i)||this.#ne(i)){await g(w("\u5F53\u524D\u4EFB\u52A1\u4ECD\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u505C\u6B62\u4EFB\u52A1\u6216\u7B49\u5F85\u4EFB\u52A1\u5B8C\u6210\u540E\u518D\u5F00\u542F\u65B0\u4F1A\u8BDD\u3002"));return}await this.#r.clearSession(i),await g(w("\u5DF2\u5F00\u542F\u5168\u65B0 Harness \u4F1A\u8BDD\u3002")),await this.#X(i,o,{updateMessageId:e,replyTo:e});return}if(t==="use:current"){let c=this.#r.sessionFor(i);if(typeof c!="string"||!c){await g(w("\u5F53\u524D\u6CA1\u6709\u7ED1\u5B9A\u7684\u4F1A\u8BDD\uFF0C\u8BF7\u5148\u4ECE\u4F1A\u8BDD\u5217\u8868\u9009\u62E9\u3002"));return}await g(w("\u5DF2\u5C31\u7EEA\uFF0C\u76F4\u63A5\u53D1\u6D88\u606F\u5373\u53EF\u7EE7\u7EED\u5F53\u524D\u4F1A\u8BDD\u3002"));return}if(t==="archive_toggle"||t==="archive:on"||t==="archive:off"){let c=t==="archive:on"?!0:t==="archive:off"?!1:!(this.#r?.includesArchivedSessions?.()??!1);await this.#r?.setIncludeArchivedSessions?.(c),await g(c?w("\u5DF2\u5F00\u542F\uFF1A\u4F1A\u8BDD\u5217\u8868\u5305\u542B\u5F52\u6863\u4F1A\u8BDD\u3002"):w("\u5DF2\u5173\u95ED\uFF1A\u4F1A\u8BDD\u5217\u8868\u9690\u85CF\u5F52\u6863\u4F1A\u8BDD\u3002")),await this.#X(i,o,{updateMessageId:e,replyTo:e});return}if(t==="repair"){await g(w("\u4FEE\u590D\u9700\u5728\u79C1\u804A\u4E2D\u9A8C\u8BC1\u63A5\u5165\u8005\u8EAB\u4EFD\uFF0C\u8BF7\u76F4\u63A5\u53D1\u9001 /repair \u5F00\u59CB\u3002"));return}if(t==="compact"){await this.#oA(i,o,e);return}if(t==="stop"){await this.#nA(i,o,e);return}if(t==="steer"){await this.#sA(i,o,{updateMessageId:e});return}if(t.startsWith("steer:")){let c=t.slice(6);if(c==="custom"){await this.#J(o,IP(),{key:i,updateMessageId:e,replyTo:e});return}await this.#ue({key:i,chatId:o,messageId:e},c);return}if(t==="presets"){await this.#AA(i,o,{updateMessageId:e});return}if(t==="models"){await this.#tA(i,o,{updateMessageId:e});return}if(t==="status"){await this.#aA(i,o,{updateMessageId:e});return}if(t==="help"){await this.#iA(i,o,{updateMessageId:e});return}if(t==="back_to_menu"){await this.#X(i,o,{updateMessageId:e,replyTo:e});return}if(t==="preset_default"){await this.#ke(i,o,{updateMessageId:e});return}if(t.startsWith("preset:select:")){let c=t.slice(14);if(c===rS){await this.#ke(i,o,{updateMessageId:e});return}await this.#gA(i,o,c,{updateMessageId:e});return}if(t.startsWith("model:select:")){let c=t.slice(13);await this.#cA(i,o,c,{updateMessageId:e});return}if(t.startsWith("use:")){await this.#$e(i,o,t.slice(4),{updateMessageId:e,replyTo:e});return}if(t.startsWith("workspace:")){await this.#eA(i,o,t.slice(10),{updateMessageId:e,replyTo:e});return}if(t.startsWith("unwatch:")){(await this.#Qe(i,o,t.slice(8),{replyTo:e})).ok&&e&&await this.#pe({chatId:o,key:i,replyTo:e},a,A,{updateMessageId:e});return}if(t.startsWith("watch:")){let c=t.slice(6);(await this.#fe(i,o,c,{workspaceHint:a,replyTo:e})).ok&&e&&await this.#pe({chatId:o,key:i,replyTo:e},a,A,{updateMessageId:e})}}#ge(t,o){if(this.#v.size>=ike){let i=this.#v.keys().next().value;i!==void 0&&this.#v.delete(i)}this.#v.delete(t),this.#v.set(t,{...o,expiresAt:Date.now()+ake})}#Ze(t){let o=this.#v.get(t);return o?o.expiresAtthis.#b(i,s,{replyTo:A});if(t.kind==="menu"){let g=["workspaces","new","sessions","status","repair","help"][o-1];if(!g){await r(w("\u83DC\u5355\u6CA1\u6709\u8FD9\u4E2A\u7F16\u53F7\uFF0C\u56DE\u590D /m \u91CD\u65B0\u6253\u5F00\u3002"));return}if(g==="repair"){await this.#_e(a,"/repair");return}await this.#se(g,{chatId:i,key:e,messageId:A});return}if(t.kind==="sessions"){let s=t.sessions[o-1];if(!s?.sessionId){await r(w("\u672C\u9875\u53EA\u6709 {count} \u4E2A\u4F1A\u8BDD\uFF0C\u56DE\u590D /sessionlist \u91CD\u65B0\u67E5\u770B\u3002",{count:t.sessions.length}));return}await this.#se(`use:${s.sessionId}`,{chatId:i,key:e,messageId:A});return}if(t.kind==="workspaces"){let s=t.paths[o-1];if(!s){await r(w("\u53EA\u6709 {count} \u4E2A\u5DE5\u4F5C\u533A\uFF0C\u56DE\u590D /workspacelist \u91CD\u65B0\u67E5\u770B\u3002",{count:t.paths.length}));return}await this.#se(`workspace:${s}`,{chatId:i,key:e,messageId:A});return}if(t.kind==="watches"){let s=t.entries[o-1];if(!s?.sessionId){await r(w("\u5173\u6CE8\u5217\u8868\u53EA\u6709 {count} \u4E2A\u4F1A\u8BDD\u3002",{count:t.entries.length}));return}await this.#se(`unwatch:${s.sessionId}`,{chatId:i,key:e,messageId:A})}}#ce(t){return this.#r?.includesArchivedSessions?.()===!1?t.filter(o=>o.archived!==!0):t}async#pe({chatId:t,key:o,replyTo:i=null},e,a=0,{updateMessageId:A=null}={}){try{let r=this.#$(),s=await i_(e??"",this.#t,{signal:r});if(s.error){await this.#b(t,s.error,{replyTo:i});return}let g=await this.#t.listWorkspaceSessions(s.workspace,{signal:r}),c=this.#ce(Array.isArray(g?.sessions)?g.sessions:[]),I=g?.workspace??s.workspace;if(c.length===0){await this.#b(t,w(`\u5DE5\u4F5C\u533A\uFF1A{workspace} \u8BE5\u5DE5\u4F5C\u533A\u6682\u65E0\u4F1A\u8BDD\u3002`,{workspace:I}),{replyTo:i});return}let d=Math.ceil(c.length/Nc),B=Number.isSafeInteger(a)&&a>0?Math.min(a,d-1):0,f=new Set((this.#r.watchEntries?.(o)??[]).map(y=>y.sessionId)),Q=c.slice(B*Nc,(B+1)*Nc);this.#ge(o,{kind:"sessions",sessions:Q.map(y=>({...y,watched:f.has(y.sessionId)}))}),await this.#J(t,u6(I,c,B,c.length,f),{key:o,updateMessageId:A,replyTo:i,sessionWorkspace:s.workspace,sessionPage:B})}catch(r){await this.#j(t,r,{logLabel:"session list",replyTo:i})}}async#Re({chatId:t,key:o,replyTo:i=null},{updateMessageId:e=null}={}){try{let{current:a,paths:A}=await bp(this.#t,{signal:this.#$()});this.#ge(o,{kind:"workspaces",paths:A}),await this.#J(t,B6(A,a),{key:o,updateMessageId:e,replyTo:i})}catch(a){await this.#j(t,a,{logLabel:"workspace list",replyTo:i})}}async#$e(t,o,i,{updateMessageId:e=null,replyTo:a=null}={}){try{let A=await this.#t.bindWorkspaceSession(t,i),r=String(A?.title??"").replace(/\s+/gu," ").trim()||w("\u6682\u65E0\u6807\u9898");await this.#b(o,[w(`\u5DF2\u7ED1\u5B9A\u4F1A\u8BDD\u300C{title}\u300D ID\uFF1A{id}`,{title:r,id:A?.sessionId??i}),w("\u53D1\u9001 /history \u67E5\u770B\u6700\u8FD1\u5BF9\u8BDD\u3002")].join(` `),{replyTo:a}),await this.#X(t,o,{updateMessageId:e,replyTo:a})}catch(A){await this.#j(o,A,{logLabel:"session binding",replyTo:a,userMessage:w("\u7ED1\u5B9A\u5931\u8D25\uFF1A{message}",{message:Ou(A)})})}}async#eA(t,o,i,{updateMessageId:e=null,replyTo:a=null}={}){try{let A=await this.#t.switchWorkspace(i);await this.#b(o,w("\u5DE5\u4F5C\u533A\u5DF2\u5207\u6362\u4E3A\uFF1A{workspace}",{workspace:A}),{replyTo:a}),await this.#X(t,o,{updateMessageId:e,replyTo:a})}catch(A){await this.#j(o,A,{logLabel:"workspace switch",replyTo:a,userMessage:w("\u5207\u6362\u5931\u8D25\uFF1A{message}",{message:Ou(A)})})}}#Ce(t,o,i){if(!(!i.key||!t)&&(this.#K.delete(t),this.#K.set(t,{key:i.key,chatId:o,sessionWorkspace:typeof i.sessionWorkspace=="string"&&i.sessionWorkspace?i.sessionWorkspace:null,sessionPage:Number.isSafeInteger(i.sessionPage)&&i.sessionPage>=0?i.sessionPage:0}),this.#K.size>200)){let e=this.#K.keys().next().value;e!==void 0&&this.#K.delete(e)}}async#J(t,o,i={}){let e=lA(i.updateMessageId),a=lA(i.replyTo);if(e)try{let g=await this.#e.im.v1.message.patch({path:{message_id:e},data:{content:o}});if(g?.code&&g.code!==0)throw new Error(`Feishu card update failed: ${g.msg||g.code}`);return this.#Ce(e,t,i),e}catch(g){this.#O.warn?.("[dsh-feishu] card update failed:",g?.code??g?.message??g,"sending new")}let A=o;if(a)try{let g=await this.#e.im.v1.message.reply({path:{message_id:a},data:{msg_type:"interactive",content:A}});if(g?.code&&g.code!==0)throw new Error(`Feishu card reply failed: ${g.msg||g.code}`);let c=lA(g?.data?.message_id);if(c)return this.#Ce(c,t,i),c}catch(g){this.#O.warn?.("[dsh-feishu] threaded card reply failed; sending a plain card:",g?.message??String(g))}let r=await this.#e.im.v1.message.create({params:{receive_id_type:"chat_id"},data:{receive_id:t,msg_type:"interactive",content:o}});if(r?.code&&r.code!==0)throw new Error(`Feishu card send failed: ${r.msg||r.code}`);let s=lA(r?.data?.message_id);return this.#Ce(s,t,i),s}async#X(t,o,{updateMessageId:i=null,replyTo:e=null}={}){let a=null,A=null;try{let L=this.#r.sessionFor(t);if(typeof L=="string"&&L){a=L;let U=this.#t.workspaceSession?.(L);A=lA(U?.title)??lA(U?.name)??lA(U?.displayName)}}catch{}let r=this.#$(),s=bp(this.#t,{signal:r}).catch(()=>{let L=typeof this.#t.currentWorkspace=="function"?this.#t.currentWorkspace():null;return{current:L,paths:L?[L]:[]}}),g=(async()=>{let L=typeof this.#t.currentWorkspace=="function"?this.#t.currentWorkspace():null;if(!L||typeof this.#t.listWorkspaceSessions!="function")return[];try{let U=await this.#t.listWorkspaceSessions(L,{signal:r});return this.#ce(Array.isArray(U?.sessions)?U.sessions:[])}catch{return[]}})(),c=(async()=>{try{let L=await this.#t.agentPresetSettings({signal:r});return{...L.agentPresetCatalog,_currentId:L.agentPreset}}catch{return null}})(),I=(async()=>{try{if(a){let L=this.#t.workspaceSession?.(a);if(typeof L?.models=="function")return await L.models({signal:r})}return await this.#t.listModels({signal:r})}catch{return null}})(),[d,B,f,Q]=await Promise.all([s,g,c,I]),y=Array.isArray(d.paths)?d.paths:[],m=d.current??null,_=B.find(L=>L.sessionId===a),T=a?lA(_?.title)??lA(_?.name)??A??a:null,R=B.map(L=>({id:L.sessionId,title:L.title??L.name??L.sessionId})).slice(0,20);a&&(R=R.filter(L=>L.id!==a),R.unshift({id:a,title:T??a}),R=R.slice(0,20));let K=this.#r?.includesArchivedSessions?.()??!1;this.#ge(t,{kind:"menu",chatId:o}),await this.#J(o,I6({workspaces:y,currentWorkspace:m,currentSession:a?{id:a,title:T}:null,sessions:R,archiveVisible:K,presetCatalog:f,modelCatalog:Q}),{key:t,updateMessageId:i,replyTo:e})}async#yA(t,o){try{if(typeof this.#t.workspaceSession=="function"){let e=this.#t.workspaceSession(o);if(e&&typeof e=="object"){let a=lA(e.title)??lA(e.name)??lA(e.displayName);if(a)return a}}let i=typeof this.#t.currentWorkspace=="function"?this.#t.currentWorkspace():null;if(i&&typeof this.#t.listWorkspaceSessions=="function"){let e=await this.#t.listWorkspaceSessions(i),a=(Array.isArray(e?.sessions)?e.sessions:[]).find(A=>A.sessionId===o);if(a){let A=lA(a.title)??lA(a.name);if(A)return A}}}catch{}return null}async#AA(t,o,{updateMessageId:i=null}={}){try{let e=await this.#t.agentPresetSettings({signal:this.#$()}),a=e.agentPresetCatalog;a._currentId=e.agentPreset,await this.#J(o,l6(a),{key:t,updateMessageId:i})}catch(e){await this.#j(o,e,{logLabel:"preset card",replyTo:i})}}async#tA(t,o,{updateMessageId:i=null}={}){try{let e=this.#$();await this.#t.ensureRunning({signal:e});let a=this.#r?.sessionFor?.(t),A;if(typeof a=="string"&&a){let r=this.#t.workspaceSession(a);r?.models&&(A=await r.models({signal:e}))}A||(A=await this.#t.listModels({signal:e})),await this.#J(o,p6(A),{key:t,updateMessageId:i})}catch(e){await this.#j(o,e,{logLabel:"model card",replyTo:i})}}async#rA(t,o,i=null){try{await this.#t.ensureRunning({signal:this.#w});let e=[w("\u8FDE\u63A5\u6B63\u5E38")],a=typeof this.#t.currentWorkspace=="function"?this.#t.currentWorkspace():null;a&&e.push(w("\u5DE5\u4F5C\u533A\uFF1A{workspace}",{workspace:a}));let A=typeof this.#t.agentPresetSettings=="function"?await this.#t.agentPresetSettings({signal:this.#w}).catch(()=>null):null;if(A){let r=A.agentPresetCatalog?.items?.find(s=>s.id===A.agentPreset);e.push(w("\u9884\u8BBE\uFF1A{preset}",{preset:r?`${r.label}\uFF08${r.id}\uFF09`:A.agentPreset||w("\u8DDF\u968F\u9ED8\u8BA4")}))}await this.#b(o,e.join(` -`),{replyTo:i})}catch(e){await this.#j(o,e,{logLabel:"status text",replyTo:i})}}async#aA(t,o,{updateMessageId:i=null}={}){try{let e=this.#$();await this.#t.ensureRunning({signal:e});let a={connected:!0,workspace:null,preset:null,model:null,sessionCount:0};try{let A=typeof this.#t.currentWorkspace=="function"?this.#t.currentWorkspace():null;a.workspace=A||w("\u672A\u77E5")}catch{}try{let A=await this.#t.agentPresetSettings({signal:e}),r=A.agentPresetCatalog.items.find(s=>s.id===A.agentPreset);a.preset=r?`${r.label}\uFF08${r.id}\uFF09`:A.agentPreset||w("\u8DDF\u968F\u9ED8\u8BA4")}catch{}try{let A=this.#r?.sessionFor?.(t);if(typeof A=="string"&&A){let r=this.#t.workspaceSession(A);if(r?.models){let s=await r.models({signal:e});s.current&&(a.model=`${s.current.provider}/${s.current.model}`)}}}catch{}try{let A=typeof this.#t.currentWorkspace=="function"?this.#t.currentWorkspace():null;if(A){let r=await this.#t.listWorkspaceSessions(A,{signal:e});Array.isArray(r?.sessions)&&(a.sessionCount=r.sessions.length)}}catch{}await this.#J(o,d6(a),{key:t,updateMessageId:i})}catch(e){await this.#j(o,e,{logLabel:"status card",replyTo:i})}}async#iA(t,o,{updateMessageId:i=null}={}){await this.#J(o,E6(yke.map(e=>w(e))),{key:t,updateMessageId:i})}async#oA(t,o,i=null){try{let e=await bi("/compact",this.#t,this.#r,t,{signal:this.#w});await this.#b(o,e?.message||w("\u4E0A\u4E0B\u6587\u538B\u7F29\u5931\u8D25\u3002"),{replyTo:i})}catch(e){await this.#j(o,e,{logLabel:"compact",replyTo:i})}}async#nA(t,o,i=null){try{let e=await Mi("/stop",this.#t,this.#r,t,{signal:this.#w,control:{owner:this,key:t}});e?.stopped&&await Promise.allSettled([this.#be(t),this.#D.closeRoute(t)]),await this.#b(o,e?.message||w("/stop \u6267\u884C\u5B8C\u6210\u3002"),{replyTo:i})}catch(e){await this.#j(o,e,{logLabel:"stop",replyTo:i})}}async#sA(t,o,{updateMessageId:i=null}={}){let e=!!this.#r.sessionFor?.(t);this.#ge(t,{kind:"steer"}),await this.#J(o,m6({hasSession:e}),{key:t,updateMessageId:i})}async#ue(t,o){let{key:i,chatId:e}=t,a=await Mi(`/steer ${o}`,this.#t,this.#r,i,{signal:this.#w,pendingInteraction:this.#ne(i),control:{owner:this,key:i}});await this.#b(e,a?.message||w("\u5DF2\u63D0\u4EA4\u8865\u5145\u6307\u4EE4\u3002"),{replyTo:t.messageId??null})}async#ke(t,o,{updateMessageId:i=null,replyTo:e=null}={}){try{let a=await _i("/preset --default",this.#t,this.#r,t,{signal:this.#w});for(let A of a?.messages??[a?.message])A&&await this.#b(o,A,{replyTo:e})}catch(a){await this.#j(o,a,{logLabel:"preset reset",replyTo:i});return}try{await this.#X(t,o,{updateMessageId:i,replyTo:e})}catch(a){this.#O.warn?.("[dsh-feishu] menu refresh failed after preset reset:",a.message)}}async#gA(t,o,i,{updateMessageId:e=null,replyTo:a=null}={}){try{let A=/^\d+$/u.test(i)?`id:${i}`:i,r=await _i(`/preset ${A}`,this.#t,this.#r,t,{signal:this.#w});for(let s of r?.messages??[r?.message])s&&await this.#b(o,s,{replyTo:a})}catch(A){await this.#j(o,A,{logLabel:"preset selection",replyTo:e});return}try{await this.#X(t,o,{updateMessageId:e,replyTo:a})}catch(A){this.#O.warn?.("[dsh-feishu] menu refresh failed after preset select:",A.message)}}async#cA(t,o,i,{updateMessageId:e=null,replyTo:a=null}={}){try{let A=await ro(`/model ${i}`,this.#t,this.#r,t,{signal:this.#w,pendingInteraction:this.#ne(t),control:{owner:this,key:t}});for(let r of A?.messages??[A?.message])r&&await this.#b(o,r,{replyTo:a})}catch(A){await this.#j(o,A,{logLabel:"model selection",replyTo:e});return}try{await this.#X(t,o,{updateMessageId:e,replyTo:a})}catch(A){this.#O.warn?.("[dsh-feishu] menu refresh failed after model select:",A.message)}}#ve(){if(this.#q||typeof this.#t?.watchHarnessEvents!="function"||this.#w?.aborted)return;let t=this.#w??new AbortController().signal;try{this.#q=this.#t.watchHarnessEvents({signal:t,onSessionEvent:o=>this.#dA(o),onReconnect:()=>{this.#hA()}}),Promise.resolve(this.#q).catch(o=>{t.aborted||this.#O.warn?.("[dsh-feishu] event watcher stopped:",o.message)})}catch(o){this.#q=null,this.#O.warn?.("[dsh-feishu] event watcher failed to start:",o.message)}}#Pe(t,o){let i=this.#V.get(t)??Promise.resolve(),e;return e=i.then(o,o).catch(a=>{this.#w?.aborted||this.#O.warn?.("[dsh-feishu] completion event failed:",a.message)}).finally(()=>{this.#V.get(t)===e&&this.#V.delete(t)}),this.#V.set(t,e),e}#Be(t=Date.now()){let o=0;for(let[i,e]of this.#M){for(let[a,A]of e)(!Number.isSafeInteger(A?.arrivalAt)||A.arrivalAt<0||A.arrivalAt+gke<=t)&&e.delete(a);for(;e.size>Ike;){let a=e.keys().next().value;if(a===void 0)break;e.delete(a)}e.size===0?this.#M.delete(i):o+=e.size}for(;this.#M.size>cke;){let i=this.#M.keys().next().value;if(i===void 0)break;o-=this.#M.get(i)?.size??0,this.#M.delete(i)}for(;o>lke;){let i=this.#M.keys().next().value;if(i===void 0)break;let e=this.#M.get(i),a=e?.keys().next().value;if(a===void 0){this.#M.delete(i);continue}e.delete(a),o-=1,e.size===0&&this.#M.delete(i)}}#IA(t,o,i=Date.now()){this.#Be(i);let e=this.#M.get(t);e||(e=new Map);let a=o?.data?.reason,A=typeof a=="string"?a:typeof a?.kind=="string"?{kind:a.kind}:null;e.delete(o.seq),e.set(o.seq,{arrivalAt:i,event:{type:"turn/end",seq:o.seq,...Number.isSafeInteger(o.time)&&o.time>=0?{time:o.time}:{},data:{reason:A}}}),this.#M.delete(t),this.#M.set(t,e),this.#Be(i)}async#lA(t,{workspaceHint:o=null,signal:i=this.#w}={}){if(typeof t!="string"||t==="")return{error:w("\u7528\u6CD5\uFF1A/watch ")};let e=/^\d{1,4}$/.test(t)?Number(t):null,a=typeof this.#t?.currentWorkspace=="function"?this.#t.currentWorkspace():null,A=async s=>{let g=await this.#t.listWorkspaceSessions(s,{signal:i});return Array.isArray(g?.sessions)?g.sessions:[]};if(e!==null){if(!a)return{error:w("\u5F53\u524D\u673A\u5668\u4EBA\u6CA1\u6709\u53EF\u7528\u7684\u5DE5\u4F5C\u533A\uFF0C\u65E0\u6CD5\u6309\u5E8F\u53F7\u89E3\u6790\u4F1A\u8BDD\u3002")};let s=this.#ce(await A(a)),g=s[e-1];return g?.sessionId?{sessionId:g.sessionId,title:g.title??w("\u6682\u65E0\u6807\u9898"),workspace:a,...Mn(g.lastSeq)?{lastSeq:g.lastSeq}:{}}:{error:w("\u5F53\u524D\u5DE5\u4F5C\u533A\u53EA\u6709 {count} \u4E2A\u4F1A\u8BDD\u3002",{count:s.length})}}let r;if(lA(o))r=[o];else{let s=typeof this.#t?.listWorkspaces=="function"?(await this.#t.listWorkspaces({signal:i})).filter(g=>g!==a):[];r=[a,...s].filter(Boolean)}for(let s of r){let c=(await A(s)).find(I=>I.sessionId===t);if(c)return{sessionId:t,title:c.title??w("\u6682\u65E0\u6807\u9898"),workspace:s,...Mn(c.lastSeq)?{lastSeq:c.lastSeq}:{}}}return{error:w("\u6CA1\u6709\u627E\u5230\u8FD9\u4E2A\u4F1A\u8BDD\uFF0C\u8BF7\u7528 /sessionlist \u67E5\u770B\u53EF\u7528\u4F1A\u8BDD\u3002")}}async#pA(t){let o=lA(t)??(typeof this.#t?.currentWorkspace=="function"?lA(this.#t.currentWorkspace()):null);if(!o||typeof this.#t?.listWorkspaceSessions!="function")return new Map;let i=await this.#t.listWorkspaceSessions(o,{signal:this.#$()});return new Map(this.#ce(Array.isArray(i?.sessions)?i.sessions:[]).filter(e=>lA(e?.sessionId)).map(e=>[e.sessionId,{sessionId:e.sessionId,title:e.title??e.name??w("\u6682\u65E0\u6807\u9898"),workspace:o,...Mn(e.lastSeq)?{lastSeq:e.lastSeq}:{}}]))}#Ge(t){let o=this.#z.get(t);if(o)return o.requested=!0,o.promise;let i={requested:!0,promise:null};return this.#z.set(t,i),i.promise=Promise.resolve().then(async()=>{try{do i.requested=!1,await this.#Pe(t,()=>this.#Le(t));while(i.requested&&!this.#w?.aborted)}finally{this.#z.get(t)===i&&this.#z.delete(t)}}),i.promise}async#fe(t,o,i,{notify:e=!0,validatedTarget:a=null,workspaceHint:A=null,replyTo:r=null}={}){let s=Date.now(),g=async y=>{e&&await this.#b(o,y,{replyTo:r}).catch(m=>{this.#O.warn?.("[dsh-feishu] watch notification failed:",m.message)})};if(this.#ve(),typeof this.#r?.setWatch!="function")return await g(w("\u5F53\u524D\u72B6\u6001\u5B58\u50A8\u4E0D\u652F\u6301\u5173\u6CE8\u3002")),{ok:!1,changed:!1,reason:"unsupported"};let c;try{c=a?.sessionId===i?a:await this.#lA(i,{workspaceHint:A,signal:A?this.#$():this.#w})}catch(y){return await g(w("\u65E0\u6CD5\u89E3\u6790\u4F1A\u8BDD\uFF1A{message}",{message:Ou(y)})),{ok:!1,changed:!1,reason:"resolve"}}if(c.error)return await g(c.error),{ok:!1,changed:!1,reason:"not-found"};let I=this.#r.watchEntries?.(t)??[],d=I.find(y=>y.sessionId===c.sessionId);if(!d&&I.length>=nS)return await g(w("\u6BCF\u4E2A\u804A\u5929\u6700\u591A\u5173\u6CE8 {count} \u4E2A\u4F1A\u8BDD\u3002",{count:nS})),{ok:!1,changed:!1,reason:"limit"};let B=Mn(d?.lastSeq)?d.lastSeq:Mn(c.lastSeq)?c.lastSeq:null,Q=lS(d)??(d?null:s);try{await this.#r.setWatch(t,{sessionId:c.sessionId,title:c.title,chatId:o,lastSeq:B,...Q!==null?{watchStartedAt:Q}:{},...r?{replyToMessageId:r}:d?.replyToMessageId?{replyToMessageId:d.replyToMessageId}:{}})}catch(y){return await g(w("\u5173\u6CE8\u5931\u8D25\uFF1A{message}",{message:Ou(y)})),{ok:!1,changed:!1,reason:"persist"}}return(!d||Q!==null||!Mn(B))&&this.#Ge(c.sessionId),await g(w("\u5DF2\u5173\u6CE8\u4F1A\u8BDD\u300C{title}\u300D\uFF0C\u4EFB\u52A1\u5B8C\u6210\u4F1A\u63A8\u9001\u7ED3\u679C\u3002",{title:String(c.title).replace(/\s+/gu," ")})),{ok:!0,changed:!d,entry:this.#r.watchEntry?.(t,c.sessionId)}}async#Qe(t,o,i,{notify:e=!0,replyTo:a=null}={}){let A=async g=>{e&&await this.#b(o,g,{replyTo:a}).catch(c=>{this.#O.warn?.("[dsh-feishu] unwatch notification failed:",c.message)})};if(typeof this.#r?.removeWatch!="function")return{ok:!1,changed:!1,reason:"unsupported"};let r=this.#r.watchEntries?.(t)??[],s=typeof i=="string"&&/^\d{1,4}$/.test(i)?r[Number(i)-1]:r.find(g=>g.sessionId===i);if(!s)return await A(w("\u5173\u6CE8\u5217\u8868\u91CC\u6CA1\u6709\u8FD9\u4E2A\u4F1A\u8BDD\uFF0C\u56DE\u590D /watchlist \u67E5\u770B\u3002")),{ok:!0,changed:!1,reason:"absent"};try{await this.#r.removeWatch(t,s.sessionId),this.#Y.delete(`${t}\0${s.sessionId}`)}catch(g){return await A(w("\u53D6\u6D88\u5931\u8D25\uFF1A{message}",{message:Ou(g)})),{ok:!1,changed:!1,reason:"persist"}}return await A(w("\u5DF2\u53D6\u6D88\u5173\u6CE8\u300C{title}\u300D\u3002",{title:String(s.title??"").replace(/\s+/gu," ")})),{ok:!0,changed:!0,entry:s}}async#ye(t,o,{updateMessageId:i=null,replyTo:e=null}={}){let a=this.#r.watchEntries?.(t)??[],A=[],r=null;try{if(r=typeof this.#t?.currentWorkspace=="function"?this.#t.currentWorkspace():null,r&&typeof this.#t?.listWorkspaceSessions=="function"){let s=await this.#t.listWorkspaceSessions(r,{signal:this.#$()});A=this.#ce(Array.isArray(s?.sessions)?s.sessions:[]).map(g=>({sessionId:g.sessionId,title:g.title??g.name??g.sessionId}))}}catch{}this.#ge(t,{kind:"watches",entries:a}),await this.#J(o,f6(a,A),{key:t,updateMessageId:i,replyTo:e,sessionWorkspace:r})}#dA({sessionId:t,event:o}){this.#w?.aborted||!t||!o||typeof o!="object"||o.type!=="turn/end"||!_u(o.seq)||(this.#IA(t,o),this.#Pe(t,async()=>{let i=this.#r.keysWatching?.(t)??[],e=i.some(A=>IS(this.#r.watchEntry?.(A,t))),a=i.some(A=>this.#Y.has(`${A}\0${t}`));(e||a)&&await this.#Le(t),await this.#Fe(t,o)}))}async#Fe(t,o,{keys:i=null}={}){if(this.#w?.aborted||typeof this.#r?.keysWatching!="function")return;let e=o?.data?.reason?.kind??o?.data?.reason??null,a=i??this.#r.keysWatching(t);for(let A of a){if(this.#w?.aborted)return;let r=this.#r.watchEntry?.(A,t),s=`${A}\0${t}`,g=this.#Y.get(s);if(_u(g)&&Mn(r?.lastSeq)&&r.lastSeq>=g&&(this.#Y.delete(s),g=void 0),!(!r?.chatId||IS(r)||Mn(r.lastSeq)&&r.lastSeq>=o.seq||_u(g)&&o.seq>g))try{await this.#J(r.chatId,Q6(t,r.title,e),{key:A,replyTo:r.replyToMessageId??null});let c=this.#r.watchEntry?.(A,t);if(!c||c.chatId!==r.chatId||IS(c)||Mn(c.lastSeq)&&c.lastSeq>=o.seq)continue;await this.#r.setWatch(A,{...c,lastSeq:o.seq}),g===o.seq&&this.#Y.delete(s)}catch(c){this.#Y.set(s,_u(g)?Math.min(g,o.seq):o.seq),this.#O.warn?.("[dsh-feishu] completion push failed:",c.message)}}}async#Le(t){if(!(this.#w?.aborted||typeof this.#t?.rpc!="function"))try{let o=await this.#t.rpc("session.history",{sessionId:t,maxMessages:20},3e4,{signal:this.#w}),i=Ske(o);this.#Be();let e=this.#M.get(t)??new Map,a=new Map(i.map(g=>[g.seq,g]));for(let[g,c]of e)a.has(g)||a.set(g,c.event);let A=[...a.values()].sort((g,c)=>g.seq-c.seq),r=A.at(-1)?.seq??-1,s=typeof this.#r?.keysWatching=="function"?this.#r.keysWatching(t):[];for(let g of s){let c=this.#r.watchEntry?.(g,t);if(!IS(c))continue;let I=lS(c),d=Mn(c.lastSeq)?c.lastSeq:-1,B=I===null?null:A.find(_=>{if(_.seq<=d)return!1;let T=Number.isSafeInteger(_.time)&&_.time>=0?_.time:e.get(_.seq)?.arrivalAt;return Number.isSafeInteger(T)&&T>=I}),f=B?Math.max(d,B.seq-1):Math.max(d,r),Q=this.#r.watchEntry?.(g,t);if(!Q||Q.chatId!==c.chatId||Q.lastSeq!==c.lastSeq||lS(Q)!==I)continue;let{watchStartedAt:y,...m}=Q;await this.#r.setWatch(g,{...m,lastSeq:f})}for(let g of A)g.type==="turn/end"&&await this.#Fe(t,g,{keys:s})}catch(o){this.#w?.aborted||this.#O.warn?.(`[dsh-feishu] watch compensation failed for ${t}:`,o.message)}}async#hA(){let t=typeof this.#r?.watchedSessionIds=="function"?this.#r.watchedSessionIds():[];this.#w?.aborted||await Promise.allSettled(t.map(o=>this.#Ge(o)))}#me(t,o,i){return{timeoutMs:this.#T,signal:this.#w,control:{owner:this,key:o},onInteraction:e=>this.#uA(e,{key:o,actor:xd(t),chatId:t.message.chat_id,requiresMention:t.message.chat_type!=="p2p",replyToMessageId:t.message.message_id}),onInteractionResolved:e=>this.#BA(e),files:i}}async#Se(t,o,{deliveryId:i,presentation:e,replyTo:a=null}){let A=[];for(let r of s6(o)){this.#w?.throwIfAborted();let s=await this.#b(t,r,{replyTo:a});s&&A.push(s)}return Zt({deliveryId:i,presentation:e,providerMessageIds:A})}async#de(t,o,i=[],e){let a=await Po({artifacts:i,baseReceipt:e,deliveryId:e?.deliveryId??i[0]?.deliveryKey??o,aggregatePresentation:e?"feishu-text-and-files":"feishu-files",channelKey:"feishu",signal:this.#w,sendImage:typeof this.#A?.sendImage=="function"?A=>this.#A.sendImage(t,A,{replyTo:o,signal:this.#w}):void 0,sendFile:typeof this.#A?.sendFile=="function"?A=>this.#A.sendFile(t,A,{replyTo:o,signal:this.#w}):void 0,onFailure:(A,r)=>tt(this.#E,r,{userMessage:mke(A?.fileName,r),reason:r?.code}),sendFailureNotice:async(A,r,s)=>({messageId:await this.#b(t,ht(s))}),logger:this.#O});return this.#E.artifactsSent=(this.#E.artifactsSent??0)+a.artifactsSent,this.#E.artifactSendErrors=(this.#E.artifactSendErrors??0)+a.artifactSendErrors,a.receipt?{receipt:a.receipt,failureNoticeVisible:a.failureNoticeVisible,artifactSendErrors:a.artifactSendErrors}:{receipt:Zt({deliveryId:o,presentation:"feishu-files"}),failureNoticeVisible:a.failureNoticeVisible,artifactSendErrors:a.artifactSendErrors}}async#EA(t,o,i,{onAskComplete:e}={}){let a=t.message.chat_id,A=t.message.message_id,r=i.content,s=!1,g=()=>{s||(s=!0,e?.())},c=Tt(i)?await bo(i,{signal:this.#w}):void 0,I=this.#c.get(A),d=!1;if(I){let _=c??r;c=No(_,I,()=>({channel:"feishu",senderId:xd(t)})),d=c!==_}if(!this.#A?.stream){let{answer:_,artifacts:T=[]}=await Oi({harness:this.#t,state:this.#r,key:o,text:r,content:c,contextEnhanced:d,createOptions:{signal:this.#w},existsOptions:{signal:this.#w},askOptions:this.#me(t,o,i.files)});g();let R,K=null;try{R=await this.#Se(a,cS(_,T),{deliveryId:A,presentation:"feishu-text",replyTo:A})}catch(z){K=Pa(z),this.#O.warn?.("[dsh-feishu] final text delivery failed; continuing with result files:",z)}let L=await this.#de(a,A,T,R),U=L.receipt.artifacts.some(({outcome:z})=>z==="sent"||z==="unknown");if(K&&!U&&!L.failureNoticeVisible)throw K;return K&&L.artifactSendErrors===0&&tt(this.#E,K),this.#E.streamFallbacks=(this.#E.streamFallbacks??0)+1,{...L,textDeliveryErrors:K?1:0}}let B=!1,f="",Q=[],y;try{y=await this.#A.stream(a,{markdown:async _=>{B=!0;let T=this.#me(t,o,i.files),R={...T,onInteraction:async L=>{(L?.kind==="question"||L?.kind==="approval")&&typeof _?.rotate=="function"&&await _.rotate(),await T.onInteraction(L)},onUpdate:async L=>{await _.setContent(this.#fA(L)),this.#E.streamUpdates=(this.#E.streamUpdates??0)+1}},K=await Oi({harness:this.#t,state:this.#r,key:o,text:r,content:c,contextEnhanced:d,createOptions:{signal:this.#w},existsOptions:{signal:this.#w},askOptions:R});g(),f=K.answer,Q=K.artifacts??[],await _.setContent(cS(f,Q))}},{replyTo:A})}catch(_){if(this.#E.streamErrors=(this.#E.streamErrors??0)+1,f||Q.length>0){this.#O.warn?.("[dsh-feishu] native stream failed after generation; sending final text:",_.message);let q,x=null;try{q=await this.#Se(a,cS(f,Q),{deliveryId:A,presentation:"feishu-text-fallback",replyTo:A})}catch(F){x=Pa(F),this.#O.warn?.("[dsh-feishu] fallback text delivery failed; continuing with result files:",F)}let G=await this.#de(a,A,Q,q),V=G.receipt.artifacts.some(({outcome:F})=>F==="sent"||F==="unknown");if(x&&!V&&!G.failureNoticeVisible)throw x;return x&&G.artifactSendErrors===0&&tt(this.#E,x),this.#E.streamFallbacks=(this.#E.streamFallbacks??0)+1,{...G,textDeliveryErrors:x?1:0}}if(B)throw _;this.#O.warn?.("[dsh-feishu] native stream unavailable; using text fallback:",_.message);let{answer:T,artifacts:R=[]}=await Oi({harness:this.#t,state:this.#r,key:o,text:r,content:c,contextEnhanced:d,createOptions:{signal:this.#w},existsOptions:{signal:this.#w},askOptions:this.#me(t,o,i.files)});g();let K,L=null;try{K=await this.#Se(a,cS(T,R),{deliveryId:A,presentation:"feishu-text-fallback",replyTo:A})}catch(q){L=Pa(q),this.#O.warn?.("[dsh-feishu] fallback text delivery failed; continuing with result files:",q)}let U=await this.#de(a,A,R,K),z=U.receipt.artifacts.some(({outcome:q})=>q==="sent"||q==="unknown");if(L&&!z&&!U.failureNoticeVisible)throw L;return L&&U.artifactSendErrors===0&&tt(this.#E,L),this.#E.streamFallbacks=(this.#E.streamFallbacks??0)+1,{...U,textDeliveryErrors:L?1:0}}let m=await this.#de(a,A,Q,Zt({deliveryId:A,presentation:"feishu-cardkit",providerMessageIds:Ua(y)}));return this.#E.streamResponses=(this.#E.streamResponses??0)+1,m}async#CA(t,o,i,e,a){this.#w?.throwIfAborted();let A=this.#n.get(i),r=e.claimedReplyMessageId===o;if(!A||A!==e||A.submitting)return this.#we(t,i)?this.#De(t,o):r&&(!A||A!==e)?this.#De(t,o):this.#x(t,o,i,a,{releaseMessageId:!1,finalize:!1});if(this.#r.hasSeen(o))return;await this.#r.markSeen(o),this.#E.lastMessageAt=new Date().toISOString(),this.#E.messagesReceived+=1;let s=Hd(t);if(!s){await this.#b(t.message.chat_id,w("\u8BF7\u7528\u6587\u5B57\u56DE\u7B54\u5F53\u524D\u95EE\u9898\u3002"));return}let g=this.#n.get(i);if(!g||g!==e||g.submitting){if(this.#we(t,i)){await this.#b(t.message.chat_id,Du(),{replyTo:t.message.message_id}).catch(()=>{});return}if(r&&(!g||g!==e)){await this.#b(t.message.chat_id,Du(),{replyTo:t.message.message_id});return}return this.#x(t,o,i,a,{releaseMessageId:!1,alreadyRecorded:!0,finalize:!1})}if(g.chatId=t.message.chat_id,g.needsPresentation){try{await this.#he(g)}catch{this.#E.lastError="\u98DE\u4E66\u4EA4\u4E92\u95EE\u9898\u53D1\u9001\u5931\u8D25\u3002",this.#O.error?.("[dsh-feishu] failed to retry an interaction question"),g.interaction.reconnect?.()}return}g.questions[g.index]&&await this.#Ue(g,s,{chatId:t.message.chat_id,messageId:o})}async#Ue(t,o,{chatId:i,messageId:e}={}){let a=t.questions[t.index];if(!a)return;if(t.chatId=i??t.chatId,t.answers.push(wi(a,o)),t.index+=1,t.index{});return}t.submitting=!1,t.answers.pop(),t.index-=1,this.#E.lastError="\u56DE\u7B54\u63D0\u4EA4\u5931\u8D25\u3002",this.#O.error?.("[dsh-feishu] failed to answer a Harness interaction"),i&&await this.#b(i,w("\u56DE\u7B54\u63D0\u4EA4\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u5F53\u524D\u95EE\u9898\u7684\u7B54\u6848\u3002")).catch(()=>{})}}async#uA(t,{key:o,actor:i,chatId:e,requiresMention:a,replyToMessageId:A}){if(await this.#D.handleRequested(t,{key:o,actor:i,requiresMention:a,send:I=>this.#b(e,I,{replyTo:A}),...this.#Ae?{render:async I=>{await this.#J(e,S6({toolName:I.toolCall?.name??I.payload?.toolName,operation:Bke(I.toolCall),reason:I.payload?.reason,approvalId:I.approvalId}),{key:o,replyTo:A}).catch(()=>this.#b(e,I.text,{replyTo:A}).catch(()=>{}))}}:{}})||t?.kind!=="question")return;let r=t?.payload?.questions,s=typeof t?.interactionId=="string"?t.interactionId:t?.rpcId;if(typeof t.rpcId!="string"||typeof s!="string"||typeof t.sessionId!="string"||!Array.isArray(r)||r.length===0||r.some(I=>!Si(I))){this.#O.warn?.("[dsh-feishu] ignored an invalid Harness question interaction");return}if(t.recovered===!0){await t.respond({ok:!1,error:{code:"cancelled",message:"Feishu safely cancelled an interaction left by an earlier client.",details:{}}}),await this.#b(e,w("\u68C0\u6D4B\u5230\u8FD9\u4E2A Session \u4E2D\u9057\u7559\u7684\u5F85\u56DE\u7B54\u95EE\u9898\uFF0C\u5DF2\u5B89\u5168\u53D6\u6D88\u5E76\u7EE7\u7EED\u5904\u7406\u4F60\u521A\u624D\u7684\u6D88\u606F\u3002"),{replyTo:A}).catch(()=>{});return}let g=this.#n.get(o);if(g?.interactionId===s){g.interaction=t,g.needsPresentation&&await this.#he(g);return}if(this.#s.has(s))return;if(g){await t.respond({ok:!1,error:{code:"cancelled",message:"Feishu is already handling another user interaction.",details:{}}});return}let c={kind:"question",interactionId:s,sessionId:t.sessionId,interaction:t,key:o,actor:i,requiresMention:a,questions:r,answers:[],index:0,chatId:e,replyToMessageId:A,queue:null,claimedReplyMessageId:null,submitting:!1,needsPresentation:!0,questionMessageIds:new Set,inactive:!1};this.#n.set(o,c),this.#s.set(c.interactionId,o),await this.#he(c)}async#BA(t){if(await this.#D.handleResolved(t))return;let o=t?.interactionId;if(t?.kind!=="question"||typeof o!="string")return;let i=this.#s.get(o);if(!i)return;let e=this.#n.get(i);e&&this.#Ie(i,e),this.#Oe(i,o)}async#he(t){let o=t.questions[t.index];if(!o)return;let i=Array.isArray(o?.options)?o.options:[],e=this.#Ae&&i.length>0&&o.multiSelect!==!0,a;e?a=await this.#J(t.chatId,w6({interactionId:t.interactionId,header:o.header,question:o.question,detail:o.detail,options:i,index:t.index,total:t.questions.length}),{key:t.key,replyTo:t.replyToMessageId}).catch(()=>this.#b(t.chatId,ni(o,t.index,t.questions.length,{requiresMention:t.requiresMention}),{replyTo:t.replyToMessageId}).catch(()=>{})):a=await this.#b(t.chatId,ni(o,t.index,t.questions.length,{requiresMention:t.requiresMention}),{replyTo:t.replyToMessageId}),a&&(t.questionMessageIds.add(a),t.inactive&&this.#Ie(t.key,t)),t.needsPresentation=!1}#Ie(t,o){let i=Date.now()+eke;for(let e of o.questionMessageIds??[])this.#p.set(e,{key:t,expiresAt:i})}#we(t,o){let i=Date.now();for(let[e,a]of this.#p)a.expiresAt<=i&&this.#p.delete(e);for(let e of[t?.message?.parent_id,t?.message?.root_id]){let a=this.#p.get(e);if(a?.key===o&&a.expiresAt>i)return!0}return!1}async#De(t,o){this.#r.hasSeen(o)||(await this.#r.markSeen(o),this.#E.lastMessageAt=new Date().toISOString(),this.#E.messagesReceived+=1,await this.#b(t.message.chat_id,Du(),{replyTo:t.message.message_id}).catch(()=>{}))}#He(t,o){let i=this.#n.get(t);return!i||o!==void 0&&i.interactionId!==o?null:(this.#n.delete(t),this.#s.delete(i.interactionId),i.inactive=!0,i)}#Oe(t,o){return this.#He(t,o)!==null}async#be(t){let o=this.#He(t);if(!(!o||o.kind!=="question")){this.#Ie(t,o);try{await o.interaction.respond({ok:!1,error:{code:"cancelled",message:"The Feishu interaction ended before the user answered.",details:{}}},{signal:AbortSignal.timeout(5e3)})}catch(i){i?.code!=="interaction-not-pending"&&this.#O.warn?.("[dsh-feishu] failed to cancel a pending Harness interaction")}}}#fA(t){return t.type==="text"&&t.text?t.text:t.type==="tool"?t.name==="web_search"?w("_\u6B63\u5728\u641C\u7D22\u7F51\u7EDC\u5E76\u6574\u7406\u4FE1\u606F\u2026_"):w("_\u6B63\u5728\u4F7F\u7528 {name}\u2026_",{name:t.name||w("\u5DE5\u5177")}):w("_{text}_",{text:t.text||w("\u6B63\u5728\u5904\u7406\u2026")})}#QA(t){return qQ({adapter:this.#A,target:t,reactions:{processing:"OnIt",success:"DONE",error:"ERROR"},status:this.#E,logger:this.#O,label:"feishu"})}#Ke(t,o){o.clear()}#re(t,o,i){i==="ERROR"?o.error():o.success()}async#b(t,o,{replyTo:i}={}){let e=JSON.stringify({text:o});if(i)try{let A=await this.#e.im.v1.message.reply({path:{message_id:i},data:{msg_type:"text",content:e}});if(A?.code&&A.code!==0)throw new Error(`Feishu reply failed: ${A.msg||A.code}`);return lA(A?.data?.message_id)}catch(A){this.#O.warn?.("[dsh-feishu] threaded reply failed; falling back to a plain message:",A?.message??String(A))}let a=await this.#e.im.v1.message.create({params:{receive_id_type:"chat_id"},data:{receive_id:t,msg_type:"text",content:e}});if(a?.code&&a.code!==0)throw new Error(`Feishu send failed: ${a.msg||a.code}`);return lA(a?.data?.message_id)}};import{createHash as bke}from"node:crypto";var G6="stream_md",Mke="\u5DF2\u8FDE\u63A5 DeepSeek Harness\uFF0C\u6B63\u5728\u601D\u8003\u2026",Nu=28e3,dS=12e4,F6=new Map([[99991672,["artifact-permission-required","Feishu file delivery requires the im:resource permission."]],[234006,["artifact-too-large","The result file exceeds Feishu's size limit."]],[234010,["artifact-empty","Feishu does not accept empty files."]],[230017,["artifact-provider-rejected","Feishu rejected the uploaded file ownership."]],[230020,["artifact-rate-limited","Feishu temporarily rate-limited file delivery."]],[230049,["artifact-delivery-uncertain","Feishu could not confirm the file message result."]],[230055,["artifact-provider-rejected","Feishu rejected the file message type."]]]);function Cl(n,t){if(t?.code&&t.code!==0)throw new Error(`${n} failed: ${t.msg||t.code}`);return t}function L6(n){let t=[n],o=new Set,i;for(;t.length>0;){let e=t.shift();if(!e||o.has(e))continue;if(typeof e=="object"&&o.add(e),Array.isArray(e)){t.push(...e);continue}let a=Number(e?.code);if(Number.isFinite(a)&&a!==0){if(F6.has(a))return a;i??=a}t.push(e?.response?.data,e?.data,e?.error,e?.cause)}return i}function Tu(n,t,o,{uncertain:i=!1}={}){let e=o==null?void 0:Number(o),a=Number.isFinite(e)&&e!==0?e:L6(t),A=Number.isFinite(a)?["artifact-provider-rejected",`Feishu rejected file ${n}.`]:i?["artifact-delivery-uncertain","Feishu could not confirm the file message result."]:["artifact-provider-failed",`Feishu file ${n} failed.`],[r,s]=F6.get(a)??A,g=new Error(s,{cause:t});return g.code=r,Number.isFinite(a)&&(g.providerCode=a),g}function v6(n,t){if(!Number.isInteger(n)||n<1||n>dS)throw new TypeError(`${t} must be an integer between 1 and ${dS}`);return n}function uP(n){return n?.reason??new DOMException("The operation was aborted","AbortError")}function _ke(n){let t=new Error(`Feishu file ${n} timed out.`);return t.code="provider-timeout",t}function P6(n,{signal:t,timeoutMs:o,stage:i}){t?.throwIfAborted();let e=new AbortController,a=t?AbortSignal.any([t,e.signal]):e.signal;return new Promise((A,r)=>{let s=!1,g=(d,B)=>{s||(s=!0,clearTimeout(I),a.removeEventListener("abort",c),d(B))},c=()=>g(r,t?.aborted?uP(t):_ke(i)),I=setTimeout(()=>e.abort(),o);a.addEventListener("abort",c,{once:!0}),Promise.resolve().then(()=>n(a)).then(d=>g(A,d),d=>g(r,d)),a.aborted&&c()})}function Tke(n,t,o){let i=`${n.deliveryKey}\0${t}`;return`dshim_${bke("sha256").update(o==="file"?i:`${i}\0${o}`).digest("hex").slice(0,40)}`}function Nke(n){let t=String(n??"").replace(/\s+/g," ").trim();return t.length<=50?t:`${BP(t,49)}\u2026`}function BP(n,t){let o=Math.min(n.length,t),i=n.charCodeAt(o-1),e=n.charCodeAt(o);return i>=55296&&i<=56319&&e>=56320&&e<=57343&&(o-=1),n.slice(0,o)}function CP(n){if(n.length<=Nu)return n;let t=` +`),{replyTo:i})}catch(e){await this.#j(o,e,{logLabel:"status text",replyTo:i})}}async#aA(t,o,{updateMessageId:i=null}={}){try{let e=this.#$();await this.#t.ensureRunning({signal:e});let a={connected:!0,workspace:null,preset:null,model:null,sessionCount:0};try{let A=typeof this.#t.currentWorkspace=="function"?this.#t.currentWorkspace():null;a.workspace=A||w("\u672A\u77E5")}catch{}try{let A=await this.#t.agentPresetSettings({signal:e}),r=A.agentPresetCatalog.items.find(s=>s.id===A.agentPreset);a.preset=r?`${r.label}\uFF08${r.id}\uFF09`:A.agentPreset||w("\u8DDF\u968F\u9ED8\u8BA4")}catch{}try{let A=this.#r?.sessionFor?.(t);if(typeof A=="string"&&A){let r=this.#t.workspaceSession(A);if(r?.models){let s=await r.models({signal:e});s.current&&(a.model=`${s.current.provider}/${s.current.model}`)}}}catch{}try{let A=typeof this.#t.currentWorkspace=="function"?this.#t.currentWorkspace():null;if(A){let r=await this.#t.listWorkspaceSessions(A,{signal:e});Array.isArray(r?.sessions)&&(a.sessionCount=r.sessions.length)}}catch{}await this.#J(o,d6(a),{key:t,updateMessageId:i})}catch(e){await this.#j(o,e,{logLabel:"status card",replyTo:i})}}async#iA(t,o,{updateMessageId:i=null}={}){await this.#J(o,E6(yke.map(e=>w(e))),{key:t,updateMessageId:i})}async#oA(t,o,i=null){try{let e=await bi("/compact",this.#t,this.#r,t,{signal:this.#w});await this.#b(o,e?.message||w("\u4E0A\u4E0B\u6587\u538B\u7F29\u5931\u8D25\u3002"),{replyTo:i})}catch(e){await this.#j(o,e,{logLabel:"compact",replyTo:i})}}async#nA(t,o,i=null){try{let e=await Mi("/stop",this.#t,this.#r,t,{signal:this.#w,control:{owner:this,key:t}});e?.stopped&&await Promise.allSettled([this.#be(t),this.#D.closeRoute(t)]),await this.#b(o,e?.message||w("/stop \u6267\u884C\u5B8C\u6210\u3002"),{replyTo:i})}catch(e){await this.#j(o,e,{logLabel:"stop",replyTo:i})}}async#sA(t,o,{updateMessageId:i=null}={}){let e=!!this.#r.sessionFor?.(t);this.#ge(t,{kind:"steer"}),await this.#J(o,m6({hasSession:e}),{key:t,updateMessageId:i})}async#ue(t,o){let{key:i,chatId:e}=t,a=await Mi(`/steer ${o}`,this.#t,this.#r,i,{signal:this.#w,pendingInteraction:this.#ne(i),control:{owner:this,key:i}});await this.#b(e,a?.message||w("\u5DF2\u63D0\u4EA4\u8865\u5145\u6307\u4EE4\u3002"),{replyTo:t.messageId??null})}async#ke(t,o,{updateMessageId:i=null,replyTo:e=null}={}){try{let a=await _i("/preset --default",this.#t,this.#r,t,{signal:this.#w});for(let A of a?.messages??[a?.message])A&&await this.#b(o,A,{replyTo:e})}catch(a){await this.#j(o,a,{logLabel:"preset reset",replyTo:i});return}try{await this.#X(t,o,{updateMessageId:i,replyTo:e})}catch(a){this.#O.warn?.("[dsh-feishu] menu refresh failed after preset reset:",a.message)}}async#gA(t,o,i,{updateMessageId:e=null,replyTo:a=null}={}){try{let A=/^\d+$/u.test(i)?`id:${i}`:i,r=await _i(`/preset ${A}`,this.#t,this.#r,t,{signal:this.#w});for(let s of r?.messages??[r?.message])s&&await this.#b(o,s,{replyTo:a})}catch(A){await this.#j(o,A,{logLabel:"preset selection",replyTo:e});return}try{await this.#X(t,o,{updateMessageId:e,replyTo:a})}catch(A){this.#O.warn?.("[dsh-feishu] menu refresh failed after preset select:",A.message)}}async#cA(t,o,i,{updateMessageId:e=null,replyTo:a=null}={}){try{let A=await ro(`/model ${i}`,this.#t,this.#r,t,{signal:this.#w,pendingInteraction:this.#ne(t),control:{owner:this,key:t}});for(let r of A?.messages??[A?.message])r&&await this.#b(o,r,{replyTo:a})}catch(A){await this.#j(o,A,{logLabel:"model selection",replyTo:e});return}try{await this.#X(t,o,{updateMessageId:e,replyTo:a})}catch(A){this.#O.warn?.("[dsh-feishu] menu refresh failed after model select:",A.message)}}#ve(){if(this.#q||typeof this.#t?.watchHarnessEvents!="function"||this.#w?.aborted)return;let t=this.#w??new AbortController().signal;try{this.#q=this.#t.watchHarnessEvents({signal:t,onSessionEvent:o=>this.#dA(o),onReconnect:()=>{this.#hA()}}),Promise.resolve(this.#q).catch(o=>{t.aborted||this.#O.warn?.("[dsh-feishu] event watcher stopped:",o.message)})}catch(o){this.#q=null,this.#O.warn?.("[dsh-feishu] event watcher failed to start:",o.message)}}#Pe(t,o){let i=this.#V.get(t)??Promise.resolve(),e;return e=i.then(o,o).catch(a=>{this.#w?.aborted||this.#O.warn?.("[dsh-feishu] completion event failed:",a.message)}).finally(()=>{this.#V.get(t)===e&&this.#V.delete(t)}),this.#V.set(t,e),e}#Be(t=Date.now()){let o=0;for(let[i,e]of this.#M){for(let[a,A]of e)(!Number.isSafeInteger(A?.arrivalAt)||A.arrivalAt<0||A.arrivalAt+gke<=t)&&e.delete(a);for(;e.size>Ike;){let a=e.keys().next().value;if(a===void 0)break;e.delete(a)}e.size===0?this.#M.delete(i):o+=e.size}for(;this.#M.size>cke;){let i=this.#M.keys().next().value;if(i===void 0)break;o-=this.#M.get(i)?.size??0,this.#M.delete(i)}for(;o>lke;){let i=this.#M.keys().next().value;if(i===void 0)break;let e=this.#M.get(i),a=e?.keys().next().value;if(a===void 0){this.#M.delete(i);continue}e.delete(a),o-=1,e.size===0&&this.#M.delete(i)}}#IA(t,o,i=Date.now()){this.#Be(i);let e=this.#M.get(t);e||(e=new Map);let a=o?.data?.reason,A=typeof a=="string"?a:typeof a?.kind=="string"?{kind:a.kind}:null;e.delete(o.seq),e.set(o.seq,{arrivalAt:i,event:{type:"turn/end",seq:o.seq,...Number.isSafeInteger(o.time)&&o.time>=0?{time:o.time}:{},data:{reason:A}}}),this.#M.delete(t),this.#M.set(t,e),this.#Be(i)}async#lA(t,{workspaceHint:o=null,signal:i=this.#w}={}){if(typeof t!="string"||t==="")return{error:w("\u7528\u6CD5\uFF1A/watch ")};let e=/^\d{1,4}$/.test(t)?Number(t):null,a=typeof this.#t?.currentWorkspace=="function"?this.#t.currentWorkspace():null,A=async s=>{let g=await this.#t.listWorkspaceSessions(s,{signal:i});return Array.isArray(g?.sessions)?g.sessions:[]};if(e!==null){if(!a)return{error:w("\u5F53\u524D\u673A\u5668\u4EBA\u6CA1\u6709\u53EF\u7528\u7684\u5DE5\u4F5C\u533A\uFF0C\u65E0\u6CD5\u6309\u5E8F\u53F7\u89E3\u6790\u4F1A\u8BDD\u3002")};let s=this.#ce(await A(a)),g=s[e-1];return g?.sessionId?{sessionId:g.sessionId,title:g.title??w("\u6682\u65E0\u6807\u9898"),workspace:a,...Mn(g.lastSeq)?{lastSeq:g.lastSeq}:{}}:{error:w("\u5F53\u524D\u5DE5\u4F5C\u533A\u53EA\u6709 {count} \u4E2A\u4F1A\u8BDD\u3002",{count:s.length})}}let r;if(lA(o))r=[o];else{let s=typeof this.#t?.listWorkspaces=="function"?(await this.#t.listWorkspaces({signal:i})).filter(g=>g!==a):[];r=[a,...s].filter(Boolean)}for(let s of r){let c=(await A(s)).find(I=>I.sessionId===t);if(c)return{sessionId:t,title:c.title??w("\u6682\u65E0\u6807\u9898"),workspace:s,...Mn(c.lastSeq)?{lastSeq:c.lastSeq}:{}}}return{error:w("\u6CA1\u6709\u627E\u5230\u8FD9\u4E2A\u4F1A\u8BDD\uFF0C\u8BF7\u7528 /sessionlist \u67E5\u770B\u53EF\u7528\u4F1A\u8BDD\u3002")}}async#pA(t){let o=lA(t)??(typeof this.#t?.currentWorkspace=="function"?lA(this.#t.currentWorkspace()):null);if(!o||typeof this.#t?.listWorkspaceSessions!="function")return new Map;let i=await this.#t.listWorkspaceSessions(o,{signal:this.#$()});return new Map(this.#ce(Array.isArray(i?.sessions)?i.sessions:[]).filter(e=>lA(e?.sessionId)).map(e=>[e.sessionId,{sessionId:e.sessionId,title:e.title??e.name??w("\u6682\u65E0\u6807\u9898"),workspace:o,...Mn(e.lastSeq)?{lastSeq:e.lastSeq}:{}}]))}#Ge(t){let o=this.#z.get(t);if(o)return o.requested=!0,o.promise;let i={requested:!0,promise:null};return this.#z.set(t,i),i.promise=Promise.resolve().then(async()=>{try{do i.requested=!1,await this.#Pe(t,()=>this.#Le(t));while(i.requested&&!this.#w?.aborted)}finally{this.#z.get(t)===i&&this.#z.delete(t)}}),i.promise}async#fe(t,o,i,{notify:e=!0,validatedTarget:a=null,workspaceHint:A=null,replyTo:r=null}={}){let s=Date.now(),g=async y=>{e&&await this.#b(o,y,{replyTo:r}).catch(m=>{this.#O.warn?.("[dsh-feishu] watch notification failed:",m.message)})};if(this.#ve(),typeof this.#r?.setWatch!="function")return await g(w("\u5F53\u524D\u72B6\u6001\u5B58\u50A8\u4E0D\u652F\u6301\u5173\u6CE8\u3002")),{ok:!1,changed:!1,reason:"unsupported"};let c;try{c=a?.sessionId===i?a:await this.#lA(i,{workspaceHint:A,signal:A?this.#$():this.#w})}catch(y){return await g(w("\u65E0\u6CD5\u89E3\u6790\u4F1A\u8BDD\uFF1A{message}",{message:Ou(y)})),{ok:!1,changed:!1,reason:"resolve"}}if(c.error)return await g(c.error),{ok:!1,changed:!1,reason:"not-found"};let I=this.#r.watchEntries?.(t)??[],d=I.find(y=>y.sessionId===c.sessionId);if(!d&&I.length>=nS)return await g(w("\u6BCF\u4E2A\u804A\u5929\u6700\u591A\u5173\u6CE8 {count} \u4E2A\u4F1A\u8BDD\u3002",{count:nS})),{ok:!1,changed:!1,reason:"limit"};let B=Mn(d?.lastSeq)?d.lastSeq:Mn(c.lastSeq)?c.lastSeq:null,Q=lS(d)??(d?null:s);try{await this.#r.setWatch(t,{sessionId:c.sessionId,title:c.title,chatId:o,lastSeq:B,...Q!==null?{watchStartedAt:Q}:{},...r?{replyToMessageId:r}:d?.replyToMessageId?{replyToMessageId:d.replyToMessageId}:{}})}catch(y){return await g(w("\u5173\u6CE8\u5931\u8D25\uFF1A{message}",{message:Ou(y)})),{ok:!1,changed:!1,reason:"persist"}}return(!d||Q!==null||!Mn(B))&&this.#Ge(c.sessionId),await g(w("\u5DF2\u5173\u6CE8\u4F1A\u8BDD\u300C{title}\u300D\uFF0C\u4EFB\u52A1\u5B8C\u6210\u4F1A\u63A8\u9001\u7ED3\u679C\u3002",{title:String(c.title).replace(/\s+/gu," ")})),{ok:!0,changed:!d,entry:this.#r.watchEntry?.(t,c.sessionId)}}async#Qe(t,o,i,{notify:e=!0,replyTo:a=null}={}){let A=async g=>{e&&await this.#b(o,g,{replyTo:a}).catch(c=>{this.#O.warn?.("[dsh-feishu] unwatch notification failed:",c.message)})};if(typeof this.#r?.removeWatch!="function")return{ok:!1,changed:!1,reason:"unsupported"};let r=this.#r.watchEntries?.(t)??[],s=typeof i=="string"&&/^\d{1,4}$/.test(i)?r[Number(i)-1]:r.find(g=>g.sessionId===i);if(!s)return await A(w("\u5173\u6CE8\u5217\u8868\u91CC\u6CA1\u6709\u8FD9\u4E2A\u4F1A\u8BDD\uFF0C\u56DE\u590D /watchlist \u67E5\u770B\u3002")),{ok:!0,changed:!1,reason:"absent"};try{await this.#r.removeWatch(t,s.sessionId),this.#Y.delete(`${t}\0${s.sessionId}`)}catch(g){return await A(w("\u53D6\u6D88\u5931\u8D25\uFF1A{message}",{message:Ou(g)})),{ok:!1,changed:!1,reason:"persist"}}return await A(w("\u5DF2\u53D6\u6D88\u5173\u6CE8\u300C{title}\u300D\u3002",{title:String(s.title??"").replace(/\s+/gu," ")})),{ok:!0,changed:!0,entry:s}}async#ye(t,o,{updateMessageId:i=null,replyTo:e=null}={}){let a=this.#r.watchEntries?.(t)??[],A=[],r=null;try{if(r=typeof this.#t?.currentWorkspace=="function"?this.#t.currentWorkspace():null,r&&typeof this.#t?.listWorkspaceSessions=="function"){let s=await this.#t.listWorkspaceSessions(r,{signal:this.#$()});A=this.#ce(Array.isArray(s?.sessions)?s.sessions:[]).map(g=>({sessionId:g.sessionId,title:g.title??g.name??g.sessionId}))}}catch{}this.#ge(t,{kind:"watches",entries:a}),await this.#J(o,f6(a,A),{key:t,updateMessageId:i,replyTo:e,sessionWorkspace:r})}#dA({sessionId:t,event:o}){this.#w?.aborted||!t||!o||typeof o!="object"||o.type!=="turn/end"||!_u(o.seq)||(this.#IA(t,o),this.#Pe(t,async()=>{let i=this.#r.keysWatching?.(t)??[],e=i.some(A=>IS(this.#r.watchEntry?.(A,t))),a=i.some(A=>this.#Y.has(`${A}\0${t}`));(e||a)&&await this.#Le(t),await this.#Fe(t,o)}))}async#Fe(t,o,{keys:i=null}={}){if(this.#w?.aborted||typeof this.#r?.keysWatching!="function")return;let e=o?.data?.reason?.kind??o?.data?.reason??null,a=i??this.#r.keysWatching(t);for(let A of a){if(this.#w?.aborted)return;let r=this.#r.watchEntry?.(A,t),s=`${A}\0${t}`,g=this.#Y.get(s);if(_u(g)&&Mn(r?.lastSeq)&&r.lastSeq>=g&&(this.#Y.delete(s),g=void 0),!(!r?.chatId||IS(r)||Mn(r.lastSeq)&&r.lastSeq>=o.seq||_u(g)&&o.seq>g))try{await this.#J(r.chatId,Q6(t,r.title,e),{key:A,replyTo:r.replyToMessageId??null});let c=this.#r.watchEntry?.(A,t);if(!c||c.chatId!==r.chatId||IS(c)||Mn(c.lastSeq)&&c.lastSeq>=o.seq)continue;await this.#r.setWatch(A,{...c,lastSeq:o.seq}),g===o.seq&&this.#Y.delete(s)}catch(c){this.#Y.set(s,_u(g)?Math.min(g,o.seq):o.seq),this.#O.warn?.("[dsh-feishu] completion push failed:",c.message)}}}async#Le(t){if(!(this.#w?.aborted||typeof this.#t?.rpc!="function"))try{let o=await this.#t.rpc("session.history",{sessionId:t,maxMessages:20},3e4,{signal:this.#w}),i=Ske(o);this.#Be();let e=this.#M.get(t)??new Map,a=new Map(i.map(g=>[g.seq,g]));for(let[g,c]of e)a.has(g)||a.set(g,c.event);let A=[...a.values()].sort((g,c)=>g.seq-c.seq),r=A.at(-1)?.seq??-1,s=typeof this.#r?.keysWatching=="function"?this.#r.keysWatching(t):[];for(let g of s){let c=this.#r.watchEntry?.(g,t);if(!IS(c))continue;let I=lS(c),d=Mn(c.lastSeq)?c.lastSeq:-1,B=I===null?null:A.find(_=>{if(_.seq<=d)return!1;let T=Number.isSafeInteger(_.time)&&_.time>=0?_.time:e.get(_.seq)?.arrivalAt;return Number.isSafeInteger(T)&&T>=I}),f=B?Math.max(d,B.seq-1):Math.max(d,r),Q=this.#r.watchEntry?.(g,t);if(!Q||Q.chatId!==c.chatId||Q.lastSeq!==c.lastSeq||lS(Q)!==I)continue;let{watchStartedAt:y,...m}=Q;await this.#r.setWatch(g,{...m,lastSeq:f})}for(let g of A)g.type==="turn/end"&&await this.#Fe(t,g,{keys:s})}catch(o){this.#w?.aborted||this.#O.warn?.(`[dsh-feishu] watch compensation failed for ${t}:`,o.message)}}async#hA(){let t=typeof this.#r?.watchedSessionIds=="function"?this.#r.watchedSessionIds():[];this.#w?.aborted||await Promise.allSettled(t.map(o=>this.#Ge(o)))}#me(t,o,i){return{timeoutMs:this.#T,signal:this.#w,control:{owner:this,key:o},onInteraction:e=>this.#uA(e,{key:o,actor:xd(t),chatId:t.message.chat_id,requiresMention:t.message.chat_type!=="p2p",replyToMessageId:t.message.message_id}),onInteractionResolved:e=>this.#BA(e),files:i}}async#Se(t,o,{deliveryId:i,presentation:e,replyTo:a=null}){let A=[];for(let r of s6(o)){this.#w?.throwIfAborted();let s=await this.#b(t,r,{replyTo:a});s&&A.push(s)}return Zt({deliveryId:i,presentation:e,providerMessageIds:A})}async#de(t,o,i=[],e){let a=await Po({artifacts:i,baseReceipt:e,deliveryId:e?.deliveryId??i[0]?.deliveryKey??o,aggregatePresentation:e?"feishu-text-and-files":"feishu-files",channelKey:"feishu",signal:this.#w,sendImage:typeof this.#A?.sendImage=="function"?A=>this.#A.sendImage(t,A,{replyTo:o,signal:this.#w}):void 0,sendFile:typeof this.#A?.sendFile=="function"?A=>this.#A.sendFile(t,A,{replyTo:o,signal:this.#w}):void 0,onFailure:(A,r)=>tt(this.#E,r,{userMessage:mke(A?.fileName,r),reason:r?.code}),sendFailureNotice:async(A,r,s)=>({messageId:await this.#b(t,ht(s))}),logger:this.#O});return this.#E.artifactsSent=(this.#E.artifactsSent??0)+a.artifactsSent,this.#E.artifactSendErrors=(this.#E.artifactSendErrors??0)+a.artifactSendErrors,a.receipt?{receipt:a.receipt,failureNoticeVisible:a.failureNoticeVisible,artifactSendErrors:a.artifactSendErrors}:{receipt:Zt({deliveryId:o,presentation:"feishu-files"}),failureNoticeVisible:a.failureNoticeVisible,artifactSendErrors:a.artifactSendErrors}}async#EA(t,o,i,{onAskComplete:e}={}){let a=t.message.chat_id,A=t.message.message_id,r=i.content,s=!1,g=()=>{s||(s=!0,e?.())},c=Tt(i)?await bo(i,{signal:this.#w}):void 0,I=this.#c.get(A),d=!1;if(I){let _=c??r;c=No(_,I,()=>({channel:"feishu",senderId:xd(t)})),d=c!==_}if(!this.#A?.stream){let{answer:_,artifacts:T=[]}=await Oi({harness:this.#t,state:this.#r,key:o,text:r,content:c,contextEnhanced:d,createOptions:{signal:this.#w},existsOptions:{signal:this.#w},askOptions:this.#me(t,o,i.files)});g();let R,K=null;try{R=await this.#Se(a,cS(_,T),{deliveryId:A,presentation:"feishu-text",replyTo:A})}catch(z){K=Pa(z),this.#O.warn?.("[dsh-feishu] final text delivery failed; continuing with result files:",z)}let L=await this.#de(a,A,T,R),U=L.receipt.artifacts.some(({outcome:z})=>z==="sent"||z==="unknown");if(K&&!U&&!L.failureNoticeVisible)throw K;return K&&L.artifactSendErrors===0&&tt(this.#E,K),this.#E.streamFallbacks=(this.#E.streamFallbacks??0)+1,{...L,textDeliveryErrors:K?1:0}}let B=!1,f="",Q=[],y;try{y=await this.#A.stream(a,{markdown:async _=>{B=!0;let T=this.#me(t,o,i.files),R={...T,onInteraction:async L=>{(L?.kind==="question"||L?.kind==="approval")&&typeof _?.rotate=="function"&&await _.rotate(),await T.onInteraction(L)},onUpdate:async L=>{await _.setContent(this.#fA(L)),this.#E.streamUpdates=(this.#E.streamUpdates??0)+1}},K=await Oi({harness:this.#t,state:this.#r,key:o,text:r,content:c,contextEnhanced:d,createOptions:{signal:this.#w},existsOptions:{signal:this.#w},askOptions:R});g(),f=K.answer,Q=K.artifacts??[],await _.setContent(cS(f,Q))}},{replyTo:A})}catch(_){if(this.#E.streamErrors=(this.#E.streamErrors??0)+1,f||Q.length>0){this.#O.warn?.("[dsh-feishu] native stream failed after generation; sending final text:",_.message);let q,x=null;try{q=await this.#Se(a,cS(f,Q),{deliveryId:A,presentation:"feishu-text-fallback",replyTo:A})}catch(F){x=Pa(F),this.#O.warn?.("[dsh-feishu] fallback text delivery failed; continuing with result files:",F)}let G=await this.#de(a,A,Q,q),V=G.receipt.artifacts.some(({outcome:F})=>F==="sent"||F==="unknown");if(x&&!V&&!G.failureNoticeVisible)throw x;return x&&G.artifactSendErrors===0&&tt(this.#E,x),this.#E.streamFallbacks=(this.#E.streamFallbacks??0)+1,{...G,textDeliveryErrors:x?1:0}}if(B)throw _;this.#O.warn?.("[dsh-feishu] native stream unavailable; using text fallback:",_.message);let{answer:T,artifacts:R=[]}=await Oi({harness:this.#t,state:this.#r,key:o,text:r,content:c,contextEnhanced:d,createOptions:{signal:this.#w},existsOptions:{signal:this.#w},askOptions:this.#me(t,o,i.files)});g();let K,L=null;try{K=await this.#Se(a,cS(T,R),{deliveryId:A,presentation:"feishu-text-fallback",replyTo:A})}catch(q){L=Pa(q),this.#O.warn?.("[dsh-feishu] fallback text delivery failed; continuing with result files:",q)}let U=await this.#de(a,A,R,K),z=U.receipt.artifacts.some(({outcome:q})=>q==="sent"||q==="unknown");if(L&&!z&&!U.failureNoticeVisible)throw L;return L&&U.artifactSendErrors===0&&tt(this.#E,L),this.#E.streamFallbacks=(this.#E.streamFallbacks??0)+1,{...U,textDeliveryErrors:L?1:0}}let m=await this.#de(a,A,Q,Zt({deliveryId:A,presentation:"feishu-cardkit",providerMessageIds:Ua(y)}));return this.#E.streamResponses=(this.#E.streamResponses??0)+1,m}async#CA(t,o,i,e,a){this.#w?.throwIfAborted();let A=this.#n.get(i),r=e.claimedReplyMessageId===o;if(!A||A!==e||A.submitting)return this.#we(t,i)?this.#De(t,o):r&&(!A||A!==e)?this.#De(t,o):this.#x(t,o,i,a,{releaseMessageId:!1,finalize:!1});if(this.#r.hasSeen(o))return;await this.#r.markSeen(o),this.#E.lastMessageAt=new Date().toISOString(),this.#E.messagesReceived+=1;let s=Hd(t);if(!s){await this.#b(t.message.chat_id,w("\u8BF7\u7528\u6587\u5B57\u56DE\u7B54\u5F53\u524D\u95EE\u9898\u3002"));return}let g=this.#n.get(i);if(!g||g!==e||g.submitting){if(this.#we(t,i)){await this.#b(t.message.chat_id,Du(),{replyTo:t.message.message_id}).catch(()=>{});return}if(r&&(!g||g!==e)){await this.#b(t.message.chat_id,Du(),{replyTo:t.message.message_id});return}return this.#x(t,o,i,a,{releaseMessageId:!1,alreadyRecorded:!0,finalize:!1})}if(g.chatId=t.message.chat_id,g.needsPresentation){try{await this.#he(g)}catch{this.#E.lastError="\u98DE\u4E66\u4EA4\u4E92\u95EE\u9898\u53D1\u9001\u5931\u8D25\u3002",this.#O.error?.("[dsh-feishu] failed to retry an interaction question"),g.interaction.reconnect?.()}return}g.questions[g.index]&&await this.#Ue(g,s,{chatId:t.message.chat_id,messageId:o})}async#Ue(t,o,{chatId:i,messageId:e}={}){let a=t.questions[t.index];if(!a)return;if(t.chatId=i??t.chatId,t.answers.push(wi(a,o)),t.index+=1,t.index{});return}t.submitting=!1,t.answers.pop(),t.index-=1,this.#E.lastError="\u56DE\u7B54\u63D0\u4EA4\u5931\u8D25\u3002",this.#O.error?.("[dsh-feishu] failed to answer a Harness interaction"),i&&await this.#b(i,w("\u56DE\u7B54\u63D0\u4EA4\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u5F53\u524D\u95EE\u9898\u7684\u7B54\u6848\u3002")).catch(()=>{})}}async#uA(t,{key:o,actor:i,chatId:e,requiresMention:a,replyToMessageId:A}){if(await this.#D.handleRequested(t,{key:o,actor:i,requiresMention:a,send:I=>this.#b(e,I,{replyTo:A}),...this.#Ae?{render:async I=>{await this.#J(e,S6({toolName:I.toolCall?.name??I.payload?.toolName,operation:Bke(I.toolCall),reason:I.payload?.reason,approvalId:I.approvalId}),{key:o,replyTo:A}).catch(async()=>{await this.#b(e,I.text,{replyTo:A})})}}:{}})||t?.kind!=="question")return;let r=t?.payload?.questions,s=typeof t?.interactionId=="string"?t.interactionId:t?.rpcId;if(typeof t.rpcId!="string"||typeof s!="string"||typeof t.sessionId!="string"||!Array.isArray(r)||r.length===0||r.some(I=>!Si(I))){this.#O.warn?.("[dsh-feishu] ignored an invalid Harness question interaction");return}if(t.recovered===!0){await t.respond({ok:!1,error:{code:"cancelled",message:"Feishu safely cancelled an interaction left by an earlier client.",details:{}}}),await this.#b(e,w("\u68C0\u6D4B\u5230\u8FD9\u4E2A Session \u4E2D\u9057\u7559\u7684\u5F85\u56DE\u7B54\u95EE\u9898\uFF0C\u5DF2\u5B89\u5168\u53D6\u6D88\u5E76\u7EE7\u7EED\u5904\u7406\u4F60\u521A\u624D\u7684\u6D88\u606F\u3002"),{replyTo:A}).catch(()=>{});return}let g=this.#n.get(o);if(g?.interactionId===s){g.interaction=t,g.needsPresentation&&await this.#he(g);return}if(this.#s.has(s))return;if(g){await t.respond({ok:!1,error:{code:"cancelled",message:"Feishu is already handling another user interaction.",details:{}}});return}let c={kind:"question",interactionId:s,sessionId:t.sessionId,interaction:t,key:o,actor:i,requiresMention:a,questions:r,answers:[],index:0,chatId:e,replyToMessageId:A,queue:null,claimedReplyMessageId:null,submitting:!1,needsPresentation:!0,questionMessageIds:new Set,inactive:!1};this.#n.set(o,c),this.#s.set(c.interactionId,o),await this.#he(c)}async#BA(t){if(await this.#D.handleResolved(t))return;let o=t?.interactionId;if(t?.kind!=="question"||typeof o!="string")return;let i=this.#s.get(o);if(!i)return;let e=this.#n.get(i);e&&this.#Ie(i,e),this.#Oe(i,o)}async#he(t){let o=t.questions[t.index];if(!o)return;let i=Array.isArray(o?.options)?o.options:[],e=this.#Ae&&i.length>0&&o.multiSelect!==!0,a;e?a=await this.#J(t.chatId,w6({interactionId:t.interactionId,header:o.header,question:o.question,detail:o.detail,options:i,index:t.index,total:t.questions.length}),{key:t.key,replyTo:t.replyToMessageId}).catch(async()=>{await this.#b(t.chatId,ni(o,t.index,t.questions.length,{requiresMention:t.requiresMention}),{replyTo:t.replyToMessageId})}):a=await this.#b(t.chatId,ni(o,t.index,t.questions.length,{requiresMention:t.requiresMention}),{replyTo:t.replyToMessageId}),a&&(t.questionMessageIds.add(a),t.inactive&&this.#Ie(t.key,t)),t.needsPresentation=!1}#Ie(t,o){let i=Date.now()+eke;for(let e of o.questionMessageIds??[])this.#p.set(e,{key:t,expiresAt:i})}#we(t,o){let i=Date.now();for(let[e,a]of this.#p)a.expiresAt<=i&&this.#p.delete(e);for(let e of[t?.message?.parent_id,t?.message?.root_id]){let a=this.#p.get(e);if(a?.key===o&&a.expiresAt>i)return!0}return!1}async#De(t,o){this.#r.hasSeen(o)||(await this.#r.markSeen(o),this.#E.lastMessageAt=new Date().toISOString(),this.#E.messagesReceived+=1,await this.#b(t.message.chat_id,Du(),{replyTo:t.message.message_id}).catch(()=>{}))}#He(t,o){let i=this.#n.get(t);return!i||o!==void 0&&i.interactionId!==o?null:(this.#n.delete(t),this.#s.delete(i.interactionId),i.inactive=!0,i)}#Oe(t,o){return this.#He(t,o)!==null}async#be(t){let o=this.#He(t);if(!(!o||o.kind!=="question")){this.#Ie(t,o);try{await o.interaction.respond({ok:!1,error:{code:"cancelled",message:"The Feishu interaction ended before the user answered.",details:{}}},{signal:AbortSignal.timeout(5e3)})}catch(i){i?.code!=="interaction-not-pending"&&this.#O.warn?.("[dsh-feishu] failed to cancel a pending Harness interaction")}}}#fA(t){return t.type==="text"&&t.text?t.text:t.type==="tool"?t.name==="web_search"?w("_\u6B63\u5728\u641C\u7D22\u7F51\u7EDC\u5E76\u6574\u7406\u4FE1\u606F\u2026_"):w("_\u6B63\u5728\u4F7F\u7528 {name}\u2026_",{name:t.name||w("\u5DE5\u5177")}):w("_{text}_",{text:t.text||w("\u6B63\u5728\u5904\u7406\u2026")})}#QA(t){return qQ({adapter:this.#A,target:t,reactions:{processing:"OnIt",success:"DONE",error:"ERROR"},status:this.#E,logger:this.#O,label:"feishu"})}#Ke(t,o){o.clear()}#re(t,o,i){i==="ERROR"?o.error():o.success()}async#b(t,o,{replyTo:i}={}){let e=JSON.stringify({text:o});if(i)try{let A=await this.#e.im.v1.message.reply({path:{message_id:i},data:{msg_type:"text",content:e}});if(A?.code&&A.code!==0)throw new Error(`Feishu reply failed: ${A.msg||A.code}`);return lA(A?.data?.message_id)}catch(A){this.#O.warn?.("[dsh-feishu] threaded reply failed; falling back to a plain message:",A?.message??String(A))}let a=await this.#e.im.v1.message.create({params:{receive_id_type:"chat_id"},data:{receive_id:t,msg_type:"text",content:e}});if(a?.code&&a.code!==0)throw new Error(`Feishu send failed: ${a.msg||a.code}`);return lA(a?.data?.message_id)}};import{createHash as bke}from"node:crypto";var G6="stream_md",Mke="\u5DF2\u8FDE\u63A5 DeepSeek Harness\uFF0C\u6B63\u5728\u601D\u8003\u2026",Nu=28e3,dS=12e4,F6=new Map([[99991672,["artifact-permission-required","Feishu file delivery requires the im:resource permission."]],[234006,["artifact-too-large","The result file exceeds Feishu's size limit."]],[234010,["artifact-empty","Feishu does not accept empty files."]],[230017,["artifact-provider-rejected","Feishu rejected the uploaded file ownership."]],[230020,["artifact-rate-limited","Feishu temporarily rate-limited file delivery."]],[230049,["artifact-delivery-uncertain","Feishu could not confirm the file message result."]],[230055,["artifact-provider-rejected","Feishu rejected the file message type."]]]);function Cl(n,t){if(t?.code&&t.code!==0)throw new Error(`${n} failed: ${t.msg||t.code}`);return t}function L6(n){let t=[n],o=new Set,i;for(;t.length>0;){let e=t.shift();if(!e||o.has(e))continue;if(typeof e=="object"&&o.add(e),Array.isArray(e)){t.push(...e);continue}let a=Number(e?.code);if(Number.isFinite(a)&&a!==0){if(F6.has(a))return a;i??=a}t.push(e?.response?.data,e?.data,e?.error,e?.cause)}return i}function Tu(n,t,o,{uncertain:i=!1}={}){let e=o==null?void 0:Number(o),a=Number.isFinite(e)&&e!==0?e:L6(t),A=Number.isFinite(a)?["artifact-provider-rejected",`Feishu rejected file ${n}.`]:i?["artifact-delivery-uncertain","Feishu could not confirm the file message result."]:["artifact-provider-failed",`Feishu file ${n} failed.`],[r,s]=F6.get(a)??A,g=new Error(s,{cause:t});return g.code=r,Number.isFinite(a)&&(g.providerCode=a),g}function v6(n,t){if(!Number.isInteger(n)||n<1||n>dS)throw new TypeError(`${t} must be an integer between 1 and ${dS}`);return n}function uP(n){return n?.reason??new DOMException("The operation was aborted","AbortError")}function _ke(n){let t=new Error(`Feishu file ${n} timed out.`);return t.code="provider-timeout",t}function P6(n,{signal:t,timeoutMs:o,stage:i}){t?.throwIfAborted();let e=new AbortController,a=t?AbortSignal.any([t,e.signal]):e.signal;return new Promise((A,r)=>{let s=!1,g=(d,B)=>{s||(s=!0,clearTimeout(I),a.removeEventListener("abort",c),d(B))},c=()=>g(r,t?.aborted?uP(t):_ke(i)),I=setTimeout(()=>e.abort(),o);a.addEventListener("abort",c,{once:!0}),Promise.resolve().then(()=>n(a)).then(d=>g(A,d),d=>g(r,d)),a.aborted&&c()})}function Tke(n,t,o){let i=`${n.deliveryKey}\0${t}`;return`dshim_${bke("sha256").update(o==="file"?i:`${i}\0${o}`).digest("hex").slice(0,40)}`}function Nke(n){let t=String(n??"").replace(/\s+/g," ").trim();return t.length<=50?t:`${BP(t,49)}\u2026`}function BP(n,t){let o=Math.min(n.length,t),i=n.charCodeAt(o-1),e=n.charCodeAt(o);return i>=55296&&i<=56319&&e>=56320&&e<=57343&&(o-=1),n.slice(0,o)}function CP(n){if(n.length<=Nu)return n;let t=` ${w("\u5185\u5BB9\u8F83\u957F\uFF0C\u751F\u6210\u5B8C\u6210\u540E\u5C06\u5206\u6BB5\u53D1\u9001\u5B8C\u6574\u56DE\u7B54\u3002")}`;return BP(n,Nu-t.length)+t}function Rke(n){let t=[],o=n;for(;o.length>Nu;){let i=BP(o,Nu),e=i.lastIndexOf(` `)+1;e(A&&(a=await this.#o(t,i.replyTo),e.push(a),A=!1),a),g={get messageId(){return a.messageId},rotate:async()=>{if(!A){A=!0;try{await this.#i(a,`${CP(r)} diff --git a/src/channels/feishu/bridge.mjs b/src/channels/feishu/bridge.mjs index 88ef6ae..6b4d9f2 100644 --- a/src/channels/feishu/bridge.mjs +++ b/src/channels/feishu/bridge.mjs @@ -1721,7 +1721,7 @@ export class FeishuHarnessBridge { return; } } - await this.#handleCardAction(resolvedAction, entry); + await this.#handleCardAction(resolvedAction, { ...entry, actor: entry.operatorOpenId }); }, { lane: isStop || isRealSteer ? 'control' : 'regular', coalesceStop: isStop, @@ -1878,6 +1878,7 @@ export class FeishuHarnessBridge { sessionWorkspace = null, sessionPage = 0, selections = [], + actor = null, }) { // Confirmations triggered by a card interaction stay anchored to the // card's message so they land inside the same Feishu topic. @@ -1887,22 +1888,32 @@ export class FeishuHarnessBridge { const sep = action.indexOf(':'); const approvalId = action.slice(sep + 1); const outcome = action.startsWith('approve:') ? 'allowed-once' : 'rejected'; - const submitted = await this.#approvals.submitByApprovalId(approvalId, outcome); + // Bind the decision to the operator so another allowed group member + // cannot decide someone else's approval. + const submitted = await this.#approvals.submitByApprovalId(approvalId, outcome, { actor }); if (!submitted) { await reply(t('该审批已处理或不存在,无需重复操作。')).catch(() => undefined); } return; } - // Question option buttons: answer:: + // Question option buttons: answer::: if (action.startsWith('answer:')) { const rest = action.slice('answer:'.length); - const sep = rest.indexOf(':'); - if (sep !== -1) { - const interactionId = rest.slice(0, sep); - const optionLabel = rest.slice(sep + 1); + const firstSep = rest.indexOf(':'); + if (firstSep !== -1) { + const interactionId = rest.slice(0, firstSep); + const afterId = rest.slice(firstSep + 1); + const indexSep = afterId.indexOf(':'); + const indexText = indexSep === -1 ? afterId : afterId.slice(0, indexSep); + const optionLabel = indexSep === -1 ? '' : afterId.slice(indexSep + 1); const qKey = this.#interactionKeys.get(interactionId); const pending = qKey ? this.#pendingInteractions.get(qKey) : null; - if (pending && pending.kind === 'question' && !pending.submitting) { + // Only the actor who started the interaction may answer it, and the + // card must still target the current question (a stale card from an + // earlier question in a multi-question interaction must not submit). + if (pending && pending.kind === 'question' && !pending.submitting + && pending.actor === actor + && Number(indexText) === pending.index) { await this.#submitQuestionAnswer(pending, optionLabel, { chatId }); } else { await reply(INTERACTION_RESOLVED_TEXT()).catch(() => undefined); @@ -3705,9 +3716,12 @@ export class FeishuHarnessBridge { approvalId: pending.approvalId, }), { key, replyTo: replyToMessageId }, - ).catch(() => { - // Fall back to the plain-text approval if the card cannot be sent. - return this.#send(chatId, pending.text, { replyTo: replyToMessageId }).catch(() => undefined); + ).catch(async () => { + // Fall back to the plain-text approval if the card cannot be + // sent. If the text send also fails, let the error propagate so + // the pending approval is not marked as presented and the + // existing retry/reconnect logic can run. + await this.#send(chatId, pending.text, { replyTo: replyToMessageId }); }); }, } @@ -3827,15 +3841,17 @@ export class FeishuHarnessBridge { total: pending.questions.length, }), { key: pending.key, replyTo: pending.replyToMessageId }, - ).catch(() => { + ).catch(async () => { // Fall back to the plain-text question if the card cannot be sent. - return this.#send( + // If the text send also fails, let the error propagate so the pending + // question is not marked as presented and the existing retry logic runs. + await this.#send( pending.chatId, harnessQuestionText(question, pending.index, pending.questions.length, { requiresMention: pending.requiresMention, }), { replyTo: pending.replyToMessageId }, - ).catch(() => undefined); + ); }); } else { // Multi-select or free-text questions keep the plain-text reply flow. diff --git a/src/channels/feishu/feishu-cards.mjs b/src/channels/feishu/feishu-cards.mjs index 7167ec9..22866b1 100644 --- a/src/channels/feishu/feishu-cards.mjs +++ b/src/channels/feishu/feishu-cards.mjs @@ -925,7 +925,9 @@ export function questionCard({ interactionId, header, question, detail, options, // Include the option description in the button so the user sees the full // meaning (mirrors the text form "1. label — description"). const buttonText = description ? `${label}\n${description}` : label; - elements.push(button(buttonText, `answer:${interactionId}:${label}`)); + // Action carries the question index so a stale card from a previous + // question cannot be applied to the current one: answer:::