diff --git a/lib/index.js b/lib/index.js index 38897ff..4d67693 100644 --- a/lib/index.js +++ b/lib/index.js @@ -241,7 +241,7 @@ Generate a new one`,"\u4E8C\u7EF4\u7801\u56FE\u7247\u6B63\u5728\u751F\u6210\u202 `),a=[],A="",t=!1,s=[],g=E=>{if(E.length<=i){if(!A){A=E;return}let f=`${A} ${E}`;f.length>i?(a.push(A),A=E):A=f;return}A&&(a.push(A),A="");let B=E;for(;B.length>i;)a.push(B.slice(0,i)),B=B.slice(i);A=B},c=()=>{if(s.length===0)return;let E=s.join(` `);s=[],g(E)},I=E=>{let B=E;for(;B.length>i;)A&&(a.push(A),A=""),a.push(B.slice(0,i)),B=B.slice(i);g(B)};for(let E of e){if(Kbe.test(E)){c(),!t&&A&&(a.push(A),A=""),t=!t,I(E);continue}if(t){I(E);continue}if(Ybe.test(E)){s.push(E);continue}c(),I(E)}return c(),A&&a.push(A),a}function jbe(){let n=Date.now()%1e8,r=Math.floor(Math.random()*65536);return(n^r)%65536}async function m8(n,r,o,{logger:i}={}){let e=xbe(o),a=[];for(let A of e){if(typeof n?.send=="function")try{a.push(await n.send({target:r,msgType:2,markdown:{content:A},extra:{msg_seq:jbe()}}));continue}catch(t){i?.warn?.("[dsh-im:qq] markdown delivery failed; retrying as plain text:",t)}a.push(await n.sendText(r,A))}return a}var Py="\u8FD9\u4E2A\u95EE\u9898\u5DF2\u5728\u5176\u4ED6\u5BA2\u6237\u7AEF\u5904\u7406\uFF0C\u65E0\u9700\u518D\u6B21\u56DE\u7B54\u3002",S8=12e4,Jbe=Object.freeze([".myqcloud.com",".qpic.cn",".qq.com",".qq.com.cn",".tencentcos.com",".ugcimg.cn"]),qbe=/\.(?:gif|jpe?g|png|webp)$/i,zbe=["QQ \u673A\u5668\u4EBA\u5DF2\u8FDE\u63A5 DeepSeek Harness\u3002","","\u76F4\u63A5\u53D1\u9001\u6587\u5B57\u6216\u56FE\u7247\u5373\u53EF\u7EE7\u7EED\u5F53\u524D\u4F1A\u8BDD\u3002","/new \u5F00\u542F\u4E00\u4E2A\u5168\u65B0\u4F1A\u8BDD","/compact \u538B\u7F29\u5F53\u524D\u4F1A\u8BDD\u7684\u8F83\u65E9\u4E0A\u4E0B\u6587","/workspace \u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84 \u5207\u6362\u5DE5\u4F5C\u533A","/workspacelist \u5217\u51FA\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84","/sessionlist [\u5DE5\u4F5C\u533A\u5E8F\u53F7\u6216\u7EDD\u5BF9\u8DEF\u5F84] \u5217\u51FA\u4F1A\u8BDD ID \u548C\u6807\u9898","/session Session ID \u6216\u5F53\u524D\u5DE5\u4F5C\u533A\u5E8F\u53F7 \u5C06\u5F53\u524D\u804A\u5929\u7ED1\u5B9A\u5230\u6307\u5B9A\u4F1A\u8BDD","/models \u6309\u5E8F\u53F7\u5217\u51FA\u6240\u6709\u53EF\u7528\u6A21\u578B","/model [\u5E8F\u53F7\u6216\u5B8C\u6574\u6A21\u578BID] \u67E5\u770B\u6216\u5207\u6362\u5F53\u524D\u4F1A\u8BDD\u6A21\u578B","\u793A\u4F8B\uFF1A\u5148\u53D1 /models\uFF0C\u518D\u53D1 /model 2","/presetlist \u6309\u5E8F\u53F7\u5217\u51FA\u53EF\u7528 Agent Preset","/preset [\u5E8F\u53F7\u6216\u5B8C\u6574ID] \u67E5\u770B\u6216\u8BBE\u7F6E\u5F53\u524D\u673A\u5668\u4EBA Agent Preset","\u7EAF\u6570\u5B57 ID\uFF1A/preset id:","/preset --default \u8DDF\u968F Host \u9ED8\u8BA4","/stop \u505C\u6B62\u5F53\u524D\u4EFB\u52A1","/steer \u8865\u5145\u6307\u4EE4 \u7EA0\u504F\u5F53\u524D\u4EFB\u52A1","/status \u68C0\u67E5\u8FDE\u63A5\u72B6\u6001","/help \u663E\u793A\u672C\u5E2E\u52A9"].join(` -`);function Vbe(n){return`${n.kind}:${n.kind==="group"?n.groupOpenid:n.senderId}`}function AC(n){return typeof n?.content=="string"?n.content.trim():""}function w8(n){let r=Oi(n?.content_type??n?.contentType);return r?r.split(";",1)[0].trim().toLowerCase():null}function O8(n){return w8(n)?.startsWith("image/")===!0||qbe.test(Oi(n?.filename)??"")}function Gy(n){return Array.isArray(n?.attachments)&&n.attachments.some(O8)}function Wbe(n,{fetchImpl:r=fetch}={}){if(typeof r!="function")throw new TypeError("fetchImpl must be a function");let o=[];for(let i of n?.attachments??[]){if(!O8(i))continue;let e=Oi(i?.url),a=Oi(i?.filename)??void 0,A=w8(i),t=Number(i?.size);o.push({...a?{name:a}:{},...A?.startsWith("image/")?{mediaType:A}:{},...Number.isFinite(t)&&t>=0?{size:t}:{},load:({signal:s,maxBytes:g})=>{if(!e)throw new Error("QQ image attachment has no download URL");return Ui(e,{fetchImpl:r,signal:s,maxBytes:g,allowedHosts:Jbe})}})}return{content:AC(n),images:o}}function Oi(n){return typeof n=="string"&&n.trim()?n.trim():null}function Zbe(n,r){let o=String(n??"\u7ED3\u679C\u6587\u4EF6").replace(/[\r\n]+/g," ").trim()||"\u7ED3\u679C\u6587\u4EF6";if(r?.name==="UploadDailyLimitExceededError")return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46 QQ \u4ECA\u65E5\u6587\u4EF6\u4E0A\u4F20\u989D\u5EA6\u5DF2\u7528\u5B8C\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`;switch(r?.code){case"artifact-delivery-uncertain":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u7684\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`;case"artifact-permission-required":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46\u5F53\u524D QQ \u673A\u5668\u4EBA\u6CA1\u6709\u6587\u4EF6\u6D88\u606F\u6743\u9650\u3002`;case"artifact-too-large":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u8D85\u8FC7\u5F53\u524D QQ \u673A\u5668\u4EBA\u53EF\u53D1\u9001\u7684\u6587\u4EF6\u5927\u5C0F\uFF0C\u672A\u53D1\u9001\u3002`;case"artifact-empty":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u4E3A\u7A7A\uFF0CQQ \u4E0D\u5141\u8BB8\u53D1\u9001\u7A7A\u6587\u4EF6\u3002`;case"artifact-changed":case"artifact-invalid":case"artifact-unavailable":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\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`;case"artifact-rate-limited":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u6682\u65F6\u88AB QQ \u9650\u6D41\uFF0C\u672A\u80FD\u53D1\u9001\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`;case"artifact-provider-rejected":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46 QQ \u62D2\u7EDD\u4E86\u8BE5\u6587\u4EF6\u6216\u6587\u4EF6\u6D88\u606F\u3002`;default:return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46\u6682\u65F6\u672A\u80FD\u901A\u8FC7 QQ \u53D1\u9001\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`}}function Xbe(n,r){return typeof n=="string"&&n.trim()?n:r.length>0?"\u7ED3\u679C\u6587\u4EF6\u5DF2\u751F\u6210\u3002":n}function D8(n,{dispatched:r=!1}={}){if(n?.code?.startsWith?.("artifact-")||n?.name==="UploadDailyLimitExceededError")return n;let o=Number(n?.httpStatus),i=new Error("QQ file delivery failed",{cause:n});return o===401||o===403?i.code="artifact-permission-required":o===413?i.code="artifact-too-large":o===429?i.code="artifact-rate-limited":o===400||o===404?i.code="artifact-provider-rejected":i.code=r?"artifact-delivery-uncertain":"artifact-provider-failed",i}function b8(n){return n?.reason instanceof Error?n.reason:new DOMException("The operation was aborted","AbortError")}function $be(n,r){return r?(r.throwIfAborted(),new Promise((o,i)=>{let e=!1,a=(t,s)=>{e||(e=!0,r.removeEventListener("abort",A),t(s))},A=()=>a(i,b8(r));r.addEventListener("abort",A,{once:!0}),Promise.resolve(n).then(t=>a(o,t),t=>a(i,t)),r.aborted&&A()})):n}function eMe(n,r){return r.questions[r.index]&&Oi(n?.senderId)===r.actor&&(n.kind!=="group"||n.rawEventType==="GROUP_AT_MESSAGE_CREATE")&&!Gy(n)&&Oi(AC(n))}function ik(){return{messagesReceived:0,messagesReplied:0,messagesRejected:0,artifactsSent:0,artifactSendErrors:0,lastMessageAt:null,lastReplyAt:null,lastRejectedAt:null,lastError:null}}var Fy=class{#e;#A;#t;#r;#o;#i;#I;#s;#l;#a;#n=new Map;#g=new Map;#c=new Map;#p=new Set;#h=new Set;#d=new Set;#E;constructor({bot:r,ownerUserOpenid:o,harness:i,state:e,status:a=ik(),logger:A=console,replyTimeoutMs:t=6e5,signal:s,fetchImpl:g=fetch,fileUploadTimeoutMs:c=S8}){if(!r||typeof r.sendText!="function")throw new TypeError("QQ bot client is required");if(!o)throw new TypeError("QQ scanner identity is required");if(!i||!e)throw new TypeError("Harness client and state store are required");if(typeof g!="function")throw new TypeError("fetchImpl must be a function");if(!Number.isInteger(c)||c<1)throw new TypeError("fileUploadTimeoutMs must be a positive integer");this.#e=r,this.#A=o,this.#t=i,this.#r=e,this.#o=a,this.#i=A,this.#I=t,this.#s=s,this.#l=g,this.#a=Math.min(c,S8),this.#E=new qa({label:"qq",logger:A})}get status(){return structuredClone(this.#o)}accept(r){if(this.#s?.aborted)return Promise.resolve();let o=Oi(r?.messageId),i=Oi(r?.senderId);if(!o||!i||r?.senderIsBot===!0||!["c2c","group"].includes(r?.kind)||this.#r.hasSeen(o)||this.#p.has(o))return Promise.resolve();let e=Vbe(r);this.#p.add(o),r.kind==="c2c"&&(this.#A==="*"||i===this.#A)&&r.replyTarget?.scope==="c2c"&&Oi(r.replyTarget.targetId)===i&&Li(this.#r,r.replyTarget);let a=this.#g.get(e),A=AC(r),t=hi(A)?xi:Ci(A)?Ji:ui(A)?qi:null,s=this.#A==="*"||i===this.#A,g=r.kind!=="group"||r.rawEventType==="GROUP_AT_MESSAGE_CREATE";if(t&&s&&g){let I;return I=this.#u(r,o,e,A,t).catch(E=>{if(!(E?.code==="turn-stopped"||this.#s?.aborted))return this.#o.lastError=E?.message??String(E),this.#i.error?.("[dsh-im:qq] failed to process a command:",E),this.#e.sendText(r.replyTarget,"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002").catch(()=>{})}).finally(()=>{this.#p.delete(o),this.#d.delete(I)}),this.#d.add(I),I}let c=this.#E.claimReply({key:e,actor:i,messageId:o,text:Gy(r)?"":AC(r),addressed:r.kind!=="group"||r.rawEventType==="GROUP_AT_MESSAGE_CREATE",hasPendingQuestion:!!a,questionCompletion:a?.submitting||a?.claimedReplyMessageId?a.queue:null,isQuestionPending:()=>this.#g.has(e),send:I=>this.#e.sendText(r.replyTarget,I)});if(c){let I;return I=c.process(async()=>this.#r.hasSeen(o)?!1:(await this.#r.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString(),!0)).finally(()=>{this.#p.delete(o),this.#h.delete(I)}),this.#h.add(I),I}if(a&&i!==a.actor)return this.#C(r,o,e);if(a?.submitting||a?.claimedReplyMessageId)return this.#C(r,o,e);if(a){eMe(r,a)&&(a.claimedReplyMessageId=o);let E=(a.queue??Promise.resolve()).catch(()=>{}).then(()=>this.#m(r,o,e,a)).catch(B=>this.#O(r,o,B)).finally(()=>{this.#p.delete(o),a.claimedReplyMessageId===o&&(a.claimedReplyMessageId=null),a.queue===E&&(a.queue=null)});return a.queue=E,E}return this.#C(r,o,e)}#C(r,o,i,{releaseMessageId:e=!0,alreadyRecorded:a=!1}={}){let t=(this.#n.get(i)??Promise.resolve()).catch(()=>{}).then(()=>this.#Q(r,i,{alreadyRecorded:a})).finally(()=>{e&&this.#p.delete(o),this.#n.get(i)===t&&this.#n.delete(i)});return this.#n.set(i,t),t}async waitForIdle(){await Promise.allSettled([...this.#n.values(),...[...this.#g.values()].flatMap(r=>r.queue?[r.queue]:[]),...this.#h,...this.#d])}async#u(r,o,i,e,a){if(this.#s?.throwIfAborted(),this.#r.hasSeen(o))return;await this.#r.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString();let A=await a(e,this.#t,this.#r,i,{signal:this.#s,hasImages:Gy(r),pendingInteraction:this.#g.has(i)||this.#E.hasPending(i),control:{owner:this,key:i}});A?.stopped&&await Promise.allSettled([this.#N(i),this.#E.closeRoute(i)]);for(let t of A?.messages??[A?.message])t&&await this.#e.sendText(r.replyTarget,t);this.#o.lastError=null}async#B(r,o,i=[],e=null){if(i.length===0)return{receipt:e,failureNoticeVisible:!1};let a=e?[e]:[],A=!1;for(let t of i){this.#s?.throwIfAborted();try{if(typeof this.#e.sendFile!="function"){let I=new Error("QQ file delivery is unavailable");throw I.code="artifact-provider-unavailable",I}let s=await zi(t,{signal:this.#s});this.#s?.throwIfAborted();let g;try{let I=AbortSignal.timeout(this.#a),E=this.#s?AbortSignal.any([this.#s,I]):I,B=this.#e.sendFile(r,{buffer:s.bytes},{fileName:s.fileName,onProgress:()=>this.#s?.throwIfAborted()});ln(s,B),g=await $be(B,E)}catch(I){throw this.#s?.aborted?b8(this.#s):D8(I,{dispatched:!0})}this.#s?.throwIfAborted();let c=Oi(g?.message?.id);a.push(wt({deliveryId:s.deliveryKey,presentation:"qq-file",providerMessageIds:c?[c]:[],artifacts:[{artifactId:s.artifactId,outcome:"sent"}]})),this.#o.artifactsSent=(this.#o.artifactsSent??0)+1}catch(s){if(this.#s?.aborted)throw s;let g=D8(s);this.#o.artifactSendErrors=(this.#o.artifactSendErrors??0)+1,this.#i.warn?.(`[dsh-im:qq] result file delivery failed (${g?.code??g?.name??"unknown"})`);let c=[];try{let I=await this.#e.sendText(r,Zbe(t?.fileName,g));A=!0,c=ha(I)}catch(I){if(this.#s?.aborted)throw I;this.#i.warn?.("[dsh-im:qq] unable to send the safe result-file failure notice")}a.push(Wi({artifactId:t?.artifactId??"unknown",deliveryId:t?.deliveryKey??t?.artifactId??"unknown",error:g,providerMessageIds:c}))}finally{Vi(t)}}return{receipt:Zi({deliveryId:o,presentation:e?"qq-text-and-files":"qq-files",receipts:a}),failureNoticeVisible:A}}async#Q(r,o,{alreadyRecorded:i=!1}={}){if(this.#s?.aborted)return;let e=Oi(r?.messageId),a=Oi(r?.senderId);if(!e||!a||r.senderIsBot===!0||!["c2c","group"].includes(r.kind))return;if(!i){if(this.#r.hasSeen(e))return;this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString()}if(this.#A!=="*"&&a!==this.#A){this.#o.messagesRejected+=1,this.#o.lastRejectedAt=new Date().toISOString();return}if(r.kind==="group"&&r.rawEventType!=="GROUP_AT_MESSAGE_CREATE")return;let A=r.replyTarget,t=Wbe(r,{fetchImpl:this.#l}),s=t.content,g=pr(t),c=null;try{if(!s&&!g){await this.#e.sendText(A,"\u76EE\u524D\u652F\u6301\u6587\u5B57\u548C\u56FE\u7247\u6D88\u606F\u3002"),await this.#r.markSeen(e);return}let I=s.toLowerCase();if(!g&&I==="/help"){await this.#e.sendText(A,zbe),await this.#r.markSeen(e);return}if(!g&&I==="/status"){await this.#t.ensureRunning({signal:this.#s}),await this.#e.sendText(A,"QQ \u673A\u5668\u4EBA\u4E0E DeepSeek Harness \u8FDE\u63A5\u6B63\u5E38\u3002"),await this.#r.markSeen(e);return}if(!g&&I==="/new"){await this.#r.clearSession(o),await this.#e.sendText(A,"\u5DF2\u5F00\u542F\u65B0\u4F1A\u8BDD\u3002\u8BF7\u53D1\u9001\u4F60\u7684\u95EE\u9898\u3002"),await this.#r.markSeen(e);return}let E=g?null:await ji(s,this.#t,o);if(E){for(let H of E.messages??[E.message])await this.#e.sendText(A,H);await this.#r.markSeen(e);return}let B=g?null:await Yi(s,this.#t,this.#r,o,{signal:this.#s});if(B){await this.#e.sendText(A,B.message),await this.#r.markSeen(e);return}let f=g?await Hi(t,{signal:this.#s}):void 0,Q=!1;if(r.kind==="c2c"&&A?.msgId&&typeof this.#e.openStream=="function")try{c=this.#e.openStream({target:A})}catch(H){this.#i.warn?.("[dsh-im:qq] unable to start a QQ stream; using a text reply:",H)}let y,m=[];try{({answer:y,artifacts:m=[]}=await Va({harness:this.#t,state:this.#r,key:o,...g?{content:f}:{text:s},createOptions:{signal:this.#s},existsOptions:{signal:this.#s},askOptions:{timeoutMs:this.#I,signal:this.#s,control:{owner:this,key:o},onUpdate:c?async H=>{let j=H.type==="text"?H.text:H.type==="tool"?`\u6B63\u5728\u4F7F\u7528${H.name}\u2026`:H.text;j&&await c.update(j)}:void 0,onInteraction:H=>this.#f(H,{key:o,actor:a,target:A,requiresMention:r.kind==="group"}),onInteractionResolved:H=>this.#y(H)}}))}finally{await Promise.allSettled([this.#N(o),this.#E.closeRoute(o)])}this.#s?.throwIfAborted();let N=Xbe(y,m),_=null,v=null;try{if(c)try{await c.update(N),await c.complete(),Q=!0,_=wt({deliveryId:e,presentation:"qq-text",providerMessageIds:ha(c)})}catch(H){c.cancel?.(),this.#i.warn?.("[dsh-im:qq] QQ stream finalization failed; using a text reply:",H)}if(!Q){let H=await m8(this.#e,A,N,{logger:this.#i});_=wt({deliveryId:e,presentation:"qq-text",providerMessageIds:H.flatMap(j=>ha(j))})}}catch(H){v=H,this.#i.warn?.("[dsh-im:qq] final text delivery failed; continuing with result files:",H)}let K=await this.#B(A,e,m,_),x=K.receipt?.artifacts?.some(({outcome:H})=>H==="sent"||H==="unknown");if(v&&!x&&!K.failureNoticeVisible)throw v;return await this.#r.markSeen(e),this.#o.messagesReplied+=1,this.#o.lastReplyAt=new Date().toISOString(),this.#o.lastError=null,K.receipt}catch(I){if(I?.code==="turn-stopped"){if(c){try{await c.cancel?.()}catch(E){this.#i.warn?.("[dsh-im:qq] unable to cancel a stopped QQ stream:",E)}try{await this.#e.sendText(A,"\u5DF2\u505C\u6B62\u3002")}catch(E){this.#i.warn?.("[dsh-im:qq] unable to announce a stopped QQ turn:",E)}}await this.#r.markSeen(e);return}if(c?.cancel?.(),this.#s?.aborted)return;this.#o.lastError=I?.message??String(I),this.#i.error?.("[dsh-im:qq] failed to process an inbound message:",I);try{await this.#e.sendText(A,Ki(I)??"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"),await this.#r.markSeen(e)}catch(E){this.#i.error?.("[dsh-im:qq] failed to send the safe error reply:",E)}}}async#m(r,o,i,e){this.#s?.throwIfAborted();let a=this.#g.get(i),A=e.claimedReplyMessageId===o;if(!a||a!==e||a.submitting)return A&&(!a||a!==e)?this.#w(r,o):this.#C(r,o,i,{releaseMessageId:!1});if(this.#r.hasSeen(o)||(await this.#r.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString(),r.kind==="group"&&r.rawEventType!=="GROUP_AT_MESSAGE_CREATE"))return;let t=Oi(AC(r));if(!t||Gy(r)){await this.#e.sendText(r.replyTarget,"\u8BF7\u7528\u6587\u5B57\u56DE\u7B54\u5F53\u524D\u95EE\u9898\u3002");return}let s=this.#g.get(i);if(!s||s!==e||s.submitting){if(A&&(!s||s!==e)){await this.#e.sendText(r.replyTarget,Py);return}return this.#C(r,o,i,{releaseMessageId:!1,alreadyRecorded:!0})}if(s.target=r.replyTarget,s.needsPresentation){try{await this.#D(s)}catch{this.#o.lastError="QQ \u4EA4\u4E92\u95EE\u9898\u53D1\u9001\u5931\u8D25\u3002",this.#i.error?.("[dsh-im:qq] failed to retry an interaction question"),s.interaction.reconnect?.();return}let c=this.#g.get(i);if(!c||c!==e||c.submitting){if(A&&(!c||c!==e)){await this.#e.sendText(r.replyTarget,Py).catch(()=>{});return}return this.#C(r,o,i,{releaseMessageId:!1,alreadyRecorded:!0})}}let g=s.questions[s.index];if(g){if(s.answers.push(Ja(g,t)),s.index+=1,s.index{});return}if(this.#g.get(i)!==s)return;s.submitting=!1,s.answers.pop(),s.index-=1,this.#o.lastError="\u56DE\u7B54\u63D0\u4EA4\u5931\u8D25\u3002",this.#i.error?.("[dsh-im:qq] failed to answer a Harness interaction"),await this.#e.sendText(s.target,"\u56DE\u7B54\u63D0\u4EA4\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u5F53\u524D\u95EE\u9898\u7684\u7B54\u6848\u3002").catch(()=>{})}}}async#f(r,{key:o,actor:i,target:e,requiresMention:a}){if(r?.kind==="approval")return this.#E.handleRequested(r,{key:o,actor:i,requiresMention:a,send:c=>this.#e.sendText(e,c)});if(r?.kind!=="question")return;let A=r?.payload?.questions,t=typeof r?.interactionId=="string"?r.interactionId:r?.rpcId;if(typeof r?.rpcId!="string"||typeof t!="string"||typeof r.sessionId!="string"||!Array.isArray(A)||A.length===0||A.some(c=>!xa(c))){this.#i.warn?.("[dsh-im:qq] ignored an invalid Harness question interaction");return}if(r.recovered===!0){await r.respond({ok:!1,error:{code:"cancelled",message:"QQ safely cancelled an interaction left by an earlier client.",details:{}}}),await this.#e.sendText(e,"\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").catch(()=>{});return}let s=this.#g.get(o);if(s?.interactionId===t){s.interaction=r,s.needsPresentation&&await this.#D(s);return}if(this.#c.has(t))return;if(s){await r.respond({ok:!1,error:{code:"cancelled",message:"QQ is already handling another user interaction.",details:{}}});return}let g={kind:"question",interactionId:t,sessionId:r.sessionId,interaction:r,actor:i,requiresMention:a,questions:A,answers:[],index:0,target:e,queue:null,claimedReplyMessageId:null,presentationPromise:null,submitting:!1,needsPresentation:!0};this.#g.set(o,g),this.#c.set(t,o),await this.#D(g)}async#y(r){if(r?.kind==="approval"){await this.#E.handleResolved(r);return}let o=r?.interactionId;if(r?.kind!=="question"||typeof o!="string")return;let i=this.#c.get(o);i&&this.#M(i,o)}#D(r){if(!r.needsPresentation)return Promise.resolve();if(r.presentationPromise)return r.presentationPromise;let o=r.questions[r.index];if(!o)return Promise.resolve();let i=this.#e.sendText(r.target,ja(o,r.index,r.questions.length,{requiresMention:r.requiresMention})).then(()=>{r.needsPresentation=!1}).finally(()=>{r.presentationPromise===i&&(r.presentationPromise=null)});return r.presentationPromise=i,i}async#w(r,o){this.#r.hasSeen(o)||(await this.#r.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString(),await this.#e.sendText(r.replyTarget,Py).catch(()=>{}))}#R(r,o){let i=this.#g.get(r);return!i||o!==void 0&&i.interactionId!==o?null:(this.#g.delete(r),this.#c.delete(i.interactionId),i)}#M(r,o){return this.#R(r,o)!==null}async#N(r){let o=this.#R(r);if(!(!o||o.kind!=="question"))try{await o.interaction.respond({ok:!1,error:{code:"cancelled",message:"The QQ interaction ended before the user answered.",details:{}}},{signal:AbortSignal.timeout(5e3)})}catch(i){i?.code!=="interaction-not-pending"&&this.#i.warn?.("[dsh-im:qq] failed to cancel a pending Harness interaction")}}async#O(r,o,i){this.#s?.aborted||(this.#o.lastError=i?.message??String(i),this.#i.error?.("[dsh-im:qq] failed to process an interaction reply:",i),this.#r.hasSeen(o)||await this.#r.markSeen(o).catch(()=>{}),await this.#e.sendText(r.replyTarget,"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002").catch(()=>{}))}};function rMe(){let n=new Error("QQ WebSocket did not become ready in time");return n.code="connect-timeout",n}function aMe(){return{startedAt:null,ready:!1,qqConnectionState:"idle",harnessReachable:!1,lastCheckedAt:null,lastConnectedAt:null,lastError:null,...ik()}}var tC=class{#e;#A;#t;#r;#o;#i;#I;#s;#l;#a=aMe();#n=null;#g=null;#c=null;#p=null;#h=null;constructor({config:r,appSecret:o,harness:i,state:e,logger:a=console,replyTimeoutMs:A=6e5,connectTimeoutMs:t=2e4,createBot:s=c=>new AMe(c),typingMiddleware:g=tMe}){if(!r||!o||!i||!e)throw new TypeError("QqRuntime requires config, app secret, Harness, and state");this.#e=r,this.#A=o,this.#t=i,this.#r=e,this.#o=a,this.#i=A,this.#I=t,this.#s=s,this.#l=g}get status(){return structuredClone(this.#a)}async sendConnectionTest(r){if(!this.#a.ready||!this.#n)throw ar("QQ\u673A\u5668\u4EBA");let o=typeof this.#e.ownerUserOpenid=="string"?this.#e.ownerUserOpenid.trim():"",i=nd(this.#r),e=i?.scope==="c2c"&&typeof i.targetId=="string"?i.targetId.trim():"",a=e?{scope:"c2c",targetId:e}:o&&o!=="*"?{scope:"c2c",targetId:o}:null;if(!a)throw ar("QQ\u673A\u5668\u4EBA");return await this.#n.sendText(a,r),{sent:!0}}async start(){return this.#a.ready&&this.#n?this.status:this.#h?this.#h:(this.#h=this.#d().finally(()=>{this.#h=null}),this.#h)}async#d(){await this.stop(),this.#a.startedAt=new Date().toISOString(),this.#a.qqConnectionState="connecting",this.#a.lastError=null,await this.#t.ensureRunning(),this.#a.harnessReachable=!0;let r={error:(...E)=>this.#o.error?.(...E),warn:(...E)=>this.#o.warn?.(...E),info:(...E)=>this.#o.info?.(...E),debug:()=>{}},o=this.#s({appId:this.#e.appId,appSecret:this.#A,accountId:this.#e.botId,logger:r,transport:"websocket",tokenPrefetch:"sync"});if(!o||typeof o.start!="function"||typeof o.stop!="function")throw new TypeError("QQ bot factory returned an invalid client");let i=new AbortController;this.#c=i,this.#n=o,this.#g=new Fy({bot:o,ownerUserOpenid:this.#e.ownerUserOpenid,harness:this.#t,state:this.#r,status:this.#a,logger:this.#o,replyTimeoutMs:this.#i,signal:i.signal}),o.use?.(this.#l({keepAlive:!0,predicate:E=>this.#e.ownerUserOpenid==="*"||E?.message?.senderId===this.#e.ownerUserOpenid}));let e,a,A=new Promise((E,B)=>{e=E,a=B}),t=()=>{let E=Date.now();this.#a.ready=!0,this.#a.qqConnectionState="connected",this.#a.lastCheckedAt=E,this.#a.lastConnectedAt=E,this.#a.lastError=null,e()},s=E=>{this.#a.ready?(this.#a.lastError=E?.message??String(E),this.#o.warn?.(`[dsh-im:qq] bot ${this.#e.botId} connection error:`,E)):a(E)},g=(E,B)=>{let f=this.#g?.accept(B);f&&f.catch(Q=>{i.signal.aborted||this.#o.error?.(`[dsh-im:qq] bot ${this.#e.botId} message handling failed:`,Q)})};o.on("ready",t),o.on("resumed",t),o.on("error",s),o.on("message",g);let c=Promise.resolve().then(()=>o.start(i.signal));this.#p=c,c.catch(E=>{i.signal.aborted||(a(E),this.#a.ready=!1,this.#a.qqConnectionState="failed",this.#a.lastError=E?.message??String(E),this.#o.error?.(`[dsh-im:qq] bot ${this.#e.botId} connection stopped:`,E))});let I;try{return await Promise.race([A,new Promise((E,B)=>{I=setTimeout(()=>B(rMe()),this.#I)})]),this.#a.ready=!0,this.#a.qqConnectionState="connected",this.#a.lastCheckedAt=Date.now(),this.#a.lastConnectedAt=Date.now(),this.status}catch(E){throw this.#a.ready=!1,this.#a.qqConnectionState="failed",this.#a.lastError=E?.message??String(E),await this.stop(),E}finally{clearTimeout(I)}}async stop(){let r=this.#n,o=this.#g,i=this.#p;this.#c?.abort(),this.#c=null,this.#n=null,this.#g=null,this.#p=null;try{r?.stop()}catch(e){this.#o.warn?.(`[dsh-im:qq] bot ${this.#e.botId} failed to stop cleanly:`,e)}return await Promise.race([i?.catch(()=>{})??Promise.resolve(),new Promise(e=>setTimeout(e,2e3))]),await o?.waitForIdle(),this.#a.ready=!1,this.#a.qqConnectionState="idle",this.status}};import{startQrConnect as iMe}from"@tencent-connect/qqbot-connector";var Ly=class{#e;#A;constructor({start:r=iMe,source:o="deepseek-harness"}={}){if(typeof r!="function")throw new TypeError("QQ QR connector is required");this.#e=r,this.#A=o}start(r,{signal:o}={}){if(!r||typeof r.onSuccess!="function"||typeof r.onFailure!="function")throw new TypeError("QQ QR callbacks are required");return this.#e(r,{displayQrCodeToConsole:!1,source:this.#A,signal:o})}};import{mkdir as oMe,readFile as nMe,rename as sMe,unlink as gMe,writeFile as cMe}from"node:fs/promises";import{dirname as IMe}from"node:path";var Uy=Object.freeze({version:1,sessions:{},seenMessageIds:[]});function lMe(n){if(!n||typeof n!="object")return structuredClone(Uy);let r={};if(n.sessions&&typeof n.sessions=="object"&&!Array.isArray(n.sessions))for(let[o,i]of Object.entries(n.sessions))typeof o=="string"&&typeof i=="string"&&i&&(r[o]=i);return{version:1,sessions:r,seenMessageIds:Array.isArray(n.seenMessageIds)?n.seenMessageIds.filter(o=>typeof o=="string").slice(-1e3):[]}}var Hy=class{#e;#A=structuredClone(Uy);#t=Promise.resolve();constructor(r){this.#e=r}async load(){try{this.#A=lMe(JSON.parse(await nMe(this.#e,"utf8")))}catch(r){if(r?.code!=="ENOENT")throw r;this.#A=structuredClone(Uy),await this.#r()}return this}sessionFor(r){return this.#A.sessions[r]??null}async setSession(r,o){this.#A.sessions[r]=o,await this.#r()}async clearSession(r){delete this.#A.sessions[r],await this.#r()}async clearSessions(){this.#A.sessions={},await this.#r()}hasSeen(r){return this.#A.seenMessageIds.includes(r)}async markSeen(r){this.hasSeen(r)||(this.#A.seenMessageIds.push(r),this.#A.seenMessageIds.length>1e3&&this.#A.seenMessageIds.splice(0,this.#A.seenMessageIds.length-1e3),await this.#r())}snapshot(){return structuredClone(this.#A)}async remove(){try{await gMe(this.#e)}catch(r){if(r?.code!=="ENOENT")throw r}this.#A=structuredClone(Uy)}async#r(){let r=`${JSON.stringify(this.#A,null,2)} +`);function Vbe(n){return`${n.kind}:${n.kind==="group"?n.groupOpenid:n.senderId}`}function AC(n){return typeof n?.content=="string"?n.content.trim():""}function w8(n){let r=Oi(n?.content_type??n?.contentType);return r?r.split(";",1)[0].trim().toLowerCase():null}function O8(n){return w8(n)?.startsWith("image/")===!0||qbe.test(Oi(n?.filename)??"")}function Gy(n){return Array.isArray(n?.attachments)&&n.attachments.some(O8)}function Wbe(n,{fetchImpl:r=fetch}={}){if(typeof r!="function")throw new TypeError("fetchImpl must be a function");let o=[];for(let i of n?.attachments??[]){if(!O8(i))continue;let e=Oi(i?.url),a=Oi(i?.filename)??void 0,A=w8(i),t=Number(i?.size);o.push({...a?{name:a}:{},...A?.startsWith("image/")?{mediaType:A}:{},...Number.isFinite(t)&&t>=0?{size:t}:{},load:({signal:s,maxBytes:g})=>{if(!e)throw new Error("QQ image attachment has no download URL");return Ui(e,{fetchImpl:r,signal:s,maxBytes:g,allowedHosts:Jbe})}})}return{content:AC(n),images:o}}function Oi(n){return typeof n=="string"&&n.trim()?n.trim():null}function Zbe(n,r){let o=String(n??"\u7ED3\u679C\u6587\u4EF6").replace(/[\r\n]+/g," ").trim()||"\u7ED3\u679C\u6587\u4EF6";if(r?.name==="UploadDailyLimitExceededError")return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46 QQ \u4ECA\u65E5\u6587\u4EF6\u4E0A\u4F20\u989D\u5EA6\u5DF2\u7528\u5B8C\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`;switch(r?.code){case"artifact-delivery-uncertain":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u7684\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`;case"artifact-permission-required":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46\u5F53\u524D QQ \u673A\u5668\u4EBA\u6CA1\u6709\u6587\u4EF6\u6D88\u606F\u6743\u9650\u3002`;case"artifact-too-large":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u8D85\u8FC7\u5F53\u524D QQ \u673A\u5668\u4EBA\u53EF\u53D1\u9001\u7684\u6587\u4EF6\u5927\u5C0F\uFF0C\u672A\u53D1\u9001\u3002`;case"artifact-empty":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u4E3A\u7A7A\uFF0CQQ \u4E0D\u5141\u8BB8\u53D1\u9001\u7A7A\u6587\u4EF6\u3002`;case"artifact-changed":case"artifact-invalid":case"artifact-unavailable":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\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`;case"artifact-rate-limited":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u6682\u65F6\u88AB QQ \u9650\u6D41\uFF0C\u672A\u80FD\u53D1\u9001\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`;case"artifact-provider-rejected":return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46 QQ \u62D2\u7EDD\u4E86\u8BE5\u6587\u4EF6\u6216\u6587\u4EF6\u6D88\u606F\u3002`;default:return`\u7ED3\u679C\u6587\u4EF6\u300C${o}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46\u6682\u65F6\u672A\u80FD\u901A\u8FC7 QQ \u53D1\u9001\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`}}function Xbe(n,r){return typeof n=="string"&&n.trim()?n:r.length>0?"\u7ED3\u679C\u6587\u4EF6\u5DF2\u751F\u6210\u3002":n}function D8(n,{dispatched:r=!1}={}){if(n?.code?.startsWith?.("artifact-")||n?.name==="UploadDailyLimitExceededError")return n;let o=Number(n?.httpStatus),i=new Error("QQ file delivery failed",{cause:n});return o===401||o===403?i.code="artifact-permission-required":o===413?i.code="artifact-too-large":o===429?i.code="artifact-rate-limited":o===400||o===404?i.code="artifact-provider-rejected":i.code=r?"artifact-delivery-uncertain":"artifact-provider-failed",i}function b8(n){return n?.reason instanceof Error?n.reason:new DOMException("The operation was aborted","AbortError")}function $be(n,r){return r?(r.throwIfAborted(),new Promise((o,i)=>{let e=!1,a=(t,s)=>{e||(e=!0,r.removeEventListener("abort",A),t(s))},A=()=>a(i,b8(r));r.addEventListener("abort",A,{once:!0}),Promise.resolve(n).then(t=>a(o,t),t=>a(i,t)),r.aborted&&A()})):n}function eMe(n,r){return r.questions[r.index]&&Oi(n?.senderId)===r.actor&&(n.kind!=="group"||n.rawEventType==="GROUP_AT_MESSAGE_CREATE")&&!Gy(n)&&Oi(AC(n))}function ik(){return{messagesReceived:0,messagesReplied:0,messagesRejected:0,artifactsSent:0,artifactSendErrors:0,lastMessageAt:null,lastReplyAt:null,lastRejectedAt:null,lastError:null}}var Fy=class{#e;#A;#t;#r;#o;#i;#I;#s;#l;#a;#n=new Map;#g=new Map;#c=new Map;#p=new Set;#h=new Set;#d=new Set;#E;constructor({bot:r,ownerUserOpenid:o,harness:i,state:e,status:a=ik(),logger:A=console,replyTimeoutMs:t=6e5,signal:s,fetchImpl:g=fetch,fileUploadTimeoutMs:c=S8}){if(!r||typeof r.sendText!="function")throw new TypeError("QQ bot client is required");if(!o)throw new TypeError("QQ scanner identity is required");if(!i||!e)throw new TypeError("Harness client and state store are required");if(typeof g!="function")throw new TypeError("fetchImpl must be a function");if(!Number.isInteger(c)||c<1)throw new TypeError("fileUploadTimeoutMs must be a positive integer");this.#e=r,this.#A=o,this.#t=i,this.#r=e,this.#o=a,this.#i=A,this.#I=t,this.#s=s,this.#l=g,this.#a=Math.min(c,S8),this.#E=new qa({label:"qq",logger:A})}get status(){return structuredClone(this.#o)}accept(r){if(this.#s?.aborted)return Promise.resolve();let o=Oi(r?.messageId),i=Oi(r?.senderId);if(!o||!i||r?.senderIsBot===!0||!["c2c","group"].includes(r?.kind)||this.#r.hasSeen(o)||this.#p.has(o))return Promise.resolve();let e=Vbe(r);this.#p.add(o),r.kind==="c2c"&&(this.#A==="*"||i===this.#A)&&r.replyTarget?.scope==="c2c"&&Oi(r.replyTarget.targetId)===i&&Li(this.#r,r.replyTarget);let a=this.#g.get(e),A=AC(r),t=hi(A)?xi:Ci(A)?Ji:ui(A)?qi:null,s=this.#A==="*"||i===this.#A,g=r.kind!=="group"||r.rawEventType==="GROUP_AT_MESSAGE_CREATE";if(t&&s&&g){let I;return I=this.#u(r,o,e,A,t).catch(E=>{if(!(E?.code==="turn-stopped"||this.#s?.aborted))return this.#o.lastError=E?.message??String(E),this.#i.error?.("[dsh-im:qq] failed to process a command:",E),this.#e.sendText(r.replyTarget,"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002").catch(()=>{})}).finally(()=>{this.#p.delete(o),this.#d.delete(I)}),this.#d.add(I),I}let c=this.#E.claimReply({key:e,actor:i,messageId:o,text:Gy(r)?"":AC(r),addressed:r.kind!=="group"||r.rawEventType==="GROUP_AT_MESSAGE_CREATE",hasPendingQuestion:!!a,questionCompletion:a?.submitting||a?.claimedReplyMessageId?a.queue:null,isQuestionPending:()=>this.#g.has(e),send:I=>this.#e.sendText(r.replyTarget,I)});if(c){let I;return I=c.process(async()=>this.#r.hasSeen(o)?!1:(await this.#r.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString(),!0)).finally(()=>{this.#p.delete(o),this.#h.delete(I)}),this.#h.add(I),I}if(a&&i!==a.actor)return this.#C(r,o,e);if(a?.submitting||a?.claimedReplyMessageId)return this.#C(r,o,e);if(a){eMe(r,a)&&(a.claimedReplyMessageId=o);let E=(a.queue??Promise.resolve()).catch(()=>{}).then(()=>this.#m(r,o,e,a)).catch(B=>this.#O(r,o,B)).finally(()=>{this.#p.delete(o),a.claimedReplyMessageId===o&&(a.claimedReplyMessageId=null),a.queue===E&&(a.queue=null)});return a.queue=E,E}return this.#C(r,o,e)}#C(r,o,i,{releaseMessageId:e=!0,alreadyRecorded:a=!1}={}){let t=(this.#n.get(i)??Promise.resolve()).catch(()=>{}).then(()=>this.#Q(r,i,{alreadyRecorded:a})).finally(()=>{e&&this.#p.delete(o),this.#n.get(i)===t&&this.#n.delete(i)});return this.#n.set(i,t),t}async waitForIdle(){await Promise.allSettled([...this.#n.values(),...[...this.#g.values()].flatMap(r=>r.queue?[r.queue]:[]),...this.#h,...this.#d])}async#u(r,o,i,e,a){if(this.#s?.throwIfAborted(),this.#r.hasSeen(o))return;await this.#r.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString();let A=await a(e,this.#t,this.#r,i,{signal:this.#s,hasImages:Gy(r),pendingInteraction:this.#g.has(i)||this.#E.hasPending(i),control:{owner:this,key:i}});A?.stopped&&await Promise.allSettled([this.#N(i),this.#E.closeRoute(i)]);for(let t of A?.messages??[A?.message])t&&await this.#e.sendText(r.replyTarget,t);this.#o.lastError=null}async#B(r,o,i=[],e=null){if(i.length===0)return{receipt:e,failureNoticeVisible:!1};let a=e?[e]:[],A=!1;for(let t of i){this.#s?.throwIfAborted();try{if(typeof this.#e.sendFile!="function"){let I=new Error("QQ file delivery is unavailable");throw I.code="artifact-provider-unavailable",I}let s=await zi(t,{signal:this.#s});this.#s?.throwIfAborted();let g;try{let I=AbortSignal.timeout(this.#a),E=this.#s?AbortSignal.any([this.#s,I]):I,B=this.#e.sendFile(r,{buffer:s.bytes},{fileName:s.fileName,onProgress:()=>this.#s?.throwIfAborted()});ln(s,B),g=await $be(B,E)}catch(I){throw this.#s?.aborted?b8(this.#s):D8(I,{dispatched:!0})}this.#s?.throwIfAborted();let c=Oi(g?.message?.id);a.push(wt({deliveryId:s.deliveryKey,presentation:"qq-file",providerMessageIds:c?[c]:[],artifacts:[{artifactId:s.artifactId,outcome:"sent"}]})),this.#o.artifactsSent=(this.#o.artifactsSent??0)+1}catch(s){if(this.#s?.aborted)throw s;let g=D8(s);this.#o.artifactSendErrors=(this.#o.artifactSendErrors??0)+1,this.#i.warn?.(`[dsh-im:qq] result file delivery failed (${g?.code??g?.name??"unknown"})`);let c=[];try{let I=await this.#e.sendText(r,Zbe(t?.fileName,g));A=!0,c=ha(I)}catch(I){if(this.#s?.aborted)throw I;this.#i.warn?.("[dsh-im:qq] unable to send the safe result-file failure notice")}a.push(Wi({artifactId:t?.artifactId??"unknown",deliveryId:t?.deliveryKey??t?.artifactId??"unknown",error:g,providerMessageIds:c}))}finally{Vi(t)}}return{receipt:Zi({deliveryId:o,presentation:e?"qq-text-and-files":"qq-files",receipts:a}),failureNoticeVisible:A}}async#Q(r,o,{alreadyRecorded:i=!1}={}){if(this.#s?.aborted)return;let e=Oi(r?.messageId),a=Oi(r?.senderId);if(!e||!a||r.senderIsBot===!0||!["c2c","group"].includes(r.kind))return;if(!i){if(this.#r.hasSeen(e))return;this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString()}if(this.#A!=="*"&&a!==this.#A){this.#o.messagesRejected+=1,this.#o.lastRejectedAt=new Date().toISOString();return}if(r.kind==="group"&&r.rawEventType!=="GROUP_AT_MESSAGE_CREATE")return;let A=r.replyTarget,t=Wbe(r,{fetchImpl:this.#l}),s=t.content,g=pr(t),c=null;try{if(!s&&!g){await this.#e.sendText(A,"\u76EE\u524D\u652F\u6301\u6587\u5B57\u548C\u56FE\u7247\u6D88\u606F\u3002"),await this.#r.markSeen(e);return}let I=s.toLowerCase();if(!g&&I==="/help"){await this.#e.sendText(A,zbe),await this.#r.markSeen(e);return}if(!g&&I==="/status"){await this.#t.ensureRunning({signal:this.#s}),await this.#e.sendText(A,"QQ \u673A\u5668\u4EBA\u4E0E DeepSeek Harness \u8FDE\u63A5\u6B63\u5E38\u3002"),await this.#r.markSeen(e);return}if(!g&&I==="/new"){await this.#r.clearSession(o),await this.#e.sendText(A,"\u5DF2\u5F00\u542F\u65B0\u4F1A\u8BDD\u3002\u8BF7\u53D1\u9001\u4F60\u7684\u95EE\u9898\u3002"),await this.#r.markSeen(e);return}let E=g?null:await ji(s,this.#t,o);if(E){for(let H of E.messages??[E.message])await this.#e.sendText(A,H);await this.#r.markSeen(e);return}let B=g?null:await Yi(s,this.#t,this.#r,o,{signal:this.#s});if(B){await this.#e.sendText(A,B.message),await this.#r.markSeen(e);return}let f=g?await Hi(t,{signal:this.#s}):void 0,Q=!1;if(r.kind==="c2c"&&A?.msgId&&typeof this.#e.openStream=="function")try{c=this.#e.openStream({target:A})}catch(H){this.#i.warn?.("[dsh-im:qq] unable to start a QQ stream; using a text reply:",H)}let y,m=[];try{({answer:y,artifacts:m=[]}=await Va({harness:this.#t,state:this.#r,key:o,...g?{content:f}:{text:s},createOptions:{signal:this.#s},existsOptions:{signal:this.#s},askOptions:{timeoutMs:this.#I,signal:this.#s,control:{owner:this,key:o},onUpdate:c?async H=>{let j=H.type==="text"?H.text:H.type==="tool"?`\u6B63\u5728\u4F7F\u7528${H.name}\u2026`:null;j&&await c.update(j)}:void 0,onInteraction:H=>this.#f(H,{key:o,actor:a,target:A,requiresMention:r.kind==="group"}),onInteractionResolved:H=>this.#y(H)}}))}finally{await Promise.allSettled([this.#N(o),this.#E.closeRoute(o)])}this.#s?.throwIfAborted();let N=Xbe(y,m),_=null,v=null;try{if(c)try{await c.update(N),await c.complete(),Q=!0,_=wt({deliveryId:e,presentation:"qq-text",providerMessageIds:ha(c)})}catch(H){c.cancel?.(),this.#i.warn?.("[dsh-im:qq] QQ stream finalization failed; using a text reply:",H)}if(!Q){let H=await m8(this.#e,A,N,{logger:this.#i});_=wt({deliveryId:e,presentation:"qq-text",providerMessageIds:H.flatMap(j=>ha(j))})}}catch(H){v=H,this.#i.warn?.("[dsh-im:qq] final text delivery failed; continuing with result files:",H)}let K=await this.#B(A,e,m,_),x=K.receipt?.artifacts?.some(({outcome:H})=>H==="sent"||H==="unknown");if(v&&!x&&!K.failureNoticeVisible)throw v;return await this.#r.markSeen(e),this.#o.messagesReplied+=1,this.#o.lastReplyAt=new Date().toISOString(),this.#o.lastError=null,K.receipt}catch(I){if(I?.code==="turn-stopped"){if(c){try{await c.cancel?.()}catch(E){this.#i.warn?.("[dsh-im:qq] unable to cancel a stopped QQ stream:",E)}try{await this.#e.sendText(A,"\u5DF2\u505C\u6B62\u3002")}catch(E){this.#i.warn?.("[dsh-im:qq] unable to announce a stopped QQ turn:",E)}}await this.#r.markSeen(e);return}if(c?.cancel?.(),this.#s?.aborted)return;this.#o.lastError=I?.message??String(I),this.#i.error?.("[dsh-im:qq] failed to process an inbound message:",I);try{await this.#e.sendText(A,Ki(I)??"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"),await this.#r.markSeen(e)}catch(E){this.#i.error?.("[dsh-im:qq] failed to send the safe error reply:",E)}}}async#m(r,o,i,e){this.#s?.throwIfAborted();let a=this.#g.get(i),A=e.claimedReplyMessageId===o;if(!a||a!==e||a.submitting)return A&&(!a||a!==e)?this.#w(r,o):this.#C(r,o,i,{releaseMessageId:!1});if(this.#r.hasSeen(o)||(await this.#r.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString(),r.kind==="group"&&r.rawEventType!=="GROUP_AT_MESSAGE_CREATE"))return;let t=Oi(AC(r));if(!t||Gy(r)){await this.#e.sendText(r.replyTarget,"\u8BF7\u7528\u6587\u5B57\u56DE\u7B54\u5F53\u524D\u95EE\u9898\u3002");return}let s=this.#g.get(i);if(!s||s!==e||s.submitting){if(A&&(!s||s!==e)){await this.#e.sendText(r.replyTarget,Py);return}return this.#C(r,o,i,{releaseMessageId:!1,alreadyRecorded:!0})}if(s.target=r.replyTarget,s.needsPresentation){try{await this.#D(s)}catch{this.#o.lastError="QQ \u4EA4\u4E92\u95EE\u9898\u53D1\u9001\u5931\u8D25\u3002",this.#i.error?.("[dsh-im:qq] failed to retry an interaction question"),s.interaction.reconnect?.();return}let c=this.#g.get(i);if(!c||c!==e||c.submitting){if(A&&(!c||c!==e)){await this.#e.sendText(r.replyTarget,Py).catch(()=>{});return}return this.#C(r,o,i,{releaseMessageId:!1,alreadyRecorded:!0})}}let g=s.questions[s.index];if(g){if(s.answers.push(Ja(g,t)),s.index+=1,s.index{});return}if(this.#g.get(i)!==s)return;s.submitting=!1,s.answers.pop(),s.index-=1,this.#o.lastError="\u56DE\u7B54\u63D0\u4EA4\u5931\u8D25\u3002",this.#i.error?.("[dsh-im:qq] failed to answer a Harness interaction"),await this.#e.sendText(s.target,"\u56DE\u7B54\u63D0\u4EA4\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u5F53\u524D\u95EE\u9898\u7684\u7B54\u6848\u3002").catch(()=>{})}}}async#f(r,{key:o,actor:i,target:e,requiresMention:a}){if(r?.kind==="approval")return this.#E.handleRequested(r,{key:o,actor:i,requiresMention:a,send:c=>this.#e.sendText(e,c)});if(r?.kind!=="question")return;let A=r?.payload?.questions,t=typeof r?.interactionId=="string"?r.interactionId:r?.rpcId;if(typeof r?.rpcId!="string"||typeof t!="string"||typeof r.sessionId!="string"||!Array.isArray(A)||A.length===0||A.some(c=>!xa(c))){this.#i.warn?.("[dsh-im:qq] ignored an invalid Harness question interaction");return}if(r.recovered===!0){await r.respond({ok:!1,error:{code:"cancelled",message:"QQ safely cancelled an interaction left by an earlier client.",details:{}}}),await this.#e.sendText(e,"\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").catch(()=>{});return}let s=this.#g.get(o);if(s?.interactionId===t){s.interaction=r,s.needsPresentation&&await this.#D(s);return}if(this.#c.has(t))return;if(s){await r.respond({ok:!1,error:{code:"cancelled",message:"QQ is already handling another user interaction.",details:{}}});return}let g={kind:"question",interactionId:t,sessionId:r.sessionId,interaction:r,actor:i,requiresMention:a,questions:A,answers:[],index:0,target:e,queue:null,claimedReplyMessageId:null,presentationPromise:null,submitting:!1,needsPresentation:!0};this.#g.set(o,g),this.#c.set(t,o),await this.#D(g)}async#y(r){if(r?.kind==="approval"){await this.#E.handleResolved(r);return}let o=r?.interactionId;if(r?.kind!=="question"||typeof o!="string")return;let i=this.#c.get(o);i&&this.#M(i,o)}#D(r){if(!r.needsPresentation)return Promise.resolve();if(r.presentationPromise)return r.presentationPromise;let o=r.questions[r.index];if(!o)return Promise.resolve();let i=this.#e.sendText(r.target,ja(o,r.index,r.questions.length,{requiresMention:r.requiresMention})).then(()=>{r.needsPresentation=!1}).finally(()=>{r.presentationPromise===i&&(r.presentationPromise=null)});return r.presentationPromise=i,i}async#w(r,o){this.#r.hasSeen(o)||(await this.#r.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString(),await this.#e.sendText(r.replyTarget,Py).catch(()=>{}))}#R(r,o){let i=this.#g.get(r);return!i||o!==void 0&&i.interactionId!==o?null:(this.#g.delete(r),this.#c.delete(i.interactionId),i)}#M(r,o){return this.#R(r,o)!==null}async#N(r){let o=this.#R(r);if(!(!o||o.kind!=="question"))try{await o.interaction.respond({ok:!1,error:{code:"cancelled",message:"The QQ interaction ended before the user answered.",details:{}}},{signal:AbortSignal.timeout(5e3)})}catch(i){i?.code!=="interaction-not-pending"&&this.#i.warn?.("[dsh-im:qq] failed to cancel a pending Harness interaction")}}async#O(r,o,i){this.#s?.aborted||(this.#o.lastError=i?.message??String(i),this.#i.error?.("[dsh-im:qq] failed to process an interaction reply:",i),this.#r.hasSeen(o)||await this.#r.markSeen(o).catch(()=>{}),await this.#e.sendText(r.replyTarget,"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002").catch(()=>{}))}};function rMe(){let n=new Error("QQ WebSocket did not become ready in time");return n.code="connect-timeout",n}function aMe(){return{startedAt:null,ready:!1,qqConnectionState:"idle",harnessReachable:!1,lastCheckedAt:null,lastConnectedAt:null,lastError:null,...ik()}}var tC=class{#e;#A;#t;#r;#o;#i;#I;#s;#l;#a=aMe();#n=null;#g=null;#c=null;#p=null;#h=null;constructor({config:r,appSecret:o,harness:i,state:e,logger:a=console,replyTimeoutMs:A=6e5,connectTimeoutMs:t=2e4,createBot:s=c=>new AMe(c),typingMiddleware:g=tMe}){if(!r||!o||!i||!e)throw new TypeError("QqRuntime requires config, app secret, Harness, and state");this.#e=r,this.#A=o,this.#t=i,this.#r=e,this.#o=a,this.#i=A,this.#I=t,this.#s=s,this.#l=g}get status(){return structuredClone(this.#a)}async sendConnectionTest(r){if(!this.#a.ready||!this.#n)throw ar("QQ\u673A\u5668\u4EBA");let o=typeof this.#e.ownerUserOpenid=="string"?this.#e.ownerUserOpenid.trim():"",i=nd(this.#r),e=i?.scope==="c2c"&&typeof i.targetId=="string"?i.targetId.trim():"",a=e?{scope:"c2c",targetId:e}:o&&o!=="*"?{scope:"c2c",targetId:o}:null;if(!a)throw ar("QQ\u673A\u5668\u4EBA");return await this.#n.sendText(a,r),{sent:!0}}async start(){return this.#a.ready&&this.#n?this.status:this.#h?this.#h:(this.#h=this.#d().finally(()=>{this.#h=null}),this.#h)}async#d(){await this.stop(),this.#a.startedAt=new Date().toISOString(),this.#a.qqConnectionState="connecting",this.#a.lastError=null,await this.#t.ensureRunning(),this.#a.harnessReachable=!0;let r={error:(...E)=>this.#o.error?.(...E),warn:(...E)=>this.#o.warn?.(...E),info:(...E)=>this.#o.info?.(...E),debug:()=>{}},o=this.#s({appId:this.#e.appId,appSecret:this.#A,accountId:this.#e.botId,logger:r,transport:"websocket",tokenPrefetch:"sync"});if(!o||typeof o.start!="function"||typeof o.stop!="function")throw new TypeError("QQ bot factory returned an invalid client");let i=new AbortController;this.#c=i,this.#n=o,this.#g=new Fy({bot:o,ownerUserOpenid:this.#e.ownerUserOpenid,harness:this.#t,state:this.#r,status:this.#a,logger:this.#o,replyTimeoutMs:this.#i,signal:i.signal}),o.use?.(this.#l({keepAlive:!0,predicate:E=>this.#e.ownerUserOpenid==="*"||E?.message?.senderId===this.#e.ownerUserOpenid}));let e,a,A=new Promise((E,B)=>{e=E,a=B}),t=()=>{let E=Date.now();this.#a.ready=!0,this.#a.qqConnectionState="connected",this.#a.lastCheckedAt=E,this.#a.lastConnectedAt=E,this.#a.lastError=null,e()},s=E=>{this.#a.ready?(this.#a.lastError=E?.message??String(E),this.#o.warn?.(`[dsh-im:qq] bot ${this.#e.botId} connection error:`,E)):a(E)},g=(E,B)=>{let f=this.#g?.accept(B);f&&f.catch(Q=>{i.signal.aborted||this.#o.error?.(`[dsh-im:qq] bot ${this.#e.botId} message handling failed:`,Q)})};o.on("ready",t),o.on("resumed",t),o.on("error",s),o.on("message",g);let c=Promise.resolve().then(()=>o.start(i.signal));this.#p=c,c.catch(E=>{i.signal.aborted||(a(E),this.#a.ready=!1,this.#a.qqConnectionState="failed",this.#a.lastError=E?.message??String(E),this.#o.error?.(`[dsh-im:qq] bot ${this.#e.botId} connection stopped:`,E))});let I;try{return await Promise.race([A,new Promise((E,B)=>{I=setTimeout(()=>B(rMe()),this.#I)})]),this.#a.ready=!0,this.#a.qqConnectionState="connected",this.#a.lastCheckedAt=Date.now(),this.#a.lastConnectedAt=Date.now(),this.status}catch(E){throw this.#a.ready=!1,this.#a.qqConnectionState="failed",this.#a.lastError=E?.message??String(E),await this.stop(),E}finally{clearTimeout(I)}}async stop(){let r=this.#n,o=this.#g,i=this.#p;this.#c?.abort(),this.#c=null,this.#n=null,this.#g=null,this.#p=null;try{r?.stop()}catch(e){this.#o.warn?.(`[dsh-im:qq] bot ${this.#e.botId} failed to stop cleanly:`,e)}return await Promise.race([i?.catch(()=>{})??Promise.resolve(),new Promise(e=>setTimeout(e,2e3))]),await o?.waitForIdle(),this.#a.ready=!1,this.#a.qqConnectionState="idle",this.status}};import{startQrConnect as iMe}from"@tencent-connect/qqbot-connector";var Ly=class{#e;#A;constructor({start:r=iMe,source:o="deepseek-harness"}={}){if(typeof r!="function")throw new TypeError("QQ QR connector is required");this.#e=r,this.#A=o}start(r,{signal:o}={}){if(!r||typeof r.onSuccess!="function"||typeof r.onFailure!="function")throw new TypeError("QQ QR callbacks are required");return this.#e(r,{displayQrCodeToConsole:!1,source:this.#A,signal:o})}};import{mkdir as oMe,readFile as nMe,rename as sMe,unlink as gMe,writeFile as cMe}from"node:fs/promises";import{dirname as IMe}from"node:path";var Uy=Object.freeze({version:1,sessions:{},seenMessageIds:[]});function lMe(n){if(!n||typeof n!="object")return structuredClone(Uy);let r={};if(n.sessions&&typeof n.sessions=="object"&&!Array.isArray(n.sessions))for(let[o,i]of Object.entries(n.sessions))typeof o=="string"&&typeof i=="string"&&i&&(r[o]=i);return{version:1,sessions:r,seenMessageIds:Array.isArray(n.seenMessageIds)?n.seenMessageIds.filter(o=>typeof o=="string").slice(-1e3):[]}}var Hy=class{#e;#A=structuredClone(Uy);#t=Promise.resolve();constructor(r){this.#e=r}async load(){try{this.#A=lMe(JSON.parse(await nMe(this.#e,"utf8")))}catch(r){if(r?.code!=="ENOENT")throw r;this.#A=structuredClone(Uy),await this.#r()}return this}sessionFor(r){return this.#A.sessions[r]??null}async setSession(r,o){this.#A.sessions[r]=o,await this.#r()}async clearSession(r){delete this.#A.sessions[r],await this.#r()}async clearSessions(){this.#A.sessions={},await this.#r()}hasSeen(r){return this.#A.seenMessageIds.includes(r)}async markSeen(r){this.hasSeen(r)||(this.#A.seenMessageIds.push(r),this.#A.seenMessageIds.length>1e3&&this.#A.seenMessageIds.splice(0,this.#A.seenMessageIds.length-1e3),await this.#r())}snapshot(){return structuredClone(this.#A)}async remove(){try{await gMe(this.#e)}catch(r){if(r?.code!=="ENOENT")throw r}this.#A=structuredClone(Uy)}async#r(){let r=`${JSON.stringify(this.#A,null,2)} `,o=this.#t.then(async()=>{await oMe(IMe(this.#e),{recursive:!0,mode:448});let i=`${this.#e}.tmp`;await cMe(i,r,{encoding:"utf8",mode:384}),await sMe(i,this.#e)});this.#t=o.then(()=>{},()=>{}),await o}};var M8=Object.freeze([250,1e3,3e3,5e3,1e4,3e4]);function pMe(n){if(!Array.isArray(n)||n.length===0)return[...M8];let r=n.filter(o=>Number.isFinite(o)&&o>=0);return r.length>0?r:[...M8]}var Ky=class{#e;#A;#t;#r;#o;#i;#I;#s=null;#l=null;#a=0;#n=!1;#g=!1;#c;#p;constructor({controller:r,harness:o,logger:i=console,retryDelaysMs:e,healthyIntervalMs:a=15e3,setTimeoutImpl:A=setTimeout,clearTimeoutImpl:t=clearTimeout}){if(!r||typeof r.initialize!="function"||typeof r.status!="function")throw new TypeError("ConnectionSupervisor requires a controller");if(!o||typeof o.ensureRunning!="function")throw new TypeError("ConnectionSupervisor requires a Harness client");this.#e=r,this.#A=o,this.#t=i,this.#r=pMe(e),this.#o=Number.isFinite(a)&&a>=0?a:15e3,this.#i=A,this.#I=t,this.#c=new Promise(s=>{this.#p=s})}get ready(){return this.#c}start(){return this.#g||this.#n?this:(this.#g=!0,this.#h(0),this)}async close(){this.#n||(this.#n=!0,this.#s!==null&&this.#I(this.#s),this.#s=null,await this.#l?.catch(()=>{}),this.#p?.(null),this.#p=null)}#h(r){this.#n||(this.#s=this.#i(()=>{this.#s=null,this.#d()},r),this.#s?.unref?.())}async#d(){if(this.#n||this.#l)return;let r=this.#E();this.#l=r;try{await r}finally{this.#l===r&&(this.#l=null)}}async#E(){try{if(await this.#A.ensureRunning(),this.#n)return;let r=await this.#e.initialize();if(this.#n)return;this.#p?.(r),this.#p=null;let{configured:o,connected:i}=r.totals;if(i65535)throw new Error("dsh-im QQ requires an initialized DSH webServer port");return new URL(`http://127.0.0.1:${o}`)}function CMe(n){let r=sI(n.dshHome??process.env.DSH_HOME??rC(dMe(),".dsh")),o=sI(n.dataDir??rC(r,"integrations","dsh-qq"));return{config:sI(n.configPath??rC(o,"config.json")),bots:sI(n.botsDir??rC(o,"bots")),workspaces:sI(n.workspacesPath??rC(o,"workspaces.json"))}}async function nk(n,r={},o={}){if(!n?.credentials)throw new TypeError("dsh-im QQ requires ctx.credentials");if(!n?.webServer)throw new TypeError("dsh-im QQ requires ctx.webServer");let i=o.ConfigStore??Ty,e=o.StateStore??Hy,a=o.HarnessClient??ky,A=o.Controller??eC,t=o.Runtime??tC,s=o.QrAuth??Ly,g=o.createConnectionSupervisor??ok,c=typeof n.logger=="function"?n.logger("dsh-im:qq"):n.logger??console,I=()=>da(n),E=CMe(r),B=await new i(E.config).load(),f=sI(r.workspace??process.cwd()),Q=o.WorkspaceStore??Hr,y=o.workspaces??await new Q(E.workspaces,{defaultWorkspace:f}).load(),m=B.list();await y.reconcile(m.map(M=>M.botId)),await Promise.all(m.map(M=>y.ensure(M.botId,{defaultAgentPreset:r.agentPreset})));let N=typeof B.remove=="function"?Ca(B,{workspaces:y}):B,_=o.qrAuth??new s({source:r.qrSource??"deepseek-harness"}),v=new Map,K=M=>sI(E.bots,M,"state.json"),x=async M=>{let G=v.get(M);return G||(G=await new e(K(M)).load(),v.set(M,G)),G},H=fa(n,o.commandExecutor),{controlExecutor:j,sessionMaintenanceExecutor:q}=Qa(n,{controlExecutor:o.controlExecutor,sessionMaintenanceExecutor:o.sessionMaintenanceExecutor}),Y=new a({baseUrl:hMe(n.webServer,r.harnessBaseUrl),workspace:f,autostart:!1,dshBin:r.dshBin??"dsh",...H?{commandExecutor:H}:{},...j?{controlExecutor:j}:{},...q?{sessionMaintenanceExecutor:q}:{}}),k=new A({qrAuth:_,credentials:n.credentials,configStore:N,logger:c,createRuntime:async({botId:M,config:G,appSecret:ee})=>{let Ce=await x(M);await y.ensure(M,{defaultAgentPreset:r.agentPreset});let de=ua(Y,{botId:M,workspaces:y,state:Ce,agentPresetCatalog:I});return new t({config:G,appSecret:ee,harness:de.harness,state:de.state,replyTimeoutMs:r.replyTimeoutMs??6e5,connectTimeoutMs:r.connectTimeoutMs??2e4,logger:{error:(..._e)=>c.error?.(`[${M}]`,..._e),warn:(..._e)=>c.warn?.(`[${M}]`,..._e),info:(..._e)=>c.info?.(`[${M}]`,..._e),debug:(..._e)=>c.debug?.(`[${M}]`,..._e)}})},deleteState:async({botId:M})=>{let G=v.get(M);if(v.delete(M),G&&typeof G.remove=="function")await G.remove();else try{await EMe(K(M))}catch(ee){if(ee?.code!=="ENOENT")throw ee}}}),W=Ba(k,{workspaces:y,stateFor:x,agentPresetCatalog:I}),L=g({controller:W,harness:Y,logger:c,retryDelaysMs:r.retryDelaysMs,healthyIntervalMs:r.healthyIntervalMs}).start();return{controller:W,ready:L.ready,async close(){await L.close(),await W.close(),Y.stopManagedProcess()}}}import uMe from"qrcode";var N8="/qq",wr=Object.freeze({status:"connection.status",beginProvisioning:"provision.begin",pollProvisioning:"provision.poll",cancelProvisioning:"provision.cancel",bindCredentials:"bot.bind-credentials",reconnectBot:"bot.reconnect",deleteBot:"bot.delete",setWorkspace:Za,setAgentPreset:Xa}),T8=Object.freeze(Object.values(wr)),BMe=new Set(["appSecret","app_secret","secretRef","ownerUserOpenid","userOpenid","verificationUrl"]);function ck(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}function Cp(n,r){return ck(n)&&Object.keys(n).every(o=>r.includes(o))}function sk(n){return typeof n=="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(n)}function _8(n,r){return typeof n=="string"&&n.trim().length>0&&n.length<=r}function fMe(n,r){return ck(r)?n===wr.status?Cp(r,[])?null:"connection.status does not accept fields.":n===wr.beginProvisioning?Cp(r,["locale"])&&(r.locale===void 0||r.locale==="zh-CN")?null:"provision.begin received unsupported fields.":[wr.pollProvisioning,wr.cancelProvisioning].includes(n)?Cp(r,["attemptId"])&&sk(r.attemptId)?null:`${n} requires an attemptId.`:n===wr.bindCredentials?Cp(r,["appId","appSecret"])&&_8(r.appId,256)&&_8(r.appSecret,1024)?null:"bot.bind-credentials requires AppID and AppSecret.":n===wr.reconnectBot?Cp(r,["botId","sendTest"])&&sk(r.botId)&&(r.sendTest===void 0||r.sendTest===!0)?null:"bot.reconnect requires a botId and accepts only sendTest=true.":n===wr.deleteBot?Cp(r,["botId","confirm"])&&sk(r.botId)&&r.confirm===!0?null:"bot.delete requires a botId and confirm=true.":n===wr.setWorkspace?ya(r)?null:"\u8BF7\u8F93\u5165\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84\u3002":n===wr.setAgentPreset?Sa(r)?null:"\u8BF7\u9009\u62E9 Agent Preset\u3002":"Unknown QQ endpoint.":"Payload must be an object."}function Bp(n){if(Array.isArray(n))return n.map(Bp);if(!ck(n))return n;let r={};for(let[o,i]of Object.entries(n))BMe.has(o)||(r[o]=Bp(i));return r}async function QMe(n){return uMe.toDataURL(n,{type:"image/png",errorCorrectionLevel:"M",margin:2,width:320})}async function gk(n,r){return!n||typeof n.verificationUrl!="string"?Bp(n):Bp({...n,qrCodeDataUrl:await r(n.verificationUrl)})}async function up(n,r){let o=structuredClone(n);return o?.provisioning&&(o.provisioning=await gk(o.provisioning,r)),Bp(o)}function R8(n,{encodeQr:r=QMe}={}){for(let e of["status","startProvisioning","registrationStatus","cancelProvisioning","bindCredentials","reconnectBot","deleteBot"])if(typeof n?.[e]!="function")throw new TypeError(`A complete QQ controller is required (${e})`);let o=new Map,i=e=>{let a=o.get(e);return a||(o.size>=16&&o.delete(o.keys().next().value),a=Promise.resolve().then(()=>r(e)),o.set(e,a)),a};return async(e,a,A)=>{if(A?.aborted)return{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}};if(!T8.includes(e))return{ok:!1,error:{code:"bad-request",message:"Unknown QQ endpoint."}};let t=fMe(e,a);if(t)return{ok:!1,error:{code:"bad-request",message:t}};try{let s;if(e===wr.status)s=await up(await n.status(),i);else if(e===wr.beginProvisioning)s=await gk(await n.startProvisioning(),i);else if(e===wr.pollProvisioning){let g=await n.registrationStatus(a.attemptId);if(!g)return{ok:!1,error:{code:"bad-request",message:"The provisioning attempt no longer exists."}};s=await gk(g,i)}else if(e===wr.cancelProvisioning)s=Bp(await n.cancelProvisioning(a.attemptId));else if(e===wr.bindCredentials)s=await up(await n.bindCredentials(a),i);else if(e===wr.reconnectBot){let g=await n.reconnectBot(a.botId);if(A?.aborted)return{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}};let c;if(a.sendTest===!0)if(!(g?.bots?.some(E=>E?.botId===a.botId&&E?.connected===!0)===!0)||typeof n.sendConnectionTest!="function")c=Vt(ar("QQ\u673A\u5668\u4EBA"));else{let E=null;try{await n.sendConnectionTest(a.botId)}catch(B){E=B}c=Vt(E)}s=await up({...g,...c?{testMessage:c}:{}},i)}else if(e===wr.setWorkspace){if(typeof n.updateWorkspace!="function")throw new Error("Workspace update is unavailable");s=await up(await n.updateWorkspace(a.botId,a.workspace),i)}else if(e===wr.setAgentPreset){if(typeof n.updateAgentPreset!="function")throw new Error("Agent preset update is unavailable");s=await up(await n.updateAgentPreset(a.botId,a.agentPreset),i)}else s=await up(await n.deleteBot(a.botId),i);return A?.aborted?{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}:{ok:!0,value:s}}catch(s){let g=ma(s);return A?.aborted?{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}:{ok:!1,error:g??{code:"qq-operation-failed",message:"QQ \u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"}}}}}function Yy(n,r,o,i){if(!n?.connection?.rpc||typeof n.connection.rpc.handle!="function")throw new TypeError("DSH Host Connection RPC is required");return n.connection.rpc.handle(N8,R8(r,o),{authority:hr(i)})}async function k8(n,r={}){if(r?.controller)return Yy(n,r.controller,r.rpcOptions,r.rpcAuthority);let o=await nk(n,r,r.internals),i=Yy(n,o.controller,r.rpcOptions,r.rpcAuthority);return n.effect(()=>async()=>o.close(),"dsh-im: close QQ bot connections"),i}import{unlink as t_e}from"node:fs/promises";import{resolve as j8}from"node:path";import{createHash as yMe}from"node:crypto";import{mkdir as mMe,readFile as SMe,rename as DMe,unlink as wMe,writeFile as OMe}from"node:fs/promises";import{dirname as bMe}from"node:path";var Ik=Object.freeze({version:1,bots:Object.freeze([])}),v8=/^slack_[a-f0-9]{24}$/,MMe=/^DSH_SLACK_BOT_TOKEN_[A-F0-9]{24}$/,_Me=/^DSH_SLACK_APP_TOKEN_[A-F0-9]{24}$/;function go(n){return typeof n=="string"&&n.trim()?n.trim():null}function lk(n){let r=go(n);if(!r)throw new TypeError("platformId is required");let o=yMe("sha256").update(r).digest("hex").slice(0,24),i=o.toUpperCase();return{botId:`slack_${o}`,botTokenRef:`DSH_SLACK_BOT_TOKEN_${i}`,appTokenRef:`DSH_SLACK_APP_TOKEN_${i}`}}function pk(n){let r=go(n)??"",[o,i]=r.split(":");return o&&i?`${o.slice(0,5)}\u2022\u2022\u2022 \xB7 ${i.slice(0,5)}\u2022\u2022\u2022`:r?`${r.slice(0,6)}\u2022\u2022\u2022\u2022`:"Slack\u673A\u5668\u4EBA"}var xy=class{#e;#A=Ik;#t=Promise.resolve();constructor(r){this.#e=r}async load(){try{let r=this.#o(JSON.parse(await SMe(this.#e,"utf8")));if(!r)throw new Error("dsh-im Slack config contains invalid bot data");this.#A=r}catch(r){if(r?.code!=="ENOENT")throw r;this.#A=Ik}return this}list(){return structuredClone(this.#A.bots)}get(r){let o=this.#A.bots.find(i=>i.botId===r);return o?structuredClone(o):null}getByPlatformId(r){let o=this.#A.bots.find(i=>i.platformId===r);return o?structuredClone(o):null}async save(r){let o=this.#r(r);if(!o)throw new Error("Refusing to persist incomplete Slack bot data");return this.#i(i=>{if(i.find(A=>A.botId!==o.botId&&(A.platformId===o.platformId||A.botTokenRef===o.botTokenRef||A.appTokenRef===o.appTokenRef)))throw new Error("Duplicate Slack bot identity");let a=i.findIndex(A=>A.botId===o.botId);return a===-1?i.push(o):i[a]=o,structuredClone(o)})}async remove(r){if(!v8.test(r))throw new TypeError("Invalid Slack bot id");return this.#i(o=>{let i=o.findIndex(e=>e.botId===r);return i===-1?null:structuredClone(o.splice(i,1)[0])})}async clear(){let r=this.#t.then(async()=>{try{await wMe(this.#e)}catch(o){if(o?.code!=="ENOENT")throw o}this.#A=Ik});this.#t=r.then(()=>{},()=>{}),await r}#r(r){if(!r||typeof r!="object")return null;let o=go(r.botId),i=go(r.platformId),e=go(r.botTokenRef),a=go(r.appTokenRef),A=go(r.name);if(!o||!i||!e||!a||!A||!v8.test(o)||!MMe.test(e)||!_Me.test(a))return null;let t=lk(i);return t.botId!==o||t.botTokenRef!==e||t.appTokenRef!==a?null:Object.freeze({botId:o,platformId:i,botTokenRef:e,appTokenRef:a,name:A,username:go(r.username),teamId:go(r.teamId),teamName:go(r.teamName),createdAt:go(r.createdAt)??new Date().toISOString(),connectedAt:go(r.connectedAt)})}#o(r){if(!r||r.version!==1||!Array.isArray(r.bots))return null;let o=r.bots.map(A=>this.#r(A));if(o.some(A=>A===null))return null;let i=new Set,e=new Set,a=new Set;for(let A of o){if(i.has(A.botId)||e.has(A.platformId)||a.has(A.botTokenRef)||a.has(A.appTokenRef))return null;i.add(A.botId),e.add(A.platformId),a.add(A.botTokenRef),a.add(A.appTokenRef)}return Object.freeze({version:1,bots:Object.freeze(o)})}async#i(r){let o,i=this.#t.then(async()=>{let e=[...this.#A.bots];o=r(e);let a=Object.freeze({version:1,bots:Object.freeze(e)});await mMe(bMe(this.#e),{recursive:!0,mode:448});let A=`${this.#e}.tmp`;await OMe(A,`${JSON.stringify(a,null,2)} `,{encoding:"utf8",mode:384}),await DMe(A,this.#e),this.#A=a});return this.#t=i.then(()=>{},()=>{}),await i,o}};var NMe="https://slack.com/api/",oC="files.slack.com",TMe="slack.com",P8="/files-pri/",RMe=Object.freeze([oC]),kMe="/upload/",vMe=12e4;function On(n){return typeof n=="string"&&n.trim()?n.trim():null}function G8(n,r){let o=AbortSignal.timeout(r);return n?AbortSignal.any([n,o]):o}function aC(n){return n?.reason instanceof Error?n.reason:new DOMException("The operation was aborted","AbortError")}function PMe(n,r){if(!Number.isInteger(n)||n<1)throw new TypeError(`${r} must be a positive integer`);return n}function F8(n,r){return r?.providerCode!==void 0&&(n.providerCode=r.providerCode),Number.isInteger(r?.status)&&(n.status=r.status),n}function gI(n){let r="artifact-provider-failed",o="Slack file upload preparation failed";n?.code==="slack-file_upload_size_restricted"?(r="artifact-too-large",o="Slack rejected the result file because it is too large"):n?.code==="slack-missing-scope"?(r="artifact-permission-required",o="Slack file delivery requires the files:write scope"):n?.code?.startsWith?.("slack-")&&(r="artifact-provider-rejected",o="Slack rejected file upload preparation");let i=new Error(o,{cause:n});return i.code=r,F8(i,n)}function GMe(n){let r=new Error("Slack file completion result is uncertain",{cause:n});return r.code="artifact-delivery-uncertain",F8(r,n)}function FMe(n){return Number.isInteger(n)&&n>=300&&n<400}async function Ek(n){try{await n?.body?.cancel?.()}catch{}}function LMe(n){let r=new URL(n);if(r.protocol!=="https:")throw new Error("Image download URL must use HTTPS");if(r.username||r.password||r.port&&r.port!=="443")throw new Error("Image download URL is not hosted by the messaging platform");if(r.hostname===TMe&&r.pathname.startsWith(P8)&&(r.hostname=oC),r.hostname!==oC||!r.pathname.startsWith(P8))throw new Error("Image download URL is not hosted by the messaging platform");return r.hash="",r}function UMe(n){let r=new URL(n);if(r.protocol!=="https:"||r.username||r.password||r.port&&r.port!=="443"||r.hostname!==oC||!r.pathname.startsWith(kMe))throw new Error("Slack returned an unsafe file upload URL");return r.hash="",r}function HMe(n,r){let o=n?.headers?.get?.("location");if(!o)return null;try{return new URL(o,r)}catch{return null}}function KMe(n){let r=n?.headers?.get?.("x-oauth-scopes");return r==null?null:new Set(r.split(",").map(o=>o.trim()).filter(Boolean))}function YMe(n){return n?.protocol==="https:"&&!n.username&&!n.password&&(!n.port||n.port==="443")&&n.hostname.endsWith(".slack.com")&&n.hostname!==oC&&n.pathname==="/"&&n.searchParams.has("redir")}function xMe(n,r){return new Promise((o,i)=>{if(r?.aborted){i(r.reason??new DOMException("Aborted","AbortError"));return}let e=setTimeout(o,n);e?.unref?.(),r?.addEventListener("abort",()=>{clearTimeout(e),i(r.reason??new DOMException("Aborted","AbortError"))},{once:!0})})}function Mg(n,r){let o=On(n);if(!o||!/^[A-Z][A-Z0-9]{4,30}$/i.test(o))throw new TypeError(`Invalid Slack ${r}`);return o}function fp(n,r){let o=On(n);if(!o)throw new TypeError(`Slack ${r} is required`);return o}function iC(n,{trim:r=!0}={}){let o=typeof n=="string"?n:"",i=r?o.trim():o;if(!i)throw new TypeError("Slack message text is required");return i.replace(/<@([A-Z0-9]+)>/gi,"@$1").replace(/]*)?>/gi,"@$1")}function jMe(n,r,o){let i=On(r?.error)??"unknown_error",e=new Error(`Slack ${n} failed: ${i.replaceAll("_"," ")}`);return e.providerCode=i,["invalid_auth","not_authed","token_revoked","account_inactive"].includes(i)?e.code=o==="app"?"slack-invalid-app-token":"slack-invalid-bot-token":i==="missing_scope"?e.code="slack-missing-scope":i==="method_not_supported_for_channel_type"||i==="channel_type_not_supported"||i==="deprecated_endpoint"?e.code="slack-stream-unavailable":e.code=`slack-${i}`,e}function L8(n){return typeof n=="string"&&/^xoxb-[A-Za-z0-9-]{16,}$/.test(n.trim())}function U8(n){return typeof n=="string"&&/^xapp-[A-Za-z0-9-]{16,}$/.test(n.trim())}var nC=class{#e;#A;#t;#r;#o=null;#i;constructor({botToken:r,appToken:o,fetchImpl:i=fetch,baseUrl:e=NMe,fileUploadTimeoutMs:a=vMe}){if(r!==void 0&&!L8(r))throw new TypeError("Slack Bot Token is invalid");if(o!==void 0&&!U8(o))throw new TypeError("Slack App Token is invalid");if(!r&&!o)throw new TypeError("SlackApi requires a token");if(typeof i!="function")throw new TypeError("SlackApi requires fetch");this.#e=r?.trim(),this.#A=o?.trim(),this.#t=i,this.#r=new URL(e),this.#i=PMe(a,"fileUploadTimeoutMs")}authTest(r={}){return this.#I("auth.test",{...r,tokenKind:"bot"})}openConnection(r={}){return this.#I("apps.connections.open",{...r,tokenKind:"app",body:void 0})}postMessage({channelId:r,text:o,threadTs:i,signal:e}){return this.#I("chat.postMessage",{tokenKind:"bot",signal:e,body:{channel:Mg(r,"channel id"),text:iC(o),...i?{thread_ts:On(i)}:{},mrkdwn:!0,link_names:!1,unfurl_links:!1,unfurl_media:!1}})}updateMessage({channelId:r,ts:o,text:i,signal:e}){return this.#I("chat.update",{tokenKind:"bot",signal:e,body:{channel:Mg(r,"channel id"),ts:fp(o,"message timestamp"),text:iC(i),parse:"none",link_names:!1}})}startStream({channelId:r,threadTs:o,recipientTeamId:i,recipientUserId:e,markdownText:a,signal:A}){return this.#I("chat.startStream",{tokenKind:"bot",signal:A,body:{channel:Mg(r,"channel id"),thread_ts:fp(o,"thread timestamp"),...i?{recipient_team_id:Mg(i,"team id")}:{},...e?{recipient_user_id:Mg(e,"user id")}:{},...On(a)?{markdown_text:iC(a)}:{}}})}appendStream({channelId:r,ts:o,markdownText:i,signal:e}){return this.#I("chat.appendStream",{tokenKind:"bot",signal:e,body:{channel:Mg(r,"channel id"),ts:fp(o,"stream timestamp"),markdown_text:iC(i,{trim:!1})}})}stopStream({channelId:r,ts:o,markdownText:i,signal:e}){return this.#I("chat.stopStream",{tokenKind:"bot",signal:e,body:{channel:Mg(r,"channel id"),ts:fp(o,"stream timestamp"),...On(i)?{markdown_text:iC(i,{trim:!1})}:{}}})}async uploadFile({channelId:r,threadTs:o,file:i,signal:e}){if(!i||typeof i!="object"||typeof i.fileName!="string"||!i.fileName||!Buffer.isBuffer(i.bytes))throw new TypeError("A Slack file is required");if(e?.aborted)throw aC(e);let a=G8(e,this.#i),A;try{A=await this.#I("files.getUploadURLExternal",{tokenKind:"bot",signal:a,timeoutMs:this.#i,body:{filename:i.fileName,length:i.bytes.byteLength}})}catch(I){throw e?.aborted?aC(e):gI(I)}let t,s;try{t=fp(A?.file_id,"file id"),s=UMe(A?.upload_url)}catch(I){throw gI(I)}let g;try{g=await this.#t(s,{method:"POST",headers:{"content-type":i.mediaType??"application/octet-stream"},body:i.bytes,signal:a,redirect:"error"})}catch(I){throw e?.aborted?aC(e):gI(I)}if(!Number.isInteger(g?.status))throw gI(new Error("Slack file upload returned an invalid response"));if(g.status!==200){await Ek(g);let I=new Error(`Slack file upload failed with HTTP ${g.status}`);throw I.status=g.status,g.status===413?I.code="artifact-too-large":g.status===429?I.code="artifact-rate-limited":I.code="artifact-provider-rejected",I}await Ek(g);let c;try{c={files:[{id:t,title:i.fileName}],channel_id:Mg(r,"channel id"),...o?{thread_ts:fp(o,"thread timestamp")}:{}}}catch(I){throw gI(I)}if(a.aborted)throw e?.aborted?aC(e):gI(a.reason);try{let I=await this.#I("files.completeUploadExternal",{tokenKind:"bot",signal:a,timeoutMs:this.#i,retry:!1,body:c});if(!Array.isArray(I?.files)||!I.files.some(E=>On(E?.id)===t))throw new Error("Slack file completion did not confirm the uploaded file");return I}catch(I){throw e?.aborted?aC(e):I?.code==="slack-missing-scope"?gI(I):GMe(I)}}async downloadFile({url:r,signal:o,maxBytes:i}){if(!this.#e)throw new TypeError("Slack bot token is required for file download");let e=LMe(r);return Ui(e,{fetchImpl:async(A,t)=>{let s=await this.#t(A,t);if(!FMe(s?.status))return s;let g=HMe(s,A);if(this.#o&&!this.#o.has("files:read")||YMe(g))throw await Ek(s),new It("slack-file-access-required","Slack redirected a private file request to the workspace because file access was not granted","Slack \u672A\u6388\u6743\u673A\u5668\u4EBA\u8BFB\u53D6\u8BE5\u6587\u4EF6\u3002\u8BF7\u4E3A\u5E94\u7528\u6DFB\u52A0 files:read \u540E\u91CD\u65B0\u5B89\u88C5\uFF0C\u518D\u91CD\u65B0\u53D1\u9001\u56FE\u7247\u3002");return s},headers:{authorization:`Bearer ${this.#e}`},signal:o,maxBytes:i,allowedHosts:RMe})}async#I(r,{tokenKind:o,body:i,signal:e,timeoutMs:a=15e3,retry:A=!0}){let t=o==="app"?this.#A:this.#e;if(!t)throw new TypeError(`Slack ${o} token is required for ${r}`);let s=r==="files.getUploadURLExternal",g;try{g=await this.#t(new URL(r,this.#r),{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":i===void 0||s?"application/x-www-form-urlencoded;charset=utf-8":"application/json;charset=utf-8","user-agent":"DeepSeek-Harness-dsh-im (https://github.com/xmanrui/dsh-im, 0.2.2)"},...i===void 0?{}:{body:s?new URLSearchParams(i).toString():JSON.stringify(i)},signal:G8(e,a),redirect:"error"})}catch(I){throw I?.name==="AbortError"||I?.name==="TimeoutError"?I:new Error(`Slack ${r} transport failed`)}if(o==="bot"){let I=KMe(g);I&&(this.#o=I)}let c;try{c=await g.json()}catch{throw new Error(`Slack ${r} returned invalid JSON`)}if(g.status===429&&A){let I=Number(g.headers.get("retry-after"))||1;return await xMe(Math.min(1e4,Math.max(100,I*1e3)),e),this.#I(r,{tokenKind:o,body:i,signal:e,timeoutMs:a,retry:!1})}if(!g.ok||c?.ok!==!0){let I=jMe(r,c,o);throw I.status=g.status,I}return c}};async function H8({botToken:n,appToken:r},o={}){if(!L8(n)){let t=new TypeError("Slack Bot Token \u5FC5\u987B\u4EE5 xoxb- \u5F00\u5934\u3002");throw t.code="slack-invalid-bot-token",t}if(!U8(r)){let t=new TypeError("Slack App Token \u5FC5\u987B\u4EE5 xapp- \u5F00\u5934\u3002");throw t.code="slack-invalid-app-token",t}let i=new nC({botToken:n,appToken:r,...o}),[e,a]=await Promise.all([i.authTest(),i.openConnection()]);if(!e?.team_id||!e?.user_id||!e?.bot_id)throw new Error("Slack Bot Token \u6CA1\u6709\u8FD4\u56DE\u5B8C\u6574\u7684\u673A\u5668\u4EBA\u8EAB\u4EFD\u3002");let A;try{A=new URL(a?.url)}catch{A=null}if(!A||A.protocol!=="wss:"){let t=new Error("Slack App Token \u65E0\u6CD5\u521B\u5EFA Socket Mode \u8FDE\u63A5\uFF0C\u8BF7\u786E\u8BA4\u5DF2\u542F\u7528 Socket Mode \u548C connections:write\u3002");throw t.code="slack-socket-mode",t}return{platformId:`${e.team_id}:${e.user_id}`,name:On(e.user)??"DeepSeek Harness",username:On(e.user),teamId:String(e.team_id),teamName:On(e.team)}}var dk=Object.freeze({key:"slack",label:"Slack",connectionLabel:" Socket Mode \u957F\u8FDE\u63A5"}),jy=class extends pn{constructor(r){super({descriptor:dk,...r})}};function _s(n){return typeof n=="string"&&n.trim()?n.trim():null}function Jy(n,r){return Object.freeze({code:n,message:r})}var sC=class{#e;#A;#t;#r;#o;#i;#I=new Map;#s=new Map;#l=new Map;#a=0;#n=!1;constructor({credentials:r,configStore:o,inspectCredentials:i=H8,createRuntime:e,deleteState:a=async()=>{},logger:A=console}){if(!r||typeof r.resolve!="function"||typeof r.set!="function"||typeof r.unset!="function")throw new TypeError("Slack requires the DSH credential provider");if(!o||typeof o.list!="function"||typeof o.save!="function"||typeof o.remove!="function")throw new TypeError("Slack requires a config store");if(typeof i!="function"||typeof e!="function")throw new TypeError("Slack controller dependencies are incomplete");this.#e=r,this.#A=o,this.#t=i,this.#r=e,this.#o=a,this.#i=A}async initialize(){if(this.#n)return this.status();for(let r of this.#A.list())await this.#d(r.botId,async()=>{if(this.#n||this.#I.get(r.botId)?.status?.ready)return;let o=await this.#p(r);if(!o){this.#s.set(r.botId,Jy("missing-token","Slack\u673A\u5668\u4EBA\u51ED\u636E\u7F3A\u5931\uFF0C\u8BF7\u79FB\u9664\u540E\u91CD\u65B0\u63A5\u5165\u3002"));return}try{await this.#g(r,o),this.#s.delete(r.botId)}catch(i){this.#s.set(r.botId,Jy("connection-failed","Slack Socket Mode \u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5\u3002")),this.#i.warn?.(`[dsh-im:slack] bot ${r.botId} failed to initialize:`,i)}finally{this.#E()}});return this.status()}async bindCredentials({botToken:r,appToken:o}={}){if(this.#n)throw new Error("Slack controller is closed");let i=_s(r),e=_s(o);if(!i||!e)throw new TypeError("Slack Bot Token and App Token are required");let a=await this.#t({botToken:i,appToken:e}),A=_s(a?.platformId),t=_s(a?.name);if(!A||!t)throw new Error("Slack returned an invalid bot identity");let s=lk(A);return await this.#d(s.botId,async()=>{if(this.#n)throw new Error("Slack controller is closed");let g=this.#A.getByPlatformId(A),c=await this.#e.resolve(s.botTokenRef).catch(()=>{}),I=await this.#e.resolve(s.appTokenRef).catch(()=>{}),E={...s,platformId:A,name:t,username:_s(a.username),teamId:_s(a.teamId),teamName:_s(a.teamName),createdAt:g?.createdAt??new Date().toISOString(),connectedAt:new Date().toISOString()};try{await this.#e.set(s.botTokenRef,i),await this.#e.set(s.appTokenRef,e),await this.#A.save(E)}catch(B){throw await Promise.all([this.#h(s.botTokenRef,c),this.#h(s.appTokenRef,I)]),B}try{await this.#g(E,{botToken:i,appToken:e}),this.#s.delete(s.botId)}catch(B){this.#s.set(s.botId,Jy("connection-failed","Slack\u673A\u5668\u4EBA\u5DF2\u63A5\u5165\uFF0CSocket Mode \u8FDE\u63A5\u6682\u672A\u5C31\u7EEA\u3002")),this.#i.warn?.(`[dsh-im:slack] bot ${s.botId} credential connection failed:`,B)}this.#E()}),this.status()}async reconnectBot(r){let o=this.#A.get(r);if(!o)throw new Error("Unknown Slack bot");return await this.#d(r,async()=>{let i=await this.#p(o);if(!i)throw new Error("Slack bot credentials are missing");try{await this.#g(o,i),this.#s.delete(r)}catch(e){throw this.#s.set(r,Jy("connection-failed","Slack Socket Mode \u8FDE\u63A5\u4ECD\u672A\u5C31\u7EEA\uFF0C\u8BF7\u68C0\u67E5\u4E24\u4E2A Token\u3002")),e}finally{this.#E()}}),this.status()}async sendConnectionTest(r){let o=this.#A.get(r);if(!o)throw new Error("Unknown Slack bot");return this.#d(r,async()=>{let i=this.#I.get(r);if(!i?.status?.ready||typeof i.sendConnectionTest!="function"){let e=new Error("Slack\u673A\u5668\u4EBA\u5C1A\u672A\u8FDE\u63A5");throw e.code="test-target-unavailable",e}return await i.sendConnectionTest(Ia(`${o.name}\uFF08${pk(o.platformId)}\uFF09`,"Slack\u673A\u5668\u4EBA")),{sent:!0}})}async deleteBot(r){let o=this.#A.get(r);if(!o)throw new Error("Unknown Slack bot");return await this.#d(r,async()=>{let i=await this.#e.resolve(o.botTokenRef).catch(()=>{}),e=await this.#e.resolve(o.appTokenRef).catch(()=>{});await this.#c(r);try{await this.#e.unset(o.botTokenRef),await this.#e.unset(o.appTokenRef),await this.#A.remove(r)}catch(a){throw await Promise.all([this.#h(o.botTokenRef,i),this.#h(o.appTokenRef,e)]),i?.value&&e?.value&&await this.#g(o,{botToken:i.value,appToken:e.value}).catch(()=>{}),new Error("Unable to remove the Slack bot safely.",{cause:a})}await this.#o({botId:r,config:o}).catch(a=>{this.#i.warn?.(`[dsh-im:slack] bot ${r} state cleanup failed:`,a)}),this.#s.delete(r),this.#E()}),this.status()}status(){let r=this.#A.list().map(i=>{let e=this.#I.get(i.botId)?.status??null,a=e?.ready===!0&&e.connectionState==="connected"&&e.harnessReachable===!0,A=a?"connected":e?.connectionState==="connecting"?"connecting":this.#s.has(i.botId)||e?.connectionState==="failed"?"error":"offline";return{botId:i.botId,state:A,connected:a,configured:!0,bot:{name:i.name,username:i.username,teamName:i.teamName,idMasked:pk(i.platformId)},health:{status:a?"healthy":A==="error"?"error":"offline",summary:a?`Slack${dk.connectionLabel}\u8FD0\u884C\u6B63\u5E38`:A==="error"?"Slack\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5":"Slack\u8FDE\u63A5\u5F53\u524D\u79BB\u7EBF",lastCheckedAt:e?.lastCheckedAt??null,lastConnectedAt:e?.lastConnectedAt??null},stats:{messagesReceived:e?.messagesReceived??0,messagesReplied:e?.messagesReplied??0},error:structuredClone(this.#s.get(i.botId)??null)}}),o=r.filter(i=>i.connected).length;return{schemaVersion:1,revision:this.#a,state:r.length===0?"disconnected":o===r.length?"connected":o>0?"degraded":"offline",bots:r,totals:{configured:r.length,connected:o}}}async close(){this.#n||(this.#n=!0,await Promise.allSettled([...this.#l.values()]),await Promise.allSettled([...this.#I.keys()].map(r=>this.#c(r))))}async#g(r,{botToken:o,appToken:i}){if(this.#n)throw new Error("Slack controller is closed");if(await this.#c(r.botId),this.#n)throw new Error("Slack controller is closed");let e=await this.#r({botId:r.botId,config:r,botToken:o,appToken:i});if(!e||typeof e.start!="function"||typeof e.stop!="function")throw new TypeError("createRuntime returned an invalid Slack runtime");this.#I.set(r.botId,e);try{await e.start()}catch(a){throw await e.stop().catch(()=>{}),this.#I.delete(r.botId),a}}async#c(r){let o=this.#I.get(r);this.#I.delete(r),await o?.stop().catch(i=>{this.#i.warn?.(`[dsh-im:slack] bot ${r} failed to stop cleanly:`,i)})}async#p(r){let[o,i]=await Promise.all([this.#e.resolve(r.botTokenRef).catch(()=>{}),this.#e.resolve(r.appTokenRef).catch(()=>{})]),e=_s(o?.value),a=_s(i?.value);return e&&a?{botToken:e,appToken:a}:null}async#h(r,o){o?.value?await this.#e.set(r,o.value).catch(()=>{}):await this.#e.unset(r).catch(()=>{})}#d(r,o){let a=(this.#l.get(r)??Promise.resolve()).catch(()=>{}).then(o).finally(()=>{this.#l.get(r)===a&&this.#l.delete(r)});return this.#l.set(r,a),a}#E(){this.#a+=1}};var qy=class extends ir{constructor(r){super({...r,rpcIdPrefix:"slack",logPrefix:"dsh-slack"})}};var K8=Object.freeze([1e3,3e3,5e3,1e4,3e4]),Ck=38e3,Y8=11e3,JMe=new Set(["image/jpeg","image/png","image/webp","image/gif"]);function hk(n,r,o){if(typeof n.addEventListener=="function")n.addEventListener(r,o);else if(typeof n.on=="function")n.on(r,o);else throw new TypeError("Slack WebSocket does not support events")}function qMe(n){let r=n?.data??n;return typeof r=="string"?r:Buffer.isBuffer(r)?r.toString("utf8"):r instanceof ArrayBuffer?Buffer.from(r).toString("utf8"):ArrayBuffer.isView(r)?Buffer.from(r.buffer,r.byteOffset,r.byteLength).toString("utf8"):null}function zMe(n){let r=new URL(n);if(r.protocol!=="wss:")throw new Error("Slack returned an insecure Socket Mode URL");return r.href}function VMe(n){return typeof n=="string"?n.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">"):""}function WMe(n,r){return VMe(n).replace(new RegExp(`<@${r}>`,"gi"),"").trim()}function ZMe(n,r){let o=typeof n?.mimetype=="string"?n.mimetype.toLowerCase():"",i=typeof n?.url_private_download=="string"&&n.url_private_download?n.url_private_download:n?.url_private;return!JMe.has(o)||typeof i!="string"||!i?null:{name:typeof n.name=="string"?n.name:void 0,mediaType:o,size:Number.isSafeInteger(n.size)&&n.size>=0?n.size:void 0,load:e=>r(i,e)}}function XMe(n,r,{loadFile:o=async()=>{throw new Error("Slack file downloader is unavailable")}}={}){let i=n?.event;if(!i||!n?.event_id||!i.channel||!i.user||!i.ts)return null;let e=i.type==="message"&&i.channel_type==="im",a=i.type==="app_mention";if(!e&&!a||i.subtype&&i.subtype!=="file_share"||i.bot_id||i.app_id)return null;let A=String(i.thread_ts??i.ts);return{messageId:String(n.event_id),senderId:String(i.user),senderIsBot:String(i.user)===String(r),kind:e?"direct":"group",conversationId:e?String(i.channel):`${i.channel}:${A}`,content:WMe(i.text??"",r),images:Array.isArray(i.files)?i.files.map(t=>ZMe(t,o)).filter(Boolean):[],addressed:e||a,replyTarget:{channelId:String(i.channel),threadTs:A,recipientUserId:String(i.user),recipientTeamId:String(i.user_team??n.team_id??"")},connectionTestTarget:{channelId:String(i.channel)}}}function $Me(n){return typeof n=="string"&&/^正在使用.+…$/.test(n.trim())}async function x8(n,r,o,i,e){if(i)for(let a=0;a{let y=Xi(Q,Ck)[0]??"";if(!y||!y.startsWith(A))return;let m=y.slice(A.length);m&&(await x8(n,r,a,m,o),A=y)},f=()=>{I||c||s!==null||g||!t||(s=setTimeout(()=>{s=null;let Q=t;t="",g=B(Q).catch(y=>{c=!0,i.warn?.("[dsh-im:slack] streaming append failed:",y)}).finally(()=>{g=null,f()})},350),s?.unref?.())};return{messageId:a,get providerMessageIds(){return[...E]},update(Q){I||c||typeof Q!="string"||!Q.trim()||$Me(Q)||(t=Q,f())},async finish(Q){if(I)throw new Error("Slack message stream is already closed");I=!0,s!==null&&clearTimeout(s),s=null,t="",await g?.catch(()=>{});let y=Xi(Q,Ck),m=y[0]??"\u5904\u7406\u5B8C\u6210\u3002";!c&&m.startsWith(A)?(await x8(n,r,a,m.slice(A.length),o),await n.stopStream({channelId:r.channelId,ts:a,signal:o})):(await n.stopStream({channelId:r.channelId,ts:a,signal:o}).catch(()=>{}),await n.updateMessage({channelId:r.channelId,ts:a,text:m,signal:o}));for(let N of y.slice(1)){let _=await n.postMessage({channelId:r.channelId,threadTs:r.threadTs,text:N,signal:o});typeof _?.ts=="string"&&_.ts&&E.push(_.ts)}},cancel(){I=!0,t="",s!==null&&clearTimeout(s),s=null,n.stopStream({channelId:r.channelId,ts:a,signal:o}).catch(()=>{})}}}var uk=class{#e;#A;#t;constructor({api:r,signal:o,logger:i}){this.#e=r,this.#A=o,this.#t=i}async sendText(r,o){let i=Xi(o,Ck),e=[];for(let a of i){let A=await this.#e.postMessage({channelId:r.channelId,threadTs:r.threadTs,text:a,signal:this.#A});typeof A?.ts=="string"&&A.ts&&e.push(A.ts)}return{providerMessageIds:e}}openStream(r){return e_e({api:this.#e,target:r,signal:this.#A,logger:this.#t})}sendFile(r,o){return this.#e.uploadFile({channelId:r.channelId,threadTs:r.threadTs,file:o,signal:this.#A})}};function A_e(){return{startedAt:null,ready:!1,connectionState:"idle",harnessReachable:!1,lastCheckedAt:null,lastConnectedAt:null,lastError:null,...fi()}}var gC=class{#e;#A;#t;#r;#o;#i;#I;#s;#l;#a;#n=A_e();#g=null;#c=null;#p=null;#h=null;#d=null;#E=null;#C=0;#u=0;#B=!0;#Q=null;constructor({config:r,botToken:o,appToken:i,harness:e,state:a,logger:A=console,replyTimeoutMs:t=6e5,connectTimeoutMs:s=2e4,createApi:g=I=>new nC(I),createWebSocket:c=I=>new WebSocket(I)}){if(!r||!o||!i||!e||!a)throw new TypeError("SlackRuntime requires config, both tokens, Harness, and state");if(typeof c!="function")throw new TypeError("SlackRuntime requires WebSocket");this.#e=r,this.#A=o,this.#t=i,this.#r=e,this.#o=a,this.#i=A,this.#I=t,this.#s=s,this.#l=g,this.#a=c}get status(){return structuredClone(this.#n)}async sendConnectionTest(r){if(!this.#n.ready||!this.#c){let o=new Error("Slack bot is not connected");throw o.code="test-target-unavailable",o}return this.#c.sendConnectionTest(r)}async start(){return this.#n.ready&&this.#h?this.status:this.#Q?this.#Q:(this.#Q=this.#m().finally(()=>{this.#Q=null}),this.#Q)}async#m(){await this.stop(),this.#B=!1,this.#C=0,this.#n.startedAt=new Date().toISOString(),this.#n.connectionState="connecting",this.#n.lastError=null,await this.#r.ensureRunning(),this.#n.harnessReachable=!0;let r=new AbortController;this.#p=r;let o=this.#l({botToken:this.#A,appToken:this.#t});this.#g=o;try{let i=await o.authTest({signal:r.signal});if(`${i?.team_id}:${i?.user_id}`!==this.#e.platformId)throw new Error("Slack Bot Token identity does not match the saved bot");let e=new uk({api:o,signal:r.signal,logger:this.#i});this.#c=new jy({bot:e,harness:this.#r,state:this.#o,status:this.#n,logger:this.#i,replyTimeoutMs:this.#I,signal:r.signal});let a;try{await Promise.race([this.#f(),new Promise((A,t)=>{a=setTimeout(()=>t(new Error("Slack Socket Mode did not become ready in time")),this.#s),a?.unref?.()})])}finally{clearTimeout(a)}return this.status}catch(i){throw this.#n.ready=!1,this.#n.connectionState="failed",this.#n.lastError=i?.message??String(i),await this.stop(),i}}async#f(){if(this.#B)throw new Error("Slack runtime is stopped");let r=await this.#g.openConnection({signal:this.#p?.signal});return this.#y(r?.url)}#y(r){if(this.#B)return Promise.reject(new Error("Slack runtime is stopped"));let o=++this.#u,i=this.#a(zMe(r));this.#h=i;let e=!1;return new Promise((a,A)=>{let t=s=>{if(e||o!==this.#u)return;e=!0,this.#d=s?.connection_info?.app_id??null,this.#C=0;let g=Date.now();this.#n.ready=!0,this.#n.connectionState="connected",this.#n.lastCheckedAt=g,this.#n.lastConnectedAt=g,this.#n.lastError=null,a()};hk(i,"message",s=>{if(o!==this.#u||this.#B)return;let g=qMe(s);if(!g)return;let c;try{c=JSON.parse(g)}catch{this.#i.warn?.("[dsh-im:slack] ignored malformed Socket Mode JSON");return}if(c.type==="hello"){t(c);return}if(c.envelope_id&&i.readyState===1&&(i.send(JSON.stringify({envelope_id:c.envelope_id})),this.#n.lastCheckedAt=Date.now()),c.type==="disconnect"){i.close(4e3,"Slack requested reconnect");return}if(c.type!=="events_api"||c.payload?.type!=="event_callback"||this.#d&&c.payload.api_app_id&&c.payload.api_app_id!==this.#d)return;let I=XMe(c.payload,this.#e.platformId.split(":")[1],{loadFile:(B,f)=>this.#g.downloadFile({url:B,...f})}),E=this.#c;I&&E&&E.accept(I).catch(B=>{o!==this.#u||this.#B||this.#i.error?.(`[dsh-im:slack] bot ${this.#e.botId} message handling failed:`,B)})}),hk(i,"close",(s={})=>{if(o!==this.#u)return;if(this.#h===i&&(this.#h=null),this.#B){e||A(new DOMException("Stopped","AbortError"));return}let g=Number(s.code)||0,c=new Error(`Slack Socket Mode closed (${g||"unknown"})`);this.#n.ready=!1,this.#n.connectionState="connecting",this.#n.lastError=c.message,e||(e=!0,A(c)),this.#D()}),hk(i,"error",()=>{o!==this.#u||this.#B||(this.#n.lastError="Slack Socket Mode WebSocket error")})})}#D(){if(this.#B||this.#E!==null)return;let r=K8[Math.min(this.#C,K8.length-1)];this.#C+=1,this.#E=setTimeout(()=>{this.#E=null,this.#f().catch(o=>{this.#B||(this.#i.warn?.("[dsh-im:slack] Socket Mode reconnect failed:",o),this.#D())})},r),this.#E?.unref?.()}async stop(){this.#B=!0,this.#u+=1,this.#p?.abort(),this.#p=null,this.#E!==null&&clearTimeout(this.#E),this.#E=null;let r=this.#h,o=this.#c;this.#h=null,this.#c=null,this.#g=null,this.#d=null;try{r&&r.readyState<2&&r.close(1e3,"Plugin stopped")}catch(i){this.#i.warn?.(`[dsh-im:slack] bot ${this.#e.botId} failed to close Socket Mode:`,i)}return await Promise.race([o?.waitForIdle()??Promise.resolve(),new Promise(i=>setTimeout(i,2e3))]),this.#n.ready=!1,this.#n.connectionState="idle",this.status}};var zy=class extends En{};async function Bk(n,r={},o={}){if(!n?.credentials)throw new TypeError("dsh-im slack requires ctx.credentials");if(!n?.webServer)throw new TypeError("dsh-im slack requires ctx.webServer");let i=o.ConfigStore??xy,e=o.StateStore??zy,a=o.HarnessClient??qy,A=o.Controller??sC,t=o.Runtime??gC,s=o.createConnectionSupervisor??Cl,g=typeof n.logger=="function"?n.logger("dsh-im:slack"):n.logger??console,c=()=>da(n),I=Qb(r,"slack"),E=await new i(I.config).load(),B=j8(r.workspace??process.cwd()),f=o.WorkspaceStore??Hr,Q=o.workspaces??await new f(I.workspaces,{defaultWorkspace:B}).load(),y=E.list();await Q.reconcile(y.map(W=>W.botId)),await Promise.all(y.map(W=>Q.ensure(W.botId,{defaultAgentPreset:r.agentPreset})));let m=typeof E.remove=="function"?Ca(E,{workspaces:Q}):E,N=new Map,_=W=>j8(I.bots,W,"state.json"),v=async W=>{let L=N.get(W);return L||(L=await new e(_(W)).load(),N.set(W,L)),L},K=fa(n,o.commandExecutor),{controlExecutor:x,sessionMaintenanceExecutor:H}=Qa(n,{controlExecutor:o.controlExecutor,sessionMaintenanceExecutor:o.sessionMaintenanceExecutor}),j=new a({baseUrl:Md(n.webServer,r.harnessBaseUrl),workspace:B,autostart:!1,dshBin:r.dshBin??"dsh",...K?{commandExecutor:K}:{},...x?{controlExecutor:x}:{},...H?{sessionMaintenanceExecutor:H}:{}}),q=new A({credentials:n.credentials,configStore:m,logger:g,...o.inspectCredentials?{inspectCredentials:o.inspectCredentials}:{},createRuntime:async({botId:W,config:L,botToken:M,appToken:G})=>{let ee=await v(W);await Q.ensure(W,{defaultAgentPreset:r.agentPreset});let Ce=ua(j,{botId:W,workspaces:Q,state:ee,agentPresetCatalog:c});return new t({config:L,botToken:M,appToken:G,harness:Ce.harness,state:Ce.state,replyTimeoutMs:r.replyTimeoutMs??6e5,connectTimeoutMs:r.connectTimeoutMs??2e4,logger:{error:(...de)=>g.error?.(`[${W}]`,...de),warn:(...de)=>g.warn?.(`[${W}]`,...de),info:(...de)=>g.info?.(`[${W}]`,...de),debug:(...de)=>g.debug?.(`[${W}]`,...de)}})},deleteState:async({botId:W})=>{let L=N.get(W);if(N.delete(W),L&&typeof L.remove=="function")await L.remove();else try{await t_e(_(W))}catch(M){if(M?.code!=="ENOENT")throw M}}}),Y=Ba(q,{workspaces:Q,stateFor:v,agentPresetCatalog:c}),k=s({channel:"slack",controller:Y,harness:j,logger:g,retryDelaysMs:r.retryDelaysMs,healthyIntervalMs:r.healthyIntervalMs}).start();return{controller:Y,ready:k.ready,async close(){await k.close(),await Y.close(),j.stopManagedProcess()}}}var q8="/slack",bi=Object.freeze({status:"connection.status",bindCredentials:"bot.bind-credentials",reconnectBot:"bot.reconnect",deleteBot:"bot.delete",setWorkspace:Za,setAgentPreset:Xa}),z8=Object.freeze(Object.values(bi)),r_e=new Set(["token","botToken","appToken","botTokenRef","appTokenRef","tokenRef","platformId","secret","secretRef"]);function Qk(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}function Vy(n,r){return Qk(n)&&Object.keys(n).every(o=>r.includes(o))}function J8(n){return typeof n=="string"&&/^slack_[a-f0-9]{24}$/.test(n)}function a_e(n){return typeof n=="string"&&/^xoxb-[A-Za-z0-9-]{16,}$/.test(n.trim())&&n.length<=4096}function i_e(n){return typeof n=="string"&&/^xapp-[A-Za-z0-9-]{16,}$/.test(n.trim())&&n.length<=4096}function o_e(n,r){return Qk(r)?n===bi.status?Vy(r,[])?null:"connection.status does not accept fields.":n===bi.bindCredentials?Vy(r,["botToken","appToken"])&&a_e(r.botToken)&&i_e(r.appToken)?null:"bot.bind-credentials requires xoxb Bot Token and xapp App Token.":n===bi.reconnectBot?Vy(r,["botId","sendTest"])&&J8(r.botId)&&(r.sendTest===void 0||typeof r.sendTest=="boolean")?null:"bot.reconnect requires a botId.":n===bi.deleteBot?Vy(r,["botId","confirm"])&&J8(r.botId)&&r.confirm===!0?null:"bot.delete requires a botId and confirm=true.":n===bi.setWorkspace?ya(r)?null:"\u8BF7\u8F93\u5165\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84\u3002":n===bi.setAgentPreset?Sa(r)?null:"\u8BF7\u9009\u62E9 Agent Preset\u3002":"Unknown Slack endpoint.":"Payload must be an object."}function fk(n){if(Array.isArray(n))return n.map(fk);if(!Qk(n))return n;let r={};for(let[o,i]of Object.entries(n))r_e.has(o)||(r[o]=fk(i));return r}function n_e(n){let r=ma(n);return r||(n?.code==="slack-invalid-bot-token"?{code:"invalid-bot-token",message:"Slack Bot Token \u65E0\u6548\uFF0C\u8BF7\u786E\u8BA4\u4F7F\u7528\u4EE5 xoxb- \u5F00\u5934\u7684\u4EE4\u724C\u3002"}:n?.code==="slack-invalid-app-token"?{code:"invalid-app-token",message:"Slack App Token \u65E0\u6548\uFF0C\u8BF7\u786E\u8BA4\u4F7F\u7528\u4EE5 xapp- \u5F00\u5934\u7684\u4EE4\u724C\u3002"}:n?.code==="slack-missing-scope"?{code:"missing-scope",message:"Slack \u5E94\u7528\u6743\u9650\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u91CD\u65B0\u5BFC\u5165 Manifest \u5E76\u5B89\u88C5\u5230\u5DE5\u4F5C\u533A\u3002"}:n?.code==="slack-socket-mode"?{code:"socket-mode-unavailable",message:n.message}:{code:"slack-operation-failed",message:"Slack \u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"})}function V8(n){for(let r of["status","bindCredentials","reconnectBot","deleteBot"])if(typeof n?.[r]!="function")throw new TypeError(`A complete Slack controller is required (${r})`);return async(r,o,i)=>{if(i?.aborted)return{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}};if(!z8.includes(r))return{ok:!1,error:{code:"bad-request",message:"Unknown Slack endpoint."}};let e=o_e(r,o);if(e)return{ok:!1,error:{code:"bad-request",message:e}};try{let a;if(r===bi.status)a=await n.status();else if(r===bi.bindCredentials)a=await n.bindCredentials(o);else if(r===bi.reconnectBot){if(a=await n.reconnectBot(o.botId),i?.aborted)return{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}};if(o.sendTest===!0){let A=null;try{if(a?.bots?.find(t=>t?.botId===o.botId)?.connected!==!0){let t=new Error("Bot is not connected");throw t.code="test-target-unavailable",t}if(typeof n.sendConnectionTest!="function"){let t=new Error("Connection test is unavailable");throw t.code="test-target-unavailable",t}await n.sendConnectionTest(o.botId)}catch(t){A=t}a={...a,testMessage:Vt(A)}}}else if(r===bi.setWorkspace){if(typeof n.updateWorkspace!="function")throw new Error("Workspace update is unavailable");a=await n.updateWorkspace(o.botId,o.workspace)}else if(r===bi.setAgentPreset){if(typeof n.updateAgentPreset!="function")throw new Error("Agent preset update is unavailable");a=await n.updateAgentPreset(o.botId,o.agentPreset)}else a=await n.deleteBot(o.botId);return i?.aborted?{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}:{ok:!0,value:fk(a)}}catch(a){return i?.aborted?{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}:{ok:!1,error:n_e(a)}}}}function Wy(n,r,o){if(!n?.connection?.rpc||typeof n.connection.rpc.handle!="function")throw new TypeError("DSH Host Connection RPC is required");return n.connection.rpc.handle(q8,V8(r),{authority:hr(o)})}async function W8(n,r={}){if(r?.controller)return Wy(n,r.controller,r.rpcAuthority);let o=await Bk(n,r,r.internals??{}),i=Wy(n,o.controller,r.rpcAuthority);return n.effect(()=>async()=>o.close(),"dsh-im: close Slack bot connections"),i}var Z8=Object.freeze({botPrefix:"telegram",tokenRefPrefix:"DSH_TELEGRAM_BOT_TOKEN"}),cC=Object.freeze({compatible:"compatible",privateAllowlist:"private-allowlist"}),s_e=/^[1-9]\d{0,15}$/;function X8(n){if(n===void 0)return Object.freeze([]);if(!Array.isArray(n))throw new TypeError("allowedUsers must be an array of numeric Telegram User IDs");let r=n.map(o=>{let i=typeof o=="number"&&Number.isSafeInteger(o)?String(o):typeof o=="string"?o.trim():"";if(!s_e.test(i))throw new TypeError("allowedUsers contains an invalid Telegram User ID");return i});return Object.freeze([...new Set(r)])}function _g(n={}){if(!n||typeof n!="object"||Array.isArray(n))throw new TypeError("Telegram access policy must be an object");let r=n.accessMode??cC.compatible;if(!Object.values(cC).includes(r))throw new TypeError("Telegram accessMode must be compatible or private-allowlist");return Object.freeze({accessMode:r,allowedUsers:X8(n.allowedUsers)})}function g_e(n){let r=Object.hasOwn(n,"accessMode"),o=Object.hasOwn(n,"allowedUsers");if(!r&&!o)return{};try{let i=_g(n);return{...r?{accessMode:i.accessMode}:{},...o||r?{allowedUsers:i.allowedUsers}:{}}}catch{return null}}function $8(n){return yd(n,Z8)}function ez(n){return LB(n,"Telegram\u673A\u5668\u4EBA")}var Zy=class extends dl{constructor(r){super(r,{channel:"Telegram",...Z8,normalizeBotExtension:g_e})}async save(r){let o=r?.platformId?this.getByPlatformId(String(r.platformId)):null;return super.save({...o,...r})}};var Xy=class extends ir{constructor(r){super({...r,rpcIdPrefix:"telegram",logPrefix:"dsh-telegram"})}};var c_e="https://api.telegram.org/",I_e=Object.freeze(["api.telegram.org"]),l_e=12e4;function Ng(n){return typeof n=="string"&&n.trim()?n.trim():null}function Az(n,r){let o=AbortSignal.timeout(r);return n?AbortSignal.any([n,o]):o}function tz(n){return n?.reason instanceof Error?n.reason:new DOMException("The operation was aborted","AbortError")}function p_e(n,r){if(!Number.isInteger(n)||n<1)throw new TypeError(`${r} must be a positive integer`);return n}function rz(n,r){return r?.providerCode!==void 0&&(n.providerCode=r.providerCode),r?.retry_after!==void 0&&(n.retry_after=r.retry_after,n.retryAfter=r.retry_after),Number.isInteger(r?.status)&&(n.status=r.status),n}function E_e(n){let r=Number(n?.providerCode),o=Number(n?.status),i=Ng(n?.message)??"",e="artifact-provider-rejected",a="Telegram rejected the document.";r===401||r===403||o===401||o===403?(e="artifact-permission-required",a="Telegram denied permission to send the document."):r===413||o===413||/(?:file|request|entity).{0,20}(?:too (?:big|large)|size limit)|too (?:big|large)/i.test(i)?(e="artifact-too-large",a="The document exceeds Telegram's size limit."):r===429||o===429?(e="artifact-rate-limited",a="Telegram rate-limited document delivery."):(r>=500||o>=500)&&(e="artifact-delivery-uncertain",a="Telegram document delivery result is uncertain.");let A=new Error(a,{cause:n});return A.code=e,rz(A,n)}function d_e(n){let r=new Error("Telegram document delivery result is uncertain",{cause:n});return r.code="artifact-delivery-uncertain",rz(r,n)}function h_e(n){return typeof n=="string"&&/^\d{5,20}:[A-Za-z0-9_-]{20,}$/.test(n.trim())}var C_e=/^[a-z0-9_]{1,32}$/;function u_e(n){return!!n&&typeof n=="object"&&!Array.isArray(n)&&typeof n.command=="string"&&C_e.test(n.command)&&typeof n.description=="string"&&n.description.length>=1&&n.description.length<=256}var yk=Object.freeze({type:"commands"}),IC=class{#e;#A;#t;#r;constructor({token:r,fetchImpl:o=fetch,baseUrl:i=c_e,fileUploadTimeoutMs:e=l_e}){if(!h_e(r))throw new TypeError("Telegram Bot Token is invalid");if(typeof o!="function")throw new TypeError("TelegramApi requires fetch");this.#e=r.trim(),this.#A=o,this.#t=new URL(i),this.#r=p_e(e,"fileUploadTimeoutMs")}async getMe(r={}){return this.#o("getMe",{},r)}async getWebhookInfo(r={}){return this.#o("getWebhookInfo",{},r)}async getUpdates({offset:r,timeout:o=25,signal:i}={}){let e={timeout:o,limit:100,allowed_updates:["message"],...Number.isSafeInteger(r)?{offset:r}:{}};return this.#o("getUpdates",e,{signal:i,timeoutMs:Math.max(1e4,(o+10)*1e3)})}async getFile({fileId:r,signal:o}={}){let i=Ng(r);if(!i||!/^[A-Za-z0-9_-]{1,512}$/.test(i))throw new TypeError("Telegram file id is invalid");return this.#o("getFile",{file_id:i},{signal:o})}async downloadFile({fileId:r,signal:o,maxBytes:i}={}){let e=await this.getFile({fileId:r,signal:o}),a=Ng(e?.file_path);if(!a||a.startsWith("/")||a.includes("\\")||a.includes("?")||a.includes("#"))throw new Error("Telegram returned an invalid file path");let A;try{A=a.split("/").map(s=>decodeURIComponent(s))}catch{throw new Error("Telegram returned an invalid file path")}if(A.some(s=>!s||s==="."||s===".."))throw new Error("Telegram returned an invalid file path");let t=new URL(this.#t);return t.pathname=`/file/bot${this.#e}/${a}`,Ui(t,{fetchImpl:this.#A,signal:o,maxBytes:i,allowedHosts:I_e})}async sendMessage({chatId:r,text:o,replyToMessageId:i,messageThreadId:e,signal:a}){return this.#o("sendMessage",{chat_id:r,text:o,link_preview_options:{is_disabled:!0},...i?{reply_parameters:{message_id:i,allow_sending_without_reply:!0}}:{},...e?{message_thread_id:e}:{}},{signal:a})}async sendDocument({chatId:r,file:o,replyToMessageId:i,messageThreadId:e,signal:a}){if(!o||typeof o!="object"||typeof o.fileName!="string"||!o.fileName||!Buffer.isBuffer(o.bytes))throw new TypeError("A Telegram document is required");let A=new FormData;if(A.append("chat_id",String(r)),A.append("document",new Blob([o.bytes],{type:o.mediaType??"application/octet-stream"}),o.fileName),i&&A.append("reply_parameters",JSON.stringify({message_id:i,allow_sending_without_reply:!0})),e&&A.append("message_thread_id",String(e)),a?.aborted)throw tz(a);let t=Az(a,this.#r);try{return await this.#o("sendDocument",A,{signal:t,timeoutMs:this.#r,multipart:!0})}catch(s){throw a?.aborted?tz(a):s?.code?.startsWith?.("telegram-")?E_e(s):d_e(s)}}async editMessageText({chatId:r,messageId:o,text:i,signal:e}){return this.#o("editMessageText",{chat_id:r,message_id:o,text:i,link_preview_options:{is_disabled:!0}},{signal:e})}async sendChatAction({chatId:r,messageThreadId:o,signal:i}){return this.#o("sendChatAction",{chat_id:r,action:"typing",...o?{message_thread_id:o}:{}},{signal:i})}async setMyCommands({commands:r,scope:o,languageCode:i,signal:e}={}){if(!Array.isArray(r)||r.length===0||r.some(a=>!u_e(a)))throw new TypeError("Telegram bot commands are invalid");return this.#o("setMyCommands",{commands:r,...o?{scope:o}:{},...Ng(i)?{language_code:Ng(i)}:{}},{signal:e})}async setChatMenuButton({menuButton:r=yk,signal:o}={}){if(!r||typeof r!="object"||Array.isArray(r))throw new TypeError("Telegram menu button is invalid");return this.#o("setChatMenuButton",{menu_button:r},{signal:o})}async#o(r,o,{signal:i,timeoutMs:e=15e3,multipart:a=!1}={}){let A=new URL(this.#t);A.pathname=`${A.pathname.replace(/\/$/,"")}/bot${this.#e}/${r}`;let t;try{t=await this.#A(A,{method:"POST",...a?{}:{headers:{"content-type":"application/json"}},body:a?o:JSON.stringify(o),signal:Az(i,e),redirect:"error"})}catch(g){throw g?.name==="AbortError"||g?.name==="TimeoutError"?g:new Error(`Telegram ${r} transport failed`)}let s;try{s=await t.json()}catch{let g=new Error(`Telegram ${r} returned invalid JSON`);throw g.status=t?.status,g}if(!t.ok||s?.ok!==!0){let g=Ng(s?.description),c=new Error(g??`Telegram ${r} failed`);c.code=Number.isInteger(s?.error_code)?`telegram-${s.error_code}`:"telegram-api-error",c.status=t.status,Number.isInteger(s?.error_code)&&(c.providerCode=s.error_code);let I=Number(s?.parameters?.retry_after);throw Number.isFinite(I)&&I>=0&&(c.retry_after=I,c.retryAfter=I),c}return s.result}};async function az(n,r={}){let i=await new IC({token:n,...r}).getMe();if(!i?.id||i?.is_bot!==!0)throw new Error("Telegram token does not belong to a bot");return{platformId:String(i.id),name:Ng([i.first_name,i.last_name].filter(Boolean).join(" "))??"Telegram\u673A\u5668\u4EBA",username:Ng(i.username)}}var mk=Object.freeze({key:"telegram",label:"Telegram",connectionLabel:" Bot API \u957F\u8F6E\u8BE2"}),$y=class extends pn{constructor(r){super({descriptor:mk,...r})}};var lC=class extends hl{#e;constructor(r){super({...r,descriptor:mk,inspectToken:r.inspectToken??az,deriveIdentity:$8,maskPlatformId:ez}),this.#e=r.configStore}status(){let r=super.status();return{...r,bots:r.bots.map(o=>{let i=this.#e.get(o.botId),e=_g(i??{});return{...o,accessPolicy:e}})}}async setAccessPolicy(r,o){let i=_g(o);return this.updateBotConfig(r,e=>({...e,...i}))}};var B_e=Object.freeze([{command:"new",description:"\u5F00\u542F\u4E00\u4E2A\u5168\u65B0\u4F1A\u8BDD"},{command:"compact",description:"\u538B\u7F29\u5F53\u524D\u4F1A\u8BDD\u7684\u8F83\u65E9\u4E0A\u4E0B\u6587"},{command:"workspace",description:"\u5207\u6362\u5DE5\u4F5C\u533A"},{command:"workspacelist",description:"\u5217\u51FA\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84"},{command:"sessionlist",description:"\u5217\u51FA\u4F1A\u8BDD ID \u548C\u6807\u9898"},{command:"session",description:"\u5C06\u5F53\u524D\u804A\u5929\u7ED1\u5B9A\u5230\u6307\u5B9A\u4F1A\u8BDD"},{command:"models",description:"\u6309\u5E8F\u53F7\u5217\u51FA\u6240\u6709\u53EF\u7528\u6A21\u578B"},{command:"model",description:"\u67E5\u770B\u6216\u5207\u6362\u5F53\u524D\u4F1A\u8BDD\u6A21\u578B"},{command:"presetlist",description:"\u5217\u51FA\u53EF\u7528 Agent Preset"},{command:"preset",description:"\u67E5\u770B\u6216\u8BBE\u7F6E\u65B0\u4F1A\u8BDD Agent Preset"},{command:"stop",description:"\u505C\u6B62\u5F53\u524D\u4EFB\u52A1"},{command:"steer",description:"\u7EA0\u504F\u5F53\u524D\u4EFB\u52A1"},{command:"status",description:"\u68C0\u67E5\u8FDE\u63A5\u72B6\u6001"},{command:"help",description:"\u663E\u793A\u5E2E\u52A9"}]);function f_e(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Q_e(n,r){return r?[[n?.text,n?.entities],[n?.caption,n?.caption_entities]].some(([o,i])=>typeof o=="string"&&Array.isArray(i)&&i.some(e=>e?.type!=="mention"||!Number.isInteger(e.offset)||!Number.isInteger(e.length)?!1:o.slice(e.offset,e.offset+e.length).toLowerCase()===`@${r.toLowerCase()}`)):!1}function y_e(n,r){return!r||typeof n!="string"?n:n.replace(new RegExp(`@${f_e(r)}\\b`,"ig"),"").trim()}var m_e=new Set(["image/jpeg","image/png","image/webp","image/gif"]),S_e=new Map([[".jpg","image/jpeg"],[".jpeg","image/jpeg"],[".png","image/png"],[".webp","image/webp"],[".gif","image/gif"]]);function D_e(n){let r=n?.mime_type??n?.mimetype,o=typeof r=="string"?r.toLowerCase():"";if(m_e.has(o))return o;let i=typeof n?.file_name=="string"?n.file_name.toLowerCase():"";for(let[e,a]of S_e)if(i.endsWith(e))return a;return null}function oz(n){return Number.isSafeInteger(n)&&n>=0?n:void 0}function iz(n){return oz(n?.file_size)??(Number(n?.width)||0)*(Number(n?.height)||0)}function w_e(n,r){let o,i,e;if(Array.isArray(n?.photo)&&n.photo.length>0)o=n.photo.reduce((a,A)=>iz(A)>iz(a)?A:a),i="image/jpeg",e=`${o.file_unique_id??o.file_id??"telegram-photo"}.jpg`;else if(n?.document){let a=D_e(n.document);if(!a)return null;o=n.document,i=a,e=typeof o.file_name=="string"?o.file_name:void 0}return!o||typeof o.file_id!="string"?null:{name:e,mediaType:i,size:oz(o.file_size),load:a=>r(o.file_id,a)}}function O_e(n,{botId:r,username:o,loadFile:i=async()=>{throw new Error("Telegram file downloader is unavailable")}}){let e=n?.message,a=e?.chat?.id,A=e?.from?.id,t=e?.message_id;if(!Number.isSafeInteger(n?.update_id)||a===void 0||A===void 0||!Number.isSafeInteger(t)||!["private","group","supergroup"].includes(e.chat?.type))return null;let s=e.chat.type==="private",g=s||String(e.reply_to_message?.from?.id??"")===String(r)||Q_e(e,o),c=Number.isSafeInteger(e.message_thread_id)?e.message_thread_id:void 0,I=w_e(e,i);return{messageId:String(n.update_id),senderId:String(A),senderIsBot:e.from?.is_bot===!0,kind:s?"direct":"group",conversationId:c===void 0?String(a):`${a}:${c}`,content:y_e(e.text??e.caption??"",o),images:I?[I]:[],addressed:g,replyTarget:{chatId:a,replyToMessageId:t,messageThreadId:c},connectionTestTarget:{chatId:a,messageThreadId:c}}}function b_e(n,{accessMode:r=cC.compatible,allowedPrivateUserIds:o=new Set}={}){return r!==cC.privateAllowlist?!0:n?.kind==="direct"&&o instanceof Set&&o.has(String(n.senderId))}var Sk=class{#e;#A;constructor({api:r,signal:o}){this.#e=r,this.#A=o}async sendText(r,o){let i=Xi(o,4e3),e=[];for(let[a,A]of i.entries()){let t=await this.#e.sendMessage({chatId:r.chatId,text:A,replyToMessageId:a===0?r.replyToMessageId:void 0,messageThreadId:r.messageThreadId,signal:this.#A});Number.isSafeInteger(t?.message_id)&&e.push(String(t.message_id))}return{providerMessageIds:e}}sendTyping(r){return this.#e.sendChatAction({chatId:r.chatId,messageThreadId:r.messageThreadId,signal:this.#A})}sendFile(r,o){return this.#e.sendDocument({chatId:r.chatId,file:o,replyToMessageId:r.replyToMessageId,messageThreadId:r.messageThreadId,signal:this.#A})}async openStream(r){return YB({limit:4e3,create:async i=>(await this.#e.sendMessage({chatId:r.chatId,text:i,replyToMessageId:r.replyToMessageId,messageThreadId:r.messageThreadId,signal:this.#A})).message_id,edit:(i,e)=>this.#e.editMessageText({chatId:r.chatId,messageId:i,text:e,signal:this.#A}),sendRemainder:i=>this.#e.sendMessage({chatId:r.chatId,text:i,messageThreadId:r.messageThreadId,signal:this.#A}),messageIdForResult:i=>i?.message_id}).start()}};function M_e(){return{startedAt:null,ready:!1,connectionState:"idle",harnessReachable:!1,lastCheckedAt:null,lastConnectedAt:null,lastError:null,...fi()}}var pC=class{#e;#A;#t;#r;#o;#i;#I;#s;#l;#a=M_e();#n=null;#g=null;#c=null;#p=null;#h=null;constructor({config:r,token:o,harness:i,state:e,logger:a=console,replyTimeoutMs:A=6e5,createApi:t=s=>new IC(s)}){if(!r||!o||!i||!e)throw new TypeError("TelegramRuntime requires config, token, Harness, and state");this.#e=r,this.#A=o,this.#t=i,this.#r=e,this.#o=a,this.#i=A,this.#I=t;let s=_g(r);this.#s=s.accessMode,this.#l=new Set(s.allowedUsers)}get status(){return structuredClone(this.#a)}async sendConnectionTest(r){if(!this.#a.ready||!this.#g){let o=new Error("Telegram bot is not connected");throw o.code="test-target-unavailable",o}return this.#g.sendConnectionTest(r)}async start(){return this.#a.ready&&this.#p?this.status:this.#h?this.#h:(this.#h=this.#d().finally(()=>{this.#h=null}),this.#h)}async#d(){await this.stop(),this.#a.startedAt=new Date().toISOString(),this.#a.connectionState="connecting",this.#a.lastError=null,await this.#t.ensureRunning(),this.#a.harnessReachable=!0;let r=new AbortController;this.#c=r;let o=this.#I({token:this.#A});this.#n=o;try{let i=await o.getMe({signal:r.signal});if(String(i?.id??"")!==this.#e.platformId||i?.is_bot!==!0)throw new Error("Telegram token identity does not match the saved bot");let e=await o.getWebhookInfo({signal:r.signal});if(typeof e?.url=="string"&&e.url){let s=new Error("\u8BE5 Telegram \u673A\u5668\u4EBA\u5DF2\u914D\u7F6E Webhook\uFF0C\u8BF7\u5148\u5728\u539F\u670D\u52A1\u4E2D\u79FB\u9664 Webhook \u540E\u91CD\u8BD5\u3002");throw s.code="webhook-configured",s}try{await o.setMyCommands({commands:B_e,signal:r.signal}),await o.setChatMenuButton({menuButton:yk,signal:r.signal})}catch(s){this.#o.warn?.(`[dsh-im:telegram] bot ${this.#e.botId} command menu setup failed:`,s)}let a=new Sk({api:o,signal:r.signal});this.#g=new $y({bot:a,harness:this.#t,state:this.#r,status:this.#a,logger:this.#o,replyTimeoutMs:this.#i,signal:r.signal});let A=this.#r.cursor();if(A===null){let s=await o.getUpdates({offset:-1,timeout:0,signal:r.signal});A=s.length>0?s.at(-1).update_id+1:0,await this.#r.setCursor(A)}let t=Date.now();return this.#a.ready=!0,this.#a.connectionState="connected",this.#a.lastCheckedAt=t,this.#a.lastConnectedAt=t,this.#p=this.#E(A,r.signal),this.#p.catch(s=>{r.signal.aborted||(this.#a.ready=!1,this.#a.connectionState="failed",this.#a.lastError=s?.message??String(s),this.#o.error?.(`[dsh-im:telegram] bot ${this.#e.botId} polling stopped:`,s))}),this.status}catch(i){throw this.#a.ready=!1,this.#a.connectionState="failed",this.#a.lastError=i?.message??String(i),await this.stop(),i}}async#E(r,o){let i=r;for(;!o.aborted;){let e=await this.#n.getUpdates({offset:i,timeout:25,signal:o});this.#a.lastCheckedAt=Date.now();for(let a of e){if(o.aborted)return;let A=O_e(a,{botId:this.#e.platformId,username:this.#e.username,loadFile:(t,s)=>this.#n.downloadFile({fileId:t,...s})});A&&b_e(A,{accessMode:this.#s,allowedPrivateUserIds:this.#l})?this.#g.accept(A).catch(t=>{o.aborted||this.#o.error?.(`[dsh-im:telegram] bot ${this.#e.botId} message handling failed:`,t)}):A&&(this.#a.messagesRejected+=1,this.#a.lastRejectedAt=new Date().toISOString()),i=a.update_id+1,await this.#r.setCursor(i)}}}async stop(){let r=this.#p,o=this.#g;return this.#c?.abort(),this.#c=null,this.#p=null,this.#n=null,this.#g=null,await Promise.race([r?.catch(()=>{})??Promise.resolve(),new Promise(i=>setTimeout(i,2e3))]),await Promise.race([o?.waitForIdle()??Promise.resolve(),new Promise(i=>setTimeout(i,2e3))]),this.#a.ready=!1,this.#a.connectionState="idle",this.status}};var em=class extends En{};function Dk(n,r={},o={}){return JB(n,r,o,{channel:"telegram",ConfigStore:Zy,StateStore:em,HarnessClient:Xy,Controller:lC,Runtime:pC})}var nz="/telegram",wk=Object.freeze({...Da,setAccessPolicy:"bot.access-policy.set"}),__e=Object.freeze(Object.values(wk));function sz(n){if(typeof n?.setAccessPolicy!="function")throw new TypeError("A complete Telegram controller is required (setAccessPolicy)");let r=zB(n,{channel:"Telegram"});return async(o,i,e)=>{if(o!==wk.setAccessPolicy)return r(o,i,e);if(e?.aborted)return{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}};let a=i&&typeof i=="object"&&!Array.isArray(i)?Object.keys(i):[];if(a.length!==3||!a.every(t=>["botId","accessMode","allowedUsers"].includes(t))||typeof i.botId!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(i.botId))return{ok:!1,error:{code:"bad-request",message:"bot.access-policy.set requires a valid policy."}};let A;try{A=_g(i)}catch{return{ok:!1,error:{code:"bad-request",message:"\u8BF7\u8F93\u5165\u6709\u6548\u7684 Telegram \u8BBF\u95EE\u6A21\u5F0F\u548C\u6570\u5B57 User ID\u3002"}}}try{let t=await n.setAccessPolicy(i.botId,A);return e?.aborted?{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}:{ok:!0,value:t}}catch{return e?.aborted?{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}:{ok:!1,error:{code:"telegram-operation-failed",message:"Telegram \u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"}}}}}function Am(n,r,o){if(!n?.connection?.rpc||typeof n.connection.rpc.handle!="function")throw new TypeError("DSH Host Connection RPC is required");return n.connection.rpc.handle(nz,sz(r),{authority:hr(o)})}async function gz(n,r={}){if(r?.controller)return Am(n,r.controller,r.rpcAuthority);let o=await Dk(n,r,r.internals??{}),i=Am(n,o.controller,r.rpcAuthority);return n.effect(()=>async()=>o.close(),"dsh-im: close Telegram bot connections"),i}import{unlink as dNe}from"node:fs/promises";import{homedir as hNe}from"node:os";import{join as CC,resolve as EI}from"node:path";import{createHash as N_e}from"node:crypto";import{mkdir as T_e,readFile as R_e,rename as k_e,unlink as v_e,writeFile as P_e}from"node:fs/promises";import{dirname as G_e}from"node:path";var Ok=Object.freeze({version:1,bots:Object.freeze([])});function cI(n){return typeof n=="string"&&n.trim()?n.trim():null}function cz(n){let r=cI(n);return r&&/^wecom_[a-f0-9]{24}$/.test(r)?r:null}function F_e(n){let r=cI(n);return r&&/^DSH_WECOM_BOT_SECRET_[A-F0-9]{24}$/.test(r)?r:null}function rm(n){let r=cI(n);if(!r)throw new TypeError("Enterprise WeChat bot ID is required");let o=N_e("sha256").update(r).digest("hex").slice(0,24);return{botId:`wecom_${o}`,secretRef:`DSH_WECOM_BOT_SECRET_${o.toUpperCase()}`}}function bk(n){let r=cI(n)??"";return r?r.length<=10?`${r.slice(0,3)}\u2022\u2022\u2022`:`${r.slice(0,6)}\u2022\u2022\u2022\u2022${r.slice(-4)}`:"\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA"}function Iz(n){if(!n||typeof n!="object")return null;let r=cz(n.botId),o=cI(n.remoteBotId),i=F_e(n.secretRef);if(!r||!o||!i)return null;let e=rm(o);return e.botId!==r||e.secretRef!==i?null:Object.freeze({botId:r,remoteBotId:o,secretRef:i,createdAt:cI(n.createdAt)??new Date().toISOString(),connectedAt:cI(n.connectedAt)})}function L_e(n){if(!n||n.version!==1||!Array.isArray(n.bots))return null;let r=n.bots.map(Iz);if(r.some(a=>a===null))return null;let o=new Set,i=new Set,e=new Set;for(let a of r){if(o.has(a.botId)||i.has(a.remoteBotId)||e.has(a.secretRef))return null;o.add(a.botId),i.add(a.remoteBotId),e.add(a.secretRef)}return Object.freeze({version:1,bots:Object.freeze(r)})}var tm=class{#e;#A=Ok;#t=Promise.resolve();constructor(r){this.#e=r}async load(){try{let r=L_e(JSON.parse(await R_e(this.#e,"utf8")));if(!r)throw new Error("dsh-im Enterprise WeChat config contains invalid bot data");this.#A=r}catch(r){if(r?.code!=="ENOENT")throw r;this.#A=Ok}return this}list(){return structuredClone(this.#A.bots)}get(r){let o=this.#A.bots.find(i=>i.botId===r);return o?structuredClone(o):null}getByRemoteBotId(r){let o=this.#A.bots.find(i=>i.remoteBotId===r);return o?structuredClone(o):null}async save(r){let o=Iz(r);if(!o)throw new Error("Refusing to persist incomplete Enterprise WeChat bot data");return this.#r(i=>{let e=i.find(t=>t.remoteBotId===o.remoteBotId&&t.botId!==o.botId),a=i.find(t=>t.secretRef===o.secretRef&&t.botId!==o.botId);if(e||a)throw new Error("Duplicate Enterprise WeChat bot identity");let A=i.findIndex(t=>t.botId===o.botId);return A===-1?i.push(o):i[A]=o,structuredClone(o)})}async remove(r){if(!cz(r))throw new TypeError("Invalid Enterprise WeChat bot ID");return this.#r(o=>{let i=o.findIndex(a=>a.botId===r);if(i===-1)return null;let[e]=o.splice(i,1);return structuredClone(e)})}async clear(){let r=this.#t.then(async()=>{try{await v_e(this.#e)}catch(o){if(o?.code!=="ENOENT")throw o}this.#A=Ok});this.#t=r.then(()=>{},()=>{}),await r}async#r(r){let o,i=this.#t.then(async()=>{let e=[...this.#A.bots];o=r(e);let a=Object.freeze({version:1,bots:Object.freeze(e)});await this.#o(a),this.#A=a});return this.#t=i.then(()=>{},()=>{}),await i,o}async#o(r){await T_e(G_e(this.#e),{recursive:!0,mode:448});let o=`${this.#e}.tmp`;await P_e(o,`${JSON.stringify(r,null,2)} `,{encoding:"utf8",mode:384}),await k_e(o,this.#e)}};var am=class extends ir{constructor(r){super({...r,rpcIdPrefix:"wecom",logPrefix:"dsh-wecom"})}};var U_e="https://work.weixin.qq.com/ai/qc/generate",H_e="https://work.weixin.qq.com/ai/qc/query_result";function lz(){return process.platform==="win32"?2:process.platform==="linux"?3:1}function Qp(n){return typeof n=="string"&&n.trim()?n.trim():null}function K_e(n){let r=Qp(n);if(!r)return null;try{let o=new URL(r);return o.protocol==="https:"&&o.hostname==="work.weixin.qq.com"&&(!o.port||o.port==="443")?o.href:null}catch{return null}}function Y_e(n,r){return n?AbortSignal.any([n,AbortSignal.timeout(r)]):AbortSignal.timeout(r)}async function pz(n,r,o){let i=await n(r,{method:"GET",redirect:"error",signal:Y_e(o,1e4),headers:{accept:"application/json"}});if(!i.ok)throw new Error(`Enterprise WeChat QR service returned HTTP ${i.status}`);return i.json()}var im=class{#e;#A;#t;#r;constructor({fetch:r=globalThis.fetch,clock:o=()=>Date.now(),source:i="deepseek-harness",platform:e=lz()}={}){if(typeof r!="function")throw new TypeError("A fetch implementation is required");this.#e=r,this.#A=o,this.#t=i,this.#r=[1,2,3].includes(e)?e:lz()}async start({signal:r}={}){let o=new URL(U_e);o.searchParams.set("source",this.#t),o.searchParams.set("plat",String(this.#r));let i=await pz(this.#e,o,r),e=Qp(i?.data?.scode),a=K_e(i?.data?.auth_url);if(!e||!a)throw new Error("Enterprise WeChat QR service returned invalid data");return{scode:e,verificationUrl:a,expiresAt:this.#A()+3e5,pollIntervalMs:3e3}}async poll({scode:r,signal:o}={}){let i=Qp(r);if(!i)throw new TypeError("Enterprise WeChat QR poll code is required");let e=new URL(H_e);e.searchParams.set("scode",i);let a=await pz(this.#e,e,o),A=Qp(a?.data?.status)?.toLowerCase();if(A==="success"){let t=Qp(a?.data?.bot_info?.botid),s=Qp(a?.data?.bot_info?.secret);if(!t||!s)throw new Error("Enterprise WeChat QR result omitted bot credentials");return{status:"success",remoteBotId:t,secret:s}}return["expired","timeout"].includes(A)?{status:"expired"}:["fail","failed","error"].includes(A)?{status:"failed"}:{status:"waiting"}}};import{mkdir as x_e,readFile as j_e,rename as J_e,unlink as q_e,writeFile as z_e}from"node:fs/promises";import{dirname as V_e}from"node:path";var om=Object.freeze({version:1,sessions:{},seenMessageIds:[]});function W_e(n){if(!n||typeof n!="object")return structuredClone(om);let r={};if(n.sessions&&typeof n.sessions=="object"&&!Array.isArray(n.sessions))for(let[o,i]of Object.entries(n.sessions))typeof o=="string"&&typeof i=="string"&&i&&(r[o]=i);return{version:1,sessions:r,seenMessageIds:Array.isArray(n.seenMessageIds)?n.seenMessageIds.filter(o=>typeof o=="string").slice(-1e3):[]}}var nm=class{#e;#A=structuredClone(om);#t=Promise.resolve();constructor(r){this.#e=r}async load(){try{this.#A=W_e(JSON.parse(await j_e(this.#e,"utf8")))}catch(r){if(r?.code!=="ENOENT")throw r;this.#A=structuredClone(om),await this.#r()}return this}sessionFor(r){return this.#A.sessions[r]??null}async setSession(r,o){this.#A.sessions[r]=o,await this.#r()}async clearSession(r){delete this.#A.sessions[r],await this.#r()}async clearSessions(){this.#A.sessions={},await this.#r()}hasSeen(r){return this.#A.seenMessageIds.includes(r)}async markSeen(r){this.hasSeen(r)||(this.#A.seenMessageIds.push(r),this.#A.seenMessageIds.length>1e3&&this.#A.seenMessageIds.splice(0,this.#A.seenMessageIds.length-1e3),await this.#r())}async remove(){try{await q_e(this.#e)}catch(r){if(r?.code!=="ENOENT")throw r}this.#A=structuredClone(om)}async#r(){let r=`${JSON.stringify(this.#A,null,2)} diff --git a/src/channels/qq/qq-bridge.mjs b/src/channels/qq/qq-bridge.mjs index 9ac40fd..7660829 100644 --- a/src/channels/qq/qq-bridge.mjs +++ b/src/channels/qq/qq-bridge.mjs @@ -619,11 +619,13 @@ export class QqHarnessBridge { signal: this.#signal, control: { owner: this, key }, onUpdate: stream ? async (update) => { + // status 帧(如工具结束后的“正在整理结果…”)不推送: + // 保持上一帧(工具名或已生成文本),避免中间过程反复闪提示。 const progress = update.type === 'text' ? update.text : update.type === 'tool' ? `正在使用${update.name}…` - : update.text; + : null; if (progress) await stream.update(progress); } : undefined, onInteraction: (interaction) => this.#handleInteraction(interaction, { diff --git a/test/channels/qq/bridge.test.mjs b/test/channels/qq/bridge.test.mjs index 786a636..ad66d4e 100644 --- a/test/channels/qq/bridge.test.mjs +++ b/test/channels/qq/bridge.test.mjs @@ -458,6 +458,42 @@ test('QQ private messages stream Harness snapshots and finalize once', async () assert.equal(bridge.status.messagesReplied, 1); }); +test('QQ progress stream keeps the last frame when a tool finishes', async () => { + const frames = []; + const bridge = new QqHarnessBridge({ + bot: { + sendText: async () => {}, + openStream: () => ({ + update: async (text) => frames.push(text), + complete: async () => frames.push('DONE'), + cancel() {}, + }), + }, + ownerUserOpenid: 'owner-openid', + harness: { + sessionExists: async () => true, + ask: async (_session, _text, { onUpdate }) => { + await onUpdate({ type: 'tool', name: 'bash' }); + // 工具结束后 Harness 客户端会下发 status 帧“正在整理结果…”。 + await onUpdate({ type: 'status', text: '正在整理结果…' }); + await onUpdate({ type: 'tool', name: 'read' }); + await onUpdate({ type: 'status', text: '正在整理结果…' }); + return '最终回答'; + }, + }, + state: { + hasSeen: () => false, + markSeen: async () => {}, + sessionFor: () => 'session-status', + setSession: async () => {}, + clearSession: async () => {}, + }, + }); + + await bridge.accept(message({ messageId: 'msg-status-frame' })); + assert.deepEqual(frames, ['正在使用bash…', '正在使用read…', '最终回答', 'DONE']); +}); + test('QQ delivers final group answers as markdown messages', async () => { const sentText = []; const markdownCalls = [];