diff --git a/lib/index.js b/lib/index.js index 609804c..4488cda 100644 --- a/lib/index.js +++ b/lib/index.js @@ -127,7 +127,7 @@ ${o}`:o}function Xi(n){return n instanceof Zi?n.userMessage:null}import{createHa `,o=this.#r.then(async()=>{await zce($ce(this.#e),{recursive:!0,mode:448});let i=`${this.#e}.tmp`;await Xce(i,r,{encoding:"utf8",mode:384}),await Wce(i,this.#e)});this.#r=o.then(()=>{},()=>{}),await o}};import{mkdir as tIe,readFile as rIe,realpath as aIe,rename as iIe,stat as oIe,unlink as nIe,writeFile as sIe}from"node:fs/promises";import{dirname as gIe,isAbsolute as kH,resolve as Dd}from"node:path";var nze=Object.freeze({version:1,workspaces:Object.freeze({})});function Nc(n){let r=new Error(n);return r.code=Wa,r}async function wb(n){return Dd(await aIe(n))}async function cIe(n,r){if(n===r)return!0;try{return await wb(n)===await wb(r)}catch{return!1}}function $a(n){if(typeof n!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(n))throw new TypeError("Invalid bot id");return n}function IIe(n){if(!n||n.version!==1||!n.workspaces||typeof n.workspaces!="object"||Array.isArray(n.workspaces))return null;let r={};for(let[i,e]of Object.entries(n.workspaces)){if(!/^[A-Za-z0-9_-]{1,128}$/.test(i)||typeof e!="string"||!kH(e))return null;r[i]=Dd(e)}let o={};if(n.agentPresets!==void 0){if(!n.agentPresets||typeof n.agentPresets!="object"||Array.isArray(n.agentPresets))return null;for(let[i,e]of Object.entries(n.agentPresets)){if(!/^[A-Za-z0-9_-]{1,128}$/.test(i))return null;try{let a=pl(e);if(!a)return null;o[i]=a}catch{return null}}}return{version:1,workspaces:r,agentPresets:o}}async function TH(n){if(typeof n!="string"||!n.trim()||!kH(n.trim())){let i=new Error("\u5DE5\u4F5C\u533A\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\u3002");throw i.code="workspace-not-absolute",i}let r=Dd(n.trim()),o;try{o=await oIe(r)}catch(i){let e=new Error("\u5DE5\u4F5C\u533A\u8DEF\u5F84\u4E0D\u5B58\u5728\u3002",{cause:i});throw e.code="workspace-not-found",e}if(!o.isDirectory()){let i=new Error("\u5DE5\u4F5C\u533A\u8DEF\u5F84\u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u76EE\u5F55\u3002");throw i.code="workspace-not-directory",i}return r}var Kr=class{#e;#A;#r={};#t={};#o=new Map;#i=1;#I=new Map;#s=1;#l=new Map;#a=new WeakMap;#n=new Set;#g=Promise.resolve();#c=new Map;constructor(r,{defaultWorkspace:o=process.cwd()}={}){if(typeof r!="string"||!r)throw new TypeError("workspace store path is required");this.#e=r,this.#A=Dd(o)}async load(){try{let r=IIe(JSON.parse(await rIe(this.#e,"utf8")));if(!r)throw new Error("dsh-im workspace config is invalid");this.#r=r.workspaces,this.#t=r.agentPresets}catch(r){if(r?.code!=="ENOENT")throw r;this.#r={},this.#t={}}this.#o.clear(),this.#i=1,this.#I.clear(),this.#s=1,this.#l.clear(),this.#n.clear();for(let r of Object.keys(this.#r))this.#o.set(r,this.#p()),this.#I.set(r,this.#h());return this}has(r){let o=$a(r);return Object.hasOwn(this.#r,o)&&!this.#l.has(o)}incarnationFor(r){return this.#I.get($a(r))??null}workspaceFor(r){return this.#r[$a(r)]??this.#A}agentPresetFor(r){return this.#t[$a(r)]??null}generationFor(r){return this.#o.get($a(r))??null}async whenIdle(){await this.#g}async whenBotIdle(r){let o=$a(r);for(;;){let i=this.#c.get(o);if(!i||(await i,this.#c.get(o)===i))return}}async ensure(r,{workspace:o=this.#A,defaultAgentPreset:i}={}){let e=$a(r),a=Dd(o);return this.#C(e,async()=>{if(this.#r[e])this.#o.has(e)||this.#o.set(e,this.#p());else{let A=pl(i),t=Object.hasOwn(this.#t,e),s=this.#t[e];this.#r[e]=a,A&&(this.#t[e]=A),this.#o.set(e,this.#p()),this.#I.set(e,this.#h());try{await this.#u()}catch(g){throw delete this.#r[e],t?this.#t[e]=s:delete this.#t[e],this.#o.delete(e),this.#I.delete(e),g}}return this.#r[e]})}async setWorkspace(r,o,{clearSessions:i,incarnation:e}={}){let a=$a(r);if(!this.has(a)||e!==void 0&&e!==this.incarnationFor(a)){let t=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw t.code="workspace-bot-not-found",t}let A=await TH(o);return this.#C(a,async()=>{if(!this.has(a)||e!==void 0&&e!==this.incarnationFor(a)){let s=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw s.code="workspace-bot-not-found",s}if(A===this.workspaceFor(a))return A;let t=this.#r[a];this.#o.set(a,this.#p()),await i?.(),this.#r[a]=A;try{await this.#u()}catch(s){throw this.#r[a]=t,s}return A})}async setAgentPreset(r,o,{incarnation:i}={}){let e=$a(r);if(!this.has(e)||i!==void 0&&i!==this.incarnationFor(e)){let A=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw A.code="workspace-bot-not-found",A}let a=pl(o);return this.#C(e,async()=>{if(!this.has(e)||i!==void 0&&i!==this.incarnationFor(e)){let t=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw t.code="workspace-bot-not-found",t}let A=this.#t[e]??null;if(A===a)return a;a?this.#t[e]=a:delete this.#t[e];try{await this.#u()}catch(t){throw A?this.#t[e]=A:delete this.#t[e],t}return a})}async bindWorkspaceSession(r,o,{conversationKey:i,sessionId:e,clearSessions:a,setSession:A,incarnation:t,expectedGeneration:s}={}){let g=$a(r);if(typeof i!="string"||!i||typeof e!="string"||!e)throw new TypeError("conversationKey and sessionId are required");if(typeof a!="function"||typeof A!="function")throw new TypeError("session state callbacks are required");if(!this.has(g)||t!==void 0&&t!==this.incarnationFor(g)){let I=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw I.code="workspace-bot-not-found",I}let c=await wb(await TH(o));return this.#C(g,async()=>{if(!this.has(g)||t!==void 0&&t!==this.incarnationFor(g)){let I=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw I.code="workspace-bot-not-found",I}if(s!==void 0&&s!==this.generationFor(g))throw Nc("The bot workspace changed before the session binding could be committed.");if(!await cIe(c,this.workspaceFor(g))){let I=this.#r[g];this.#o.set(g,this.#p()),await a(),this.#r[g]=c;try{await this.#u()}catch(E){throw this.#r[g]=I,E}}return await A(i,e),{workspace:c,sessionId:e,generation:this.#o.get(g)}})}async invalidateSessions(r,{clearSessions:o}={}){let i=$a(r);return this.#C(i,async()=>{this.#o.set(i,this.#p()),await o?.()})}async beginRemoval(r,{clearSessions:o}={}){let i=$a(r);return this.#C(i,async()=>{let e=this.#l.get(i);if(e)return e;let a=Object.freeze({});this.#l.set(i,a),this.#a.set(a,{botId:i,incarnation:this.incarnationFor(i)}),this.#o.set(i,this.#p());try{await o?.()}catch(A){throw this.#l.get(i)===a&&this.#l.delete(i),A}return a})}async abortRemoval(r){let{botId:o}=this.#d(r);return this.#C(o,async()=>this.#l.get(o)!==r?!1:(this.#l.delete(o),Object.hasOwn(this.#r,o)&&(this.#o.set(o,this.#p()),this.#I.has(o)||this.#I.set(o,this.#h())),!0))}async finishRemoval(r){let{botId:o,incarnation:i}=this.#d(r);return this.#C(o,async()=>this.#l.get(o)!==r?{removed:!1,persisted:!0,error:null,stale:!0}:this.incarnationFor(o)!==i?(this.#l.delete(o),{removed:!1,persisted:!0,error:null,stale:!0}):(this.#l.delete(o),this.#E(o)))}async retireAfterConfigCommit(r){let o=$a(r);return this.#C(o,async()=>(this.#l.delete(o),this.#E(o)))}async remove(r){let o=await this.retireAfterConfigCommit(r);if(o.error)throw o.error;return o.removed}async reconcile(r){let o=new Set([...r].map($a)),i=new Set([...Object.keys(this.#r),...Object.keys(this.#t),...this.#n]);for(let e of i)o.has(e)||await this.remove(e)}decorateStatus(r){return!r||typeof r!="object"||!Array.isArray(r.bots)?r:{...r,bots:r.bots.map(o=>o?.botId?{...o,workspace:this.workspaceFor(o.botId),agentPreset:this.agentPresetFor(o.botId)}:o)}}#p(){let r=this.#i;return this.#i+=1,r}#h(){let r=this.#s;return this.#s+=1,r}#d(r){if(!r||typeof r!="object")throw new TypeError("Invalid workspace removal transaction");let o=this.#a.get(r);if(!o)throw new TypeError("Invalid workspace removal transaction");return o}async#E(r){let o=Object.hasOwn(this.#r,r),i=Object.hasOwn(this.#t,r),e=o||i||this.#n.has(r);if(delete this.#r[r],delete this.#t[r],this.#o.delete(r),this.#I.delete(r),!e)return{removed:!1,persisted:!0,error:null,stale:!1};try{return await this.#B(),{removed:o,persisted:!0,error:null,stale:!1}}catch(a){return this.#n.add(r),{removed:o,persisted:!1,error:a,stale:!1}}}async#C(r,o){let i=this.#g.then(o,o),e=i.then(()=>{},()=>{});return this.#g=e,this.#c.set(r,e),e.finally(()=>{this.#c.get(r)===e&&this.#c.delete(r)}),i}async#u(){let r={version:1,workspaces:this.#r};Object.keys(this.#t).length>0&&(r.agentPresets=this.#t),await tIe(gIe(this.#e),{recursive:!0,mode:448});let o=`${this.#e}.tmp`;await sIe(o,`${JSON.stringify(r,null,2)} `,{encoding:"utf8",mode:384}),await iIe(o,this.#e),this.#n.clear()}async#B(){if(Object.keys(this.#r).length>0||Object.keys(this.#t).length>0){await this.#u();return}try{await nIe(this.#e),this.#n.clear()}catch(r){if(r?.code!=="ENOENT")throw r;this.#n.clear()}}};function Ob(n){if(!n)return null;let r=typeof n=="function"?n():n;return r&&typeof r.then=="function"?r.then(ln):ln(r)}function vH(){let n=new Error("Agent Preset \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u7528\u3002");return n.code="agent-preset-unavailable",n}function ul(n){if(n())return;let r=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw r.code="workspace-bot-not-found",r}function RH(n,r,o){let i=e=>{let a=n.decorateStatus(e);if(!o||!a||typeof a!="object")return a;let A=s=>s?{...a,agentPresetCatalog:s}:a,t=Ob(o);return t&&typeof t.then=="function"?t.then(A):A(t)};return r&&typeof r.then=="function"?r.then(i):i(r)}function lIe(n){return Promise.resolve(n.status())}function Ca(n,{workspaces:r,method:o="remove",botIdFromRemoved:i=e=>e?.botId}){if(!n||!r||typeof n[o]!="function")throw new TypeError("configStore removal observer dependencies are required");return new Proxy(n,{get(e,a){let A=Reflect.get(e,a,e);return a===o?async(...t)=>{let s=await A.apply(e,t),g=s?i(s,t):null;return g&&await r.retireAfterConfigCommit(g),s}:typeof A=="function"?A.bind(e):A}})}function ua(n,{botId:r,workspaces:o,state:i,agentPresetCatalog:e}={}){if(!n||!o||!i)throw new TypeError("harness, workspaces, and state are required");let a=o.incarnationFor(r),A=()=>o.has(r)&&o.incarnationFor(r)===a,t=async(I=e)=>{let E;try{E=await Ob(I)??ln(null)}catch(B){throw ul(A),B}return ul(A),{agentPreset:o.agentPresetFor(r),agentPresetCatalog:E}},s=new Map,g=new Proxy(n,{get(I,E){if(E==="agentPresetSettings")return async(f={})=>{f?.signal?.throwIfAborted(),ul(A);let Q=await t();return f?.signal?.throwIfAborted(),Q};if(E==="updateAgentPreset")return async(f,Q={})=>{Q?.signal?.throwIfAborted(),ul(A);let y=f==="--default"?null:pl(f),m=null;if(y&&({agentPresetCatalog:m}=await t(),Q?.signal?.throwIfAborted(),!m.items.some(_=>_.id===y)))throw vH();if(await o.setAgentPreset(r,y,{incarnation:a}),ul(A),m)return{agentPreset:o.agentPresetFor(r),agentPresetCatalog:m};try{return await t()}catch(_){if(_?.code==="workspace-bot-not-found")throw _;return ul(A),{agentPreset:o.agentPresetFor(r),agentPresetCatalog:ln(null)}}};if(E==="currentWorkspace")return()=>{if(!A()){let f=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw f.code="workspace-bot-not-found",f}return o.workspaceFor(r)};if(E==="assertWorkspaceScope")return()=>{if(!A()){let f=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw f.code="workspace-bot-not-found",f}};if((E==="listWorkspaces"||E==="listWorkspaceSessions"||E==="listModels")&&typeof I[E]=="function")return async(...f)=>{if(!A()){let y=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw y.code="workspace-bot-not-found",y}let Q=await I[E](...f);if(!A()){let y=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw y.code="workspace-bot-not-found",y}return Q};if(E==="switchWorkspace")return f=>{if(!A()){let Q=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");return Q.code="workspace-bot-not-found",Promise.reject(Q)}return o.setWorkspace(r,f,{clearSessions:()=>i.clearSessions(),incarnation:a})};if(E==="bindWorkspaceSession")return async(f,Q)=>{if(typeof f!="string"||!f||typeof Q!="string"||!Q)throw new TypeError("conversationKey and sessionId are required");if(!A()){let N=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw N.code="workspace-bot-not-found",N}if(typeof I.adoptWorkspaceSession!="function")throw new TypeError("Harness does not support adopting workspace sessions");let y=o.generationFor(r),m=await I.adoptWorkspaceSession(Q);if(!A()){let N=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw N.code="workspace-bot-not-found",N}if(y!==o.generationFor(r))throw Nc("The bot workspace changed while the session was being adopted.");if(!m||typeof m!="object"||m.sessionId!==Q||typeof m.workspace!="string")throw new TypeError("Harness returned an invalid adopted workspace session");let _=await o.bindWorkspaceSession(r,m.workspace,{conversationKey:f,sessionId:Q,clearSessions:()=>i.clearSessions(),setSession:(N,G)=>i.setSession(N,G),incarnation:a,expectedGeneration:y});if(!A()){let N=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw N.code="workspace-bot-not-found",N}if(_.generation!==o.generationFor(r))throw Nc("The bot workspace changed before the session binding completed.");return s.set(Q,_.generation),{...m,workspace:_.workspace,sessionId:_.sessionId}};if(E==="createSession")return async(f={})=>{if(await o.whenBotIdle(r),!A()){let _=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw _.code="workspace-bot-not-found",_}let Q=o.generationFor(r),y=o.agentPresetFor(r),m=await I.createSession({...f,workspace:o.workspaceFor(r),...y==null?{}:{agentPreset:y}});return s.set(m,Q),m};if(E==="workspaceSession")return f=>{if(typeof f!="string"||!f)throw new TypeError("sessionId is required");let Q=s.get(f)??o.generationFor(r);s.delete(f);let y=()=>A()&&Q===o.generationFor(r),m=async(N,G,Y)=>{if(!y())throw Nc(`The bot workspace changed before this ${Y} started.`);let j=await I[N](f,...G);if(!y())throw Nc(`The bot workspace changed while this ${Y} was running.`);return j},_=async(N,G,Y)=>{if(!y())throw Nc(`The bot workspace changed before this ${Y} started.`);return I[N](f,...G)};return Object.freeze({sessionId:f,async sessionExists(...N){if(!y())return!1;let G=await I.sessionExists(f,...N);return y()&&G},models(...N){return m("getSessionModels",N,"model listing")},selectModel(...N){return m("selectSessionModel",N,"model selection")},isRunning(...N){return m("isSessionRunning",N,"run-state check")},hasActiveTurn(...N){return m("hasActiveTurn",N,"turn ownership check")},stopActiveTurn(...N){return _("stopActiveTurn",N,"turn stop")},steerActiveTurn(...N){return _("steerActiveTurn",N,"turn steering")},ask(...N){if(!y())throw Nc("The bot workspace changed before this prompt started.");return I.ask(f,...N)}})};if(E==="sessionExists")return(f,...Q)=>{if(!A())return!1;let y=s.get(f);return y!==void 0&&y!==o.generationFor(r)?(s.delete(f),!1):I.sessionExists(f,...Q)};if(E==="ask")return(f,...Q)=>{let y=s.get(f);if(s.delete(f),!A()||y!==void 0&&y!==o.generationFor(r)){let m=new Error("The bot workspace changed before this prompt started.");throw m.code=Wa,m}return I.ask(f,...Q)};if(E==="executeCommand"&&typeof I.executeCommand=="function")return(f,...Q)=>{let y=s.get(f);if(s.delete(f),!A()||y!==void 0&&y!==o.generationFor(r)){let m=new Error("The bot workspace changed before this command started.");throw m.code=Wa,m}return I.executeCommand(f,...Q)};let B=Reflect.get(I,E,I);return typeof B=="function"?B.bind(I):B}}),c=new Proxy(i,{get(I,E){if(E===xO)return I;if(E==="sessionFor")return(f,...Q)=>{if(!A())return null;let y=I.sessionFor(f,...Q);return y&&!s.has(y)&&s.set(y,o.generationFor(r)),y};if(E==="setSession")return(f,Q,...y)=>{let m=s.get(Q);return!A()||m!==void 0&&m!==o.generationFor(r)?(s.delete(Q),!1):I.setSession(f,Q,...y)};let B=Reflect.get(I,E,I);return typeof B=="function"?B.bind(I):B}});return Object.freeze({harness:g,state:c})}function Ba(n,{workspaces:r,stateFor:o,agentPresetCatalog:i}={}){if(!n||!r||typeof o!="function")throw new TypeError("controller, workspaces, and stateFor are required");let e=new Map,a=(c,I)=>{let B=(e.get(c)??Promise.resolve()).catch(()=>{}).then(I);return e.set(c,B),B.finally(()=>{e.get(c)===B&&e.delete(c)})},A=c=>RH(r,c,i),t=(c,I)=>{let E=r.incarnationFor(c);return a(c,async()=>{if(!(await n.status())?.bots?.some(Q=>Q?.botId===c)){let Q=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw Q.code="workspace-bot-not-found",Q}let f=await o(c);return await r.setWorkspace(c,I,{clearSessions:()=>f.clearSessions(),incarnation:E}),A(await n.status())})},s=(c,I)=>{let E=r.incarnationFor(c),B=pl(I);return a(c,async()=>{if(!(await n.status())?.bots?.some(y=>y?.botId===c)){let y=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw y.code="workspace-bot-not-found",y}let Q=B&&i?await Ob(i):null;if(B&&i&&!Q?.items.some(y=>y.id===B))throw vH();return await r.setAgentPreset(c,B,{incarnation:E}),RH(r,await n.status(),Q??i)})},g=(c,I)=>a(c,async()=>{let E=await r.beginRemoval(c,{clearSessions:async()=>{try{let B=await o(c);if(!B||typeof B.clearSessions!="function")throw new TypeError("bot state does not support session cleanup");await B.clearSessions()}catch(B){console.warn(`[dsh-im] ignored session cleanup failure while deleting bot ${c}:`,B?.message??B)}}});try{let B=await I();return await r.finishRemoval(E),A(B)}catch(B){let f=await lIe(n).catch(()=>null);throw Array.isArray(f?.bots)&&!f.bots.some(y=>y?.botId===c)?await r.finishRemoval(E):await r.abortRemoval(E),B}});return new Proxy(n,{get(c,I){if(I==="updateWorkspace")return t;if(I==="updateAgentPreset")return s;let E=Reflect.get(c,I,c);return typeof E!="function"?E:I==="deleteBot"?(B,...f)=>g(B,()=>E.call(c,B,...f)):I==="disconnect"?async(...B)=>{let Q=(await c.status())?.bots?.[0]?.botId;return Q?g(Q,()=>E.apply(c,B)):A(E.apply(c,B))}:(...B)=>A(E.apply(c,B))}})}var PH=Object.freeze([250,1e3,3e3,5e3,1e4,3e4]);function pIe(n){if(!Array.isArray(n)||n.length===0)return[...PH];let r=n.filter(o=>Number.isFinite(o)&&o>=0);return r.length>0?r:[...PH]}var jB=class{#e;#A;#r;#t;#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.#r=i,this.#t=pIe(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(io.invoke({namespace:"commands",method:"execute",args:{agentId:i,line:e},signal:a.signal})}}import{randomUUID as EIe}from"node:crypto";function dIe(n){if(typeof n?.get!="function")return;let r;try{r=n.get("agents")}catch{return}if(r!=null){if(typeof r.get!="function")throw new TypeError('dsh-im requires a callable AgentRegistry when ctx.get("agents") is present');return r}}function hIe(n,r,o){if(n?.status!=="running")return!1;let i=n?.session?.events;if(!Array.isArray(i))return!1;let e=null,a=!1;for(let A of i){if(A?.type==="turn/start"){e=A.data?.turn??null,a=!1;continue}if(A?.type==="turn/end"&&A.data?.turn===e){e=null,a=!1;continue}e===r&&A?.type==="user/message"&&A.data?.source?.rpcId===o&&(a=!0)}return e===r&&a}function GH(n){if(!n||typeof n!="object"||Object.isFrozen(n))return n;for(let r of Object.values(n))GH(r);return Object.freeze(n)}function CIe(n){return GH({id:EIe(),role:"user",content:[{type:"text",text:n}],source:{kind:"user"}})}function uIe(n){let r=new Error("Session is busy with an active turn or maintenance task",{cause:n});return r.code="agent-busy",r}function BIe(n){return({sessionId:r,expectedTurn:o,promptRpcId:i,action:e,text:a})=>{let A=n.get(r);if(A){if(!hIe(A,o,i))return!1;if(e==="stop")return A.cancel({kind:"user"},{keepInbox:!0}),!0;if(e==="steer")return typeof a!="string"||!a.trim()?!1:(A.inject(CIe(a)),!0);throw new TypeError(`Unsupported Harness control action: ${String(e)}`)}}}function fIe(n){return({sessionId:r,operation:o})=>{if(typeof o!="function")throw new TypeError("maintenance operation is required");let i=n.get(r);if(!i)return o();try{return i.runMaintenance(e=>{if(n.get(r)!==i){let a=new Error("Session agent changed before maintenance started");throw a.code="agent-unavailable",a}return o(e)})}catch(e){throw uIe(e)}}}function QIe(n){return({sessionId:r,workspace:o,files:i,signal:e})=>{let A=n.get(r)?.session?.header?.cwd,t=typeof A=="string"&&A?A:o;if(typeof t!="string"||!t)throw new Zi("inbound-file-workspace-unavailable","The Harness Session workspace is unavailable for inbound files.");return rH({files:i},{workspace:t,signal:e})}}function Qa(n,r={}){let{controlExecutor:o,sessionMaintenanceExecutor:i,fileIngressExecutor:e}=r;if(o!==void 0&&typeof o!="function")throw new TypeError("controlExecutor must be a function");if(i!==void 0&&typeof i!="function")throw new TypeError("sessionMaintenanceExecutor must be a function");if(e!==void 0&&typeof e!="function")throw new TypeError("fileIngressExecutor must be a function");let a=o&&i&&e?void 0:dIe(n);return{controlExecutor:o??(a?BIe(a):void 0),sessionMaintenanceExecutor:i??(a?fIe(a):void 0),fileIngressExecutor:e??QIe(a??{get:()=>{}})}}function SIe(n,r){if(r!==void 0)return new URL(r);let o=n?.port;if(!Number.isInteger(o)||o<1||o>65535)throw new Error("dsh-dingtalk requires an initialized DSH webServer port");return new URL(`http://127.0.0.1:${o}`)}function DIe(n){let r=Tc(n.dshHome??process.env.DSH_HOME??wd(mIe(),".dsh")),o=Tc(n.dataDir??wd(r,"integrations","dsh-dingtalk"));return{root:o,config:Tc(n.configPath??wd(o,"config.json")),bots:Tc(n.botsDir??wd(o,"bots")),workspaces:Tc(n.workspacesPath??wd(o,"workspaces.json"))}}async function Mb(n,r={},o={}){if(!n?.credentials)throw new TypeError("dsh-dingtalk requires ctx.credentials");if(!n?.webServer)throw new TypeError("dsh-dingtalk requires ctx.webServer");let i=o.ConfigStore??OB,e=o.DeviceAuth??MB,a=o.StateStore??YB,A=o.HarnessClient??KB,t=o.Controller??dd,s=o.Runtime??yd,g=o.createConnectionSupervisor??bb,c=typeof n.logger=="function"?n.logger("dsh-dingtalk"):n.logger??console,I=()=>ha(n),E=DIe(r),B=await new i(E.config).load(),f=Tc(r.workspace??process.cwd()),Q=o.WorkspaceStore??Kr,y=o.workspaces??await new Q(E.workspaces,{defaultWorkspace:f}).load(),m=typeof B.list=="function",_=m?B.list():[];m&&await y.reconcile(_.map(W=>W.botId)),await Promise.all(_.map(W=>y.ensure(W.botId,{defaultAgentPreset:r.agentPreset})));let N=typeof B.remove=="function"?Ca(B,{workspaces:y}):B,G=o.deviceAuth??new e({baseUrl:r.registrationBaseUrl}),Y=new Map,j=W=>Tc(E.bots,W,"state.json"),K=async W=>{let se=Y.get(W);return se||(se=await new a(j(W)).load(),Y.set(W,se)),se},x=fa(n,o.commandExecutor),{controlExecutor:J,sessionMaintenanceExecutor:F,fileIngressExecutor:k}=Qa(n,{controlExecutor:o.controlExecutor,sessionMaintenanceExecutor:o.sessionMaintenanceExecutor,fileIngressExecutor:o.fileIngressExecutor}),z=new A({baseUrl:SIe(n.webServer,r.harnessBaseUrl),workspace:f,autostart:!1,dshBin:r.dshBin??"dsh",...x?{commandExecutor:x}:{},...J?{controlExecutor:J}:{},...F?{sessionMaintenanceExecutor:F}:{},...k?{fileIngressExecutor:k}:{}}),H=new t({deviceAuth:G,credentials:n.credentials,configStore:N,logger:c,createRuntime:async({botId:W,config:se,clientSecret:Qe})=>{let be=await K(W);await y.ensure(W,{defaultAgentPreset:r.agentPreset});let Be=ua(z,{botId:W,workspaces:y,state:be,agentPresetCatalog:I});return new s({config:se,clientSecret:Qe,harness:Be.harness,state:Be.state,replyTimeoutMs:r.replyTimeoutMs??6e5,maxMessageChars:r.maxMessageChars??4e3,connectTimeoutMs:r.connectTimeoutMs??15e3,logger:{error:(...De)=>c.error?.(`[${W}]`,...De),warn:(...De)=>c.warn?.(`[${W}]`,...De),info:(...De)=>c.info?.(`[${W}]`,...De),debug:(...De)=>c.debug?.(`[${W}]`,...De)}})},deleteState:async({botId:W})=>{let se=Y.get(W);if(Y.delete(W),se&&typeof se.remove=="function")await se.remove();else try{await yIe(j(W))}catch(Qe){if(Qe?.code!=="ENOENT")throw Qe}}}),M=Ba(H,{workspaces:y,stateFor:K,agentPresetCatalog:I}),v=g({controller:M,harness:z,logger:c,retryDelaysMs:r.retryDelaysMs,healthyIntervalMs:r.healthyIntervalMs}).start();return{controller:M,ready:v.ready,async close(){await v.close(),await M.close(),z.stopManagedProcess()}}}import MIe from"qrcode";var wIe=new Set(["loopback","trusted-host"]);function Cr(n){if(n===void 0)return"loopback";if(wIe.has(n))return n;throw new TypeError('dsh-im rpcAuthority must be "loopback" or "trusted-host"')}import{isAbsolute as OIe}from"node:path";var ei="bot.workspace.set";function ya(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)&&Object.keys(n).every(r=>["botId","workspace"].includes(r))&&typeof n.botId=="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(n.botId)&&typeof n.workspace=="string"&&n.workspace.length<=4096&&OIe(n.workspace.trim())}function ma(n){return["workspace-not-absolute","workspace-not-found","workspace-not-directory","workspace-bot-not-found","agent-preset-invalid","agent-preset-unavailable"].includes(n?.code)?{code:n.code,message:n.message}:null}var Ai="bot.preset.set";function bIe(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}function Sa(n){return bIe(n)&&Object.keys(n).every(r=>["botId","agentPreset"].includes(r))&&typeof n.botId=="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(n.botId)&&(n.agentPreset===null||typeof n.agentPreset=="string"&&sb.test(n.agentPreset))}var LH="/dingtalk",Xt=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:ei,setAgentPreset:Ai,approveSender:"bot.sender.approve",revokeSender:"bot.sender.revoke"}),UH=Object.freeze(Object.values(Xt)),_Ie=new Set(["clientSecret","client_secret","deviceCode","device_code","secretRef","staffId","senderStaffId","verificationUrl","verificationUri","userCode"]);function Nb(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}function lg(n,r){return Nb(n)&&Object.keys(n).every(o=>r.includes(o))}function Rc(n){return typeof n=="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(n)}function FH(n,r){return typeof n=="string"&&n.trim().length>0&&n.length<=r}function NIe(n,r){return Nb(r)?n===Xt.status?lg(r,[])?null:"connection.status does not accept fields.":n===Xt.beginProvisioning?lg(r,["locale"])&&(r.locale===void 0||r.locale==="zh-CN")?null:"provision.begin received unsupported fields.":[Xt.pollProvisioning,Xt.cancelProvisioning].includes(n)?lg(r,["attemptId"])&&Rc(r.attemptId)?null:`${n} requires an attemptId.`:n===Xt.bindCredentials?lg(r,["clientId","clientSecret"])&&FH(r.clientId,256)&&FH(r.clientSecret,1024)?null:"bot.bind-credentials requires Client ID and Client Secret.":n===Xt.reconnectBot?lg(r,["botId","sendTest"])&&Rc(r.botId)&&(r.sendTest===void 0||r.sendTest===!0)?null:"bot.reconnect requires a botId and optional sendTest=true.":n===Xt.deleteBot?lg(r,["botId","confirm"])&&Rc(r.botId)&&r.confirm===!0?null:"bot.delete requires a botId and confirm=true.":n===Xt.setWorkspace?ya(r)?null:"\u8BF7\u8F93\u5165\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84\u3002":n===Xt.setAgentPreset?Sa(r)?null:"\u8BF7\u9009\u62E9 Agent Preset\u3002":n===Xt.approveSender?lg(r,["botId","requestId","confirm"])&&Rc(r.botId)&&Rc(r.requestId)&&r.confirm===!0?null:"bot.sender.approve requires botId, requestId, and confirm=true.":n===Xt.revokeSender?lg(r,["botId","senderKey","confirm"])&&Rc(r.botId)&&Rc(r.senderKey)&&r.confirm===!0?null:"bot.sender.revoke requires botId, senderKey, and confirm=true.":"Unknown DingTalk endpoint.":"Payload must be an object."}function JB(n){return{ok:!1,error:{code:"bad-request",message:n}}}function Od(){return{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}}function TIe(){return{ok:!1,error:{code:"dingtalk-operation-failed",message:"\u9489\u9489\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"}}}function Bl(n){if(Array.isArray(n))return n.map(Bl);if(!Nb(n))return n;let r={};for(let[o,i]of Object.entries(n))_Ie.has(o)||(r[o]=Bl(i));return r}async function RIe(n){return MIe.toDataURL(n,{type:"image/png",errorCorrectionLevel:"M",margin:2,width:320})}async function _b(n,r){return!n||typeof n.verificationUrl!="string"?Bl(n):Bl({...n,qrCodeDataUrl:await r(n.verificationUrl)})}async function pg(n,r){let o=structuredClone(n);return o?.provisioning&&(o.provisioning=await _b(o.provisioning,r)),Bl(o)}function kIe(n){for(let r of["status","startProvisioning","registrationStatus","cancelProvisioning","bindCredentials","reconnectBot","deleteBot","approveSender","revokeSender"])if(typeof n?.[r]!="function")throw new TypeError(`A complete DingTalk controller is required (${r})`)}function HH(n,{encodeQr:r=RIe}={}){kIe(n);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 Od();if(!UH.includes(e))return JB("Unknown DingTalk endpoint.");let t=NIe(e,a);if(t)return JB(t);try{let s;if(e===Xt.status)s=await pg(await n.status(),i);else if(e===Xt.beginProvisioning){let g=await n.startProvisioning({signal:A});if(A?.aborted)return await n.cancelProvisioning(g.attemptId),Od();s=await _b(g,i)}else if(e===Xt.pollProvisioning){let g=await n.registrationStatus(a.attemptId);if(!g)return JB("The provisioning attempt no longer exists.");s=await _b(g,i)}else if(e===Xt.cancelProvisioning){if(s=await n.cancelProvisioning(a.attemptId),!s)return JB("The provisioning attempt no longer exists.");s=Bl(s)}else if(e===Xt.bindCredentials)s=await pg(await n.bindCredentials(a),i);else if(e===Xt.reconnectBot){let g=await n.reconnectBot(a.botId);if(A?.aborted)return Od();let c;if(a.sendTest===!0)if(!g?.bots?.some(E=>E?.botId===a.botId&&E?.connected===!0)||typeof n.sendConnectionTest!="function")c=Wt(ir("\u9489\u9489\u673A\u5668\u4EBA"));else try{await n.sendConnectionTest(a.botId),c=Wt()}catch(E){c=Wt(E)}s=await pg({...g,...c?{testMessage:c}:{}},i)}else if(e===Xt.deleteBot)s=await pg(await n.deleteBot(a.botId),i);else if(e===Xt.setWorkspace){if(typeof n.updateWorkspace!="function")throw new Error("Workspace update is unavailable");s=await pg(await n.updateWorkspace(a.botId,a.workspace),i)}else if(e===Xt.setAgentPreset){if(typeof n.updateAgentPreset!="function")throw new Error("Agent preset update is unavailable");s=await pg(await n.updateAgentPreset(a.botId,a.agentPreset),i)}else e===Xt.approveSender?s=await pg(await n.approveSender(a.botId,a.requestId),i):s=await pg(await n.revokeSender(a.botId,a.senderKey),i);return A?.aborted?Od():{ok:!0,value:s}}catch(s){let g=ma(s);return A?.aborted?Od():g?{ok:!1,error:g}:TIe()}}}function qB(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(LH,HH(r,o),{authority:Cr(i)})}async function KH(n,r={}){if(r?.controller)return qB(n,r.controller,r.rpcOptions,r.rpcAuthority);let o=await Mb(n,r,r.internals),i=qB(n,o.controller,r.rpcOptions,r.rpcAuthority);return n.effect(()=>async()=>{await o.close()},"dsh-dingtalk: close bot connections"),i}import{createHash as vIe}from"node:crypto";import{mkdir as PIe,readFile as GIe,rename as FIe,unlink as LIe,writeFile as UIe}from"node:fs/promises";import{dirname as HIe}from"node:path";var Tb=Object.freeze({version:1,bots:Object.freeze([])});function ds(n){return typeof n=="string"&&n.trim()?n.trim():null}function xH(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function bd(n,{botPrefix:r,tokenRefPrefix:o}){let i=ds(n);if(!i)throw new TypeError("platformId is required");let e=vIe("sha256").update(i).digest("hex").slice(0,24);return{botId:`${r}_${e}`,tokenRef:`${o}_${e.toUpperCase()}`}}function zB(n,r){let o=ds(n)??"";return o.length<=10?o?`${o.slice(0,3)}\u2022\u2022\u2022`:r:`${o.slice(0,6)}\u2022\u2022\u2022\u2022${o.slice(-4)}`}var fl=class{#e;#A;#r;#t;#o;#i;#I;#s=Tb;#l=Promise.resolve();constructor(r,{channel:o,botPrefix:i,tokenRefPrefix:e,normalizeBotExtension:a=()=>({})}){if(typeof a!="function")throw new TypeError("normalizeBotExtension must be a function");this.#e=r,this.#A=o,this.#r=i,this.#t=e,this.#o=a,this.#i=new RegExp(`^${xH(i)}_[a-f0-9]{24}$`),this.#I=new RegExp(`^${xH(e)}_[A-F0-9]{24}$`)}async load(){try{let r=this.#n(JSON.parse(await GIe(this.#e,"utf8")));if(!r)throw new Error(`dsh-im ${this.#A} config contains invalid bot data`);this.#s=r}catch(r){if(r?.code!=="ENOENT")throw r;this.#s=Tb}return this}list(){return structuredClone(this.#s.bots)}get(r){let o=this.#s.bots.find(i=>i.botId===r);return o?structuredClone(o):null}getByPlatformId(r){let o=this.#s.bots.find(i=>i.platformId===r);return o?structuredClone(o):null}async save(r){let o=this.#a(r);if(!o)throw new Error(`Refusing to persist incomplete ${this.#A} bot data`);return this.#g(i=>{let e=i.find(t=>t.platformId===o.platformId&&t.botId!==o.botId),a=i.find(t=>t.tokenRef===o.tokenRef&&t.botId!==o.botId);if(e||a)throw new Error(`Duplicate ${this.#A} 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(!this.#i.test(r))throw new TypeError(`Invalid ${this.#A} bot id`);return this.#g(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.#l.then(async()=>{try{await LIe(this.#e)}catch(o){if(o?.code!=="ENOENT")throw o}this.#s=Tb});this.#l=r.then(()=>{},()=>{}),await r}#a(r){if(!r||typeof r!="object")return null;let o=ds(r.platformId),i=ds(r.botId),e=ds(r.tokenRef),a=ds(r.name);if(!o||!i||!e||!a||!this.#i.test(i)||!this.#I.test(e))return null;let A=bd(o,{botPrefix:this.#r,tokenRefPrefix:this.#t});if(A.botId!==i||A.tokenRef!==e)return null;let t=this.#o(r);return!t||typeof t!="object"||Array.isArray(t)?null:Object.freeze({botId:i,platformId:o,tokenRef:e,name:a,username:ds(r.username),createdAt:ds(r.createdAt)??new Date().toISOString(),connectedAt:ds(r.connectedAt),...t})}#n(r){if(!r||r.version!==1||!Array.isArray(r.bots))return null;let o=r.bots.map(A=>this.#a(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.tokenRef))return null;i.add(A.botId),e.add(A.platformId),a.add(A.tokenRef)}return Object.freeze({version:1,bots:Object.freeze(o)})}async#g(r){let o,i=this.#l.then(async()=>{let e=[...this.#s.bots];o=r(e);let a=Object.freeze({version:1,bots:Object.freeze(e)});await this.#c(a),this.#s=a});return this.#l=i.then(()=>{},()=>{}),await i,o}async#c(r){await PIe(HIe(this.#e),{recursive:!0,mode:448});let o=`${this.#e}.tmp`;await UIe(o,`${JSON.stringify(r,null,2)} `,{encoding:"utf8",mode:384}),await FIe(o,this.#e)}};var YH=Object.freeze({botPrefix:"discord",tokenRefPrefix:"DSH_DISCORD_BOT_TOKEN"});function jH(n){return bd(n,YH)}function JH(n){return zB(n,"Discord\u673A\u5668\u4EBA")}var VB=class extends fl{constructor(r){super(r,{channel:"Discord",...YH})}};function Md(n){return typeof n=="string"&&n.trim()?n.trim():null}function _d(n,r){return Object.freeze({code:n,message:r})}var Ql=class{#e;#A;#r;#t;#o;#i;#I;#s;#l;#a=new Map;#n=new Map;#g=new Map;#c=0;#p=!1;constructor({descriptor:r,credentials:o,configStore:i,inspectToken:e,deriveIdentity:a,maskPlatformId:A,createRuntime:t,deleteState:s=async()=>{},logger:g=console}){if(!r?.key||!r?.label||!r?.connectionLabel)throw new TypeError("TokenBotController requires a channel descriptor");if(!o||typeof o.resolve!="function"||typeof o.set!="function"||typeof o.unset!="function")throw new TypeError(`${r.label} requires the DSH credential provider`);if(!i||typeof i.list!="function"||typeof i.save!="function"||typeof i.remove!="function")throw new TypeError(`${r.label} requires a config store`);if(typeof e!="function"||typeof a!="function"||typeof A!="function"||typeof t!="function")throw new TypeError(`${r.label} controller dependencies are incomplete`);this.#e=r,this.#A=o,this.#r=i,this.#t=e,this.#o=a,this.#i=A,this.#I=t,this.#s=s,this.#l=g}async initialize(){if(this.#p)return this.status();for(let r of this.#r.list())await this.#u(r.botId,async()=>{if(this.#p||this.#a.get(r.botId)?.status?.ready)return;let o=await this.#E(r.tokenRef);if(!o){this.#n.set(r.botId,_d("missing-token",`${this.#e.label}\u673A\u5668\u4EBA\u51ED\u636E\u7F3A\u5931\uFF0C\u8BF7\u79FB\u9664\u540E\u91CD\u65B0\u63A5\u5165\u3002`));return}try{await this.#h(r,o),this.#n.delete(r.botId)}catch(i){this.#n.set(r.botId,_d("connection-failed",`${this.#e.label}\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5\u3002`)),this.#l.warn?.(`[dsh-im:${this.#e.key}] bot ${r.botId} failed to initialize:`,i)}finally{this.#B()}});return this.status()}async bindCredentials({token:r}={}){if(this.#p)throw new Error(`${this.#e.label} controller is closed`);let o=Md(r);if(!o)throw new TypeError(`${this.#e.label} Bot Token is required`);let i=await this.#t(o),e=Md(i?.platformId),a=Md(i?.name);if(!e||!a)throw new Error(`${this.#e.label} returned an invalid bot identity`);let A=this.#o(e);return await this.#u(A.botId,async()=>{if(this.#p)throw new Error(`${this.#e.label} controller is closed`);let t=this.#r.getByPlatformId(e),s=await this.#A.resolve(A.tokenRef).catch(()=>{}),g={botId:A.botId,platformId:e,tokenRef:A.tokenRef,name:a,username:Md(i.username),createdAt:t?.createdAt??new Date().toISOString(),connectedAt:new Date().toISOString()};await this.#A.set(A.tokenRef,o);try{await this.#r.save(g)}catch(c){throw await this.#C(A.tokenRef,s),c}try{await this.#h(g,o),this.#n.delete(A.botId)}catch(c){this.#n.set(A.botId,_d("connection-failed",`${this.#e.label}\u673A\u5668\u4EBA\u5DF2\u63A5\u5165\uFF0C\u6D88\u606F\u8FDE\u63A5\u6682\u672A\u5C31\u7EEA\u3002`)),this.#l.warn?.(`[dsh-im:${this.#e.key}] bot ${A.botId} credential connection failed:`,c)}this.#B()}),this.status()}async reconnectBot(r){let o=this.#r.get(r);if(!o)throw new Error(`Unknown ${this.#e.label} bot`);return await this.#u(r,async()=>{let i=await this.#E(o.tokenRef);if(!i)throw new Error(`${this.#e.label} bot token is missing`);try{await this.#h(o,i),this.#n.delete(r)}catch(e){throw this.#n.set(r,_d("connection-failed",`${this.#e.label}\u8FDE\u63A5\u4ECD\u672A\u5C31\u7EEA\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`)),e}finally{this.#B()}}),this.status()}async updateBotConfig(r,o){if(this.#p)throw new Error(`${this.#e.label} controller is closed`);if(typeof o!="function")throw new TypeError("Bot config update must be a function");return await this.#u(r,async()=>{if(this.#p)throw new Error(`${this.#e.label} controller is closed`);let i=this.#r.get(r);if(!i)throw new Error(`Unknown ${this.#e.label} bot`);let e=await this.#E(i.tokenRef);if(!e)throw new Error(`${this.#e.label} bot token is missing`);if(this.#p)throw new Error(`${this.#e.label} controller is closed`);let a=o(i),A=await this.#r.save(a);try{await this.#h(A,e),this.#n.delete(r)}catch(t){throw this.#n.set(r,_d("connection-failed",`${this.#e.label}\u8FDE\u63A5\u4ECD\u672A\u5C31\u7EEA\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`)),t}finally{this.#B()}}),this.status()}async sendConnectionTest(r){let o=this.#r.get(r);if(!o)throw new Error(`Unknown ${this.#e.label} bot`);return this.#u(r,async()=>{let i=this.#a.get(r);if(!i?.status?.ready||typeof i.sendConnectionTest!="function"){let a=new Error(`${this.#e.label}\u673A\u5668\u4EBA\u5C1A\u672A\u8FDE\u63A5`);throw a.code="test-target-unavailable",a}let e=`${o.name}\uFF08${this.#i(o.platformId)}\uFF09`;return await i.sendConnectionTest(la(e,`${this.#e.label}\u673A\u5668\u4EBA`)),{sent:!0}})}async deleteBot(r){let o=this.#r.get(r);if(!o)throw new Error(`Unknown ${this.#e.label} bot`);return await this.#u(r,async()=>{let i=await this.#A.resolve(o.tokenRef).catch(()=>{});await this.#d(r);try{await this.#A.unset(o.tokenRef),await this.#r.remove(r)}catch(e){throw i?.value&&(await this.#A.set(o.tokenRef,i.value).catch(()=>{}),await this.#h(o,i.value).catch(()=>{})),new Error(`Unable to remove the ${this.#e.label} bot safely.`,{cause:e})}await this.#s({botId:r,config:o}).catch(e=>{this.#l.warn?.(`[dsh-im:${this.#e.key}] bot ${r} state cleanup failed:`,e)}),this.#n.delete(r),this.#B()}),this.status()}status(){let r=this.#r.list().map(i=>{let e=this.#a.get(i.botId)?.status??null,a=e?.ready===!0&&e.connectionState==="connected"&&e.harnessReachable===!0,A=a?"connected":e?.connectionState==="connecting"?"connecting":this.#n.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,idMasked:this.#i(i.platformId)},health:{status:a?"healthy":A==="error"?"error":"offline",summary:a?`${this.#e.label}${this.#e.connectionLabel}\u8FD0\u884C\u6B63\u5E38`:A==="error"?`${this.#e.label}\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5`:`${this.#e.label}\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.#n.get(i.botId)??null)}}),o=r.filter(i=>i.connected).length;return{schemaVersion:1,revision:this.#c,state:r.length===0?"disconnected":o===r.length?"connected":o>0?"degraded":"offline",bots:r,totals:{configured:r.length,connected:o}}}async close(){this.#p||(this.#p=!0,await Promise.allSettled([...this.#g.values()]),await Promise.allSettled([...this.#a.keys()].map(r=>this.#d(r))))}async#h(r,o){if(this.#p)throw new Error(`${this.#e.label} controller is closed`);if(await this.#d(r.botId),this.#p)throw new Error(`${this.#e.label} controller is closed`);let i=await this.#I({botId:r.botId,config:r,token:o});if(!i||typeof i.start!="function"||typeof i.stop!="function")throw new TypeError(`createRuntime returned an invalid ${this.#e.label} runtime`);this.#a.set(r.botId,i);try{await i.start()}catch(e){throw await i.stop().catch(()=>{}),this.#a.delete(r.botId),e}}async#d(r){let o=this.#a.get(r);this.#a.delete(r),await o?.stop().catch(i=>{this.#l.warn?.(`[dsh-im:${this.#e.key}] bot ${r} failed to stop cleanly:`,i)})}async#E(r){let o=await this.#A.resolve(r).catch(()=>{});return Md(o?.value)}async#C(r,o){o?.value?await this.#A.set(r,o.value).catch(()=>{}):await this.#A.unset(r).catch(()=>{})}#u(r,o){let a=(this.#g.get(r)??Promise.resolve()).catch(()=>{}).then(o).finally(()=>{this.#g.get(r)===a&&this.#g.delete(r)});return this.#g.set(r,a),a}#B(){this.#c+=1}};import{createHash as KIe}from"node:crypto";var xIe="https://discord.com/api/v10/",YIe=12e4,jIe=new Set([50001,50013]),JIe=new Set([40005]);function Eg(n){return typeof n=="string"&&n.trim()?n.trim():null}function qH(n,r){let o=AbortSignal.timeout(r);return n?AbortSignal.any([n,o]):o}function zH(n){return n?.reason instanceof Error?n.reason:new DOMException("The operation was aborted","AbortError")}function qIe(n,r){if(!Number.isInteger(n)||n<1)throw new TypeError(`${r} must be a positive integer`);return n}function VH(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 zIe(n){let r=Number(n?.providerCode),o=Number(n?.status),i=Eg(n?.message)??"",e="artifact-provider-rejected",a="Discord rejected the attachment.";o===401||o===403||jIe.has(r)?(e="artifact-permission-required",a="Discord denied permission to send the attachment."):o===413||JIe.has(r)||/(?:request|attachment|file).{0,24}too large/i.test(i)?(e="artifact-too-large",a="The attachment exceeds Discord's size limit."):o===429?(e="artifact-rate-limited",a="Discord rate-limited attachment delivery."):o>=500&&(e="artifact-delivery-uncertain",a="Discord attachment delivery result is uncertain.");let A=new Error(a,{cause:n});return A.code=e,VH(A,n)}function VIe(n){let r=new Error("Discord attachment delivery result is uncertain",{cause:n});return r.code="artifact-delivery-uncertain",VH(r,n)}function WIe(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 hs(n,r){let o=Eg(n);if(!o||!/^\d{5,30}$/.test(o))throw new TypeError(`Invalid Discord ${r}`);return o}function ZIe(n){return typeof n=="string"&&/^[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{20,}$/.test(n.trim())}var Nd=class{#e;#A;#r;#t;constructor({token:r,fetchImpl:o=fetch,baseUrl:i=xIe,fileUploadTimeoutMs:e=YIe}){if(!ZIe(r))throw new TypeError("Discord Bot Token is invalid");if(typeof o!="function")throw new TypeError("DiscordApi requires fetch");this.#e=r.trim(),this.#A=o,this.#r=new URL(i),this.#t=qIe(e,"fileUploadTimeoutMs")}getCurrentUser(r={}){return this.#o("users/@me",{...r,method:"GET"})}getGatewayBot(r={}){return this.#o("gateway/bot",{...r,method:"GET"})}createMessage({channelId:r,content:o,replyToMessageId:i,signal:e}){return this.#o(`channels/${hs(r,"channel id")}/messages`,{method:"POST",signal:e,body:{content:o,allowed_mentions:{parse:[],replied_user:!1},...i?{message_reference:{message_id:hs(i,"message id"),channel_id:hs(r,"channel id"),fail_if_not_exists:!1}}:{}}})}async createFileMessage({channelId:r,file:o,replyToMessageId:i,signal:e}){if(!o||typeof o!="object"||typeof o.fileName!="string"||!o.fileName||!Buffer.isBuffer(o.bytes))throw new TypeError("A Discord attachment is required");let a=Eg(o.deliveryKey)??Eg(o.artifactId),A=a?KIe("sha256").update(a).digest("hex").slice(0,25):void 0,t=new FormData;t.append("payload_json",JSON.stringify({allowed_mentions:{parse:[],replied_user:!1},attachments:[{id:0,filename:o.fileName}],...A?{nonce:A,enforce_nonce:!0}:{},...i?{message_reference:{message_id:hs(i,"message id"),channel_id:hs(r,"channel id"),fail_if_not_exists:!1}}:{}})),t.append("files[0]",new Blob([o.bytes],{type:o.mediaType??"application/octet-stream"}),o.fileName);let s=hs(r,"channel id");if(e?.aborted)throw zH(e);let g=qH(e,this.#t);try{return await this.#o(`channels/${s}/messages`,{method:"POST",signal:g,timeoutMs:this.#t,body:t,multipart:!0})}catch(c){throw e?.aborted?zH(e):c?.code?.startsWith?.("discord-")?zIe(c):VIe(c)}}editMessage({channelId:r,messageId:o,content:i,signal:e}){return this.#o(`channels/${hs(r,"channel id")}/messages/${hs(o,"message id")}`,{method:"PATCH",signal:e,body:{content:i,allowed_mentions:{parse:[],replied_user:!1}}})}sendTyping({channelId:r,signal:o}){return this.#o(`channels/${hs(r,"channel id")}/typing`,{method:"POST",signal:o,expectBody:!1})}async#o(r,{method:o,body:i,signal:e,timeoutMs:a=15e3,expectBody:A=!0,retry:t=!0,multipart:s=!1}){let g;try{g=await this.#A(new URL(r,this.#r),{method:o,headers:{authorization:`Bot ${this.#e}`,...s?{}:{"content-type":"application/json"},"user-agent":"DeepSeek-Harness-dsh-im (https://github.com/xmanrui/dsh-im, 1.1.0)"},...i===void 0?{}:{body:s?i:JSON.stringify(i)},signal:qH(e,a),redirect:"error"})}catch(I){throw I?.name==="AbortError"||I?.name==="TimeoutError"?I:new Error(`Discord ${o} transport failed`)}let c=null;if(A||g.status===429||!g.ok)try{c=await g.json()}catch{if(A){let I=new Error(`Discord ${o} returned invalid JSON`);throw I.status=g?.status,I}}if(g.status===429&&t){let I=Math.min(1e4,Math.max(50,Number(c?.retry_after)*1e3||1e3));return await WIe(I,e),this.#o(r,{method:o,body:i,signal:e,timeoutMs:a,expectBody:A,retry:!1,multipart:s})}if(!g.ok){let I=new Error(Eg(c?.message)??`Discord API failed with HTTP ${g.status}`);I.code=`discord-${g.status}`,I.status=g.status,(Number.isInteger(c?.code)||typeof c?.code=="string")&&(I.providerCode=c.code);let E=Number(c?.retry_after);throw Number.isFinite(E)&&E>=0&&(I.retry_after=E,I.retryAfter=E),I}return A?c:null}};async function WH(n,r={}){let i=await new Nd({token:n,...r}).getCurrentUser();if(!i?.id||i?.bot!==!0)throw new Error("Discord token does not belong to a bot");return{platformId:String(i.id),name:Eg(i.global_name)??Eg(i.username)??"Discord\u673A\u5668\u4EBA",username:Eg(i.username)}}var ZH="\u8FD9\u4E2A\u95EE\u9898\u5DF2\u5728\u5176\u4ED6\u5BA2\u6237\u7AEF\u5904\u7406\uFF0C\u65E0\u9700\u518D\u6B21\u56DE\u7B54\u3002",XH="\u4EFB\u52A1\u5DF2\u5B8C\u6210\u3002";function ka(n){return typeof n=="string"?n.trim():""}function XIe(n,r,o){return r.actor===o&&(n.kind!=="group"||n.addressed===!0)&&!Er(n)&&!Zt(n)&&!!ka(n.content)}function $Ie(n,r,o){let i=String(n??"\u7ED3\u679C\u6587\u4EF6").replace(/[\r\n]+/g," ").trim().slice(0,255)||"\u7ED3\u679C\u6587\u4EF6";switch(r?.code){case"artifact-delivery-uncertain":return`\u7ED3\u679C\u6587\u4EF6\u300C${i}\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 o?.key==="slack"?`\u7ED3\u679C\u6587\u4EF6\u300C${i}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46 Slack \u5E94\u7528\u7F3A\u5C11 files:write \u6743\u9650\u3002\u8BF7\u66F4\u65B0 Manifest\u3001\u91CD\u65B0\u5B89\u88C5\u5E94\u7528\u5E76\u91CD\u65B0\u8FDE\u63A5\u673A\u5668\u4EBA\u540E\u91CD\u8BD5\u3002`:o?.key==="discord"?`\u7ED3\u679C\u6587\u4EF6\u300C${i}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46\u673A\u5668\u4EBA\u7F3A\u5C11 Discord \u7684 Send Messages\u3001Attach Files \u6216 Read Message History \u6743\u9650\u3002`:o?.key==="telegram"?`\u7ED3\u679C\u6587\u4EF6\u300C${i}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46 Telegram \u4E0D\u5141\u8BB8\u673A\u5668\u4EBA\u5728\u5F53\u524D\u804A\u5929\u53D1\u9001\u6587\u6863\uFF0C\u8BF7\u68C0\u67E5\u804A\u5929\u6743\u9650\u3002`:`\u7ED3\u679C\u6587\u4EF6\u300C${i}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46\u5F53\u524D\u673A\u5668\u4EBA\u6CA1\u6709\u6587\u4EF6\u53D1\u9001\u6743\u9650\uFF0C\u8BF7\u68C0\u67E5\u6E20\u9053\u6743\u9650\u3002`;case"artifact-too-large":return`\u7ED3\u679C\u6587\u4EF6\u300C${i}\u300D\u8D85\u8FC7\u5F53\u524D\u6E20\u9053\u5927\u5C0F\u4E0A\u9650\uFF0C\u672A\u53D1\u9001\u3002`;case"artifact-empty":return`\u7ED3\u679C\u6587\u4EF6\u300C${i}\u300D\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001\u3002`;case"artifact-invalid":case"artifact-changed":case"artifact-unavailable":return`\u7ED3\u679C\u6587\u4EF6\u300C${i}\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${i}\u300D\u6682\u65F6\u88AB\u5F53\u524D\u6E20\u9053\u9650\u6D41\uFF0C\u672A\u80FD\u53D1\u9001\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`;case"artifact-provider-rejected":return`\u7ED3\u679C\u6587\u4EF6\u300C${i}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46\u5F53\u524D\u6E20\u9053\u62D2\u7EDD\u4E86\u8BE5\u6587\u4EF6\u6216\u6587\u4EF6\u6D88\u606F\u3002`;default:return`\u7ED3\u679C\u6587\u4EF6\u300C${i}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46\u5F53\u524D\u6E20\u9053\u6682\u65F6\u672A\u80FD\u53D1\u9001\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`}}function mi(){return{messagesReceived:0,messagesReplied:0,messagesRejected:0,lastMessageAt:null,lastReplyAt:null,lastRejectedAt:null,lastError:null}}var dn=class{#e;#A;#r;#t;#o;#i;#I;#s;#l=new Map;#a=new Map;#n=new Map;#g=new Set;#c=new Set;#p=new Set;#h;constructor({descriptor:r,bot:o,harness:i,state:e,status:a=mi(),logger:A=console,replyTimeoutMs:t=6e5,signal:s}){if(!r?.key||!r?.label)throw new TypeError("A channel descriptor is required");if(!o||typeof o.sendText!="function")throw new TypeError("A bot client is required");if(!i||!e)throw new TypeError("Harness client and state store are required");this.#e=r,this.#A=o,this.#r=i,this.#t=e,this.#o=a,this.#i=A,this.#I=t,this.#s=s,this.#h=new Va({label:r.key,logger:A})}get status(){return structuredClone(this.#o)}accept(r){if(this.#s?.aborted)return Promise.resolve();let o=ka(r?.conversationId),i=r?.kind==="group"?"group":"direct",e={...r,kind:i,conversationId:o},a=ka(e.messageId),A=ka(e.senderId);if(!a||!A||!o||e.senderIsBot===!0||this.#t.hasSeen(a)||this.#g.has(a))return Promise.resolve();this.#g.add(a),e.kind==="direct"&&Ki(this.#t,e.connectionTestTarget??e.replyTarget);let t=`${i}:${o}`,s=this.#a.get(t),g=ka(e.content),c=Zt(e)?null:Bi(g)?qi:fi(g)?Vi:Qi(g)?Wi:null;if(c&&(e.kind!=="group"||e.addressed===!0)){let E;return E=this.#E(e,a,t,c).finally(()=>{this.#g.delete(a),this.#p.delete(E)}),this.#p.add(E),E}let I=this.#h.claimReply({key:t,actor:A,messageId:a,text:Er(e)||Zt(e)?"":e.content,addressed:e.kind!=="group"||e.addressed===!0,hasPendingQuestion:!!s,questionCompletion:s?.submitting||s?.claimedReplyMessageId?s.queue:null,isQuestionPending:()=>this.#a.has(t),send:E=>this.#A.sendText(e.replyTarget,E)});if(I){let E;return E=I.process(async()=>this.#t.hasSeen(a)?!1:(await this.#t.markSeen(a),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString(),!0)).finally(()=>{this.#g.delete(a),this.#c.delete(E)}),this.#c.add(E),E}if(s&&s.actor!==A)return this.#d(e,a,A,t);if(s?.submitting||s?.claimedReplyMessageId)return this.#d(e,a,A,t);if(s){XIe(e,s,A)&&(s.claimedReplyMessageId=a);let B=(s.queue??Promise.resolve()).catch(()=>{}).then(()=>this.#B(e,a,A,t,s)).finally(()=>{this.#g.delete(a),s.claimedReplyMessageId===a&&(s.claimedReplyMessageId=null),s.queue===B&&(s.queue=null)});return s.queue=B,B}return this.#d(e,a,A,t)}#d(r,o,i,e,{releaseMessageId:a=!0,alreadyRecorded:A=!1}={}){let s=(this.#l.get(e)??Promise.resolve()).catch(()=>{}).then(()=>this.#u(r,o,i,e,{alreadyRecorded:A})).finally(()=>{a&&this.#g.delete(o),this.#l.get(e)===s&&this.#l.delete(e)});return this.#l.set(e,s),s}async waitForIdle(){await Promise.allSettled([...this.#l.values(),...[...this.#a.values()].flatMap(r=>r.queue?[r.queue]:[]),...this.#c,...this.#p])}async#E(r,o,i,e){if(this.#t.hasSeen(o))return;await this.#t.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString();let a=r.replyTarget;try{let A=await e(ka(r.content),this.#r,this.#t,i,{signal:this.#s,hasImages:Er(r),hasFiles:Zt(r),pendingInteraction:this.#a.has(i)||this.#h.hasPending(i),control:{owner:this,key:i}});A?.stopped&&await Promise.allSettled([this.#M(i),this.#h.closeRoute(i)]);for(let t of A?.messages??[A?.message])t&&await this.#A.sendText(a,t);this.#o.lastError=null}catch(A){if(A?.code==="turn-stopped"||this.#s?.aborted)return;this.#o.lastError=A?.message??String(A),this.#i.error?.(`[dsh-im:${this.#e.key}] failed to process a command:`,A),await this.#A.sendText(a,"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002").catch(()=>{})}}sendConnectionTest(r){return sg({state:this.#t,text:r,channelLabel:`${this.#e.label}\u673A\u5668\u4EBA`,send:(o,i)=>this.#A.sendText(o,i)})}async#C(r,o,i=[],e){let a=await $i({artifacts:i,baseReceipt:e,deliveryId:o,channelKey:this.#e.key,signal:this.#s,sendImage:typeof this.#A.sendImage=="function"?A=>this.#A.sendImage(r,A):void 0,sendFile:typeof this.#A.sendFile=="function"?A=>this.#A.sendFile(r,A):void 0,sendFailureNotice:(A,t)=>this.#A.sendText(r,$Ie(A?.fileName,t,this.#e)),logger:this.#i});return this.#o.artifactsSent=(this.#o.artifactsSent??0)+a.artifactsSent,this.#o.artifactSendErrors=(this.#o.artifactSendErrors??0)+a.artifactSendErrors,{receipt:a.receipt,userVisible:a.userVisible}}async#u(r,o,i,e,{alreadyRecorded:a=!1}={}){if(!a){if(this.#t.hasSeen(o))return;await this.#t.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString()}let A=r.replyTarget,t=ka(r.content),s=null,g=!1;try{if(this.#s?.throwIfAborted(),r.kind==="group"&&r.addressed!==!0){this.#o.messagesRejected+=1,this.#o.lastRejectedAt=new Date().toISOString();return}let c=Er(r),I=Zt(r);if(!t&&!c&&!I){await this.#A.sendText(A,"\u76EE\u524D\u652F\u6301\u6587\u5B57\u3001\u56FE\u7247\u548C\u6587\u4EF6\u6D88\u606F\u3002");return}let E=t.toLowerCase();if(!c&&!I&&E==="/help"){await this.#A.sendText(A,[`${this.#e.label}\u673A\u5668\u4EBA\u5DF2\u8FDE\u63A5 DeepSeek Harness\u3002`,"","\u76F4\u63A5\u53D1\u9001\u6587\u5B57\u3001\u56FE\u7247\u6216\u6587\u4EF6\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(` -`));return}if(!c&&!I&&E==="/status"){await this.#r.ensureRunning({signal:this.#s}),await this.#A.sendText(A,`${this.#e.label}\u673A\u5668\u4EBA\u4E0E DeepSeek Harness \u8FDE\u63A5\u6B63\u5E38\u3002`);return}let B=!c&&!I?await zi(t,this.#r,e):null;if(B){for(let x of B.messages??[B.message])await this.#A.sendText(A,x);return}if(!c&&!I&&E==="/new"){await this.#t.clearSession(e),await this.#A.sendText(A,"\u5DF2\u5F00\u542F\u65B0\u4F1A\u8BDD\u3002\u8BF7\u53D1\u9001\u4F60\u7684\u95EE\u9898\u3002");return}let f=!c&&!I?await Ji(t,this.#r,this.#t,e,{signal:this.#s}):null;if(f){await this.#A.sendText(A,f.message);return}await this.#A.sendTyping?.(A).catch(x=>{this.#i.warn?.(`[dsh-im:${this.#e.key}] typing indicator failed:`,x)});let Q=!1;if(typeof this.#A.openDeliveryStream=="function")try{s=await this.#A.openDeliveryStream(A),g=!0}catch(x){this.#i.warn?.(`[dsh-im:${this.#e.key}] unable to start a semantic reply stream; using final delivery:`,x)}else if(typeof this.#A.openStream=="function")try{s=await this.#A.openStream(A)}catch(x){this.#i.warn?.(`[dsh-im:${this.#e.key}] unable to start a streamed reply; using text:`,x)}let y=c?await Yi(r,{signal:this.#s}):void 0,{answer:m,artifacts:_=[]}=await Za({harness:this.#r,state:this.#t,key:e,text:t,content:y,createOptions:this.#s?{signal:this.#s}:void 0,existsOptions:this.#s?{signal:this.#s}:void 0,askOptions:{timeoutMs:this.#I,signal:this.#s,control:{owner:this,key:e},onUpdate:s?async x=>{let J=x.type==="text"?x.text:x.type==="tool"?`\u6B63\u5728\u4F7F\u7528${x.name}\u2026`:x.text;if(J){let F=x.type==="text"?"markdown":"plain";await s.update(g?En(J,F):J)}}:void 0,onInteraction:x=>this.#Q(x,{key:e,actor:i,target:A,requiresMention:r.kind==="group"}),onInteractionResolved:x=>this.#m(x),files:r.files}}),N=!ka(m)&&_.length>0?XH:m,G=N===XH&&!ka(m)?"plain":"markdown",Y=null,j=null;if(s)try{let x=await s.finish(g?En(N,G):N);Q=!0,j=Ut({deliveryId:o,presentation:x?.presentation??s.presentation??`${this.#e.key}-stream`,providerMessageIds:[...Xa(s),...Xa(x)],deliveryOutcome:x?.deliveryOutcome,reason:x?.reason})}catch(x){s.cancel?.(),this.#i.warn?.(`[dsh-im:${this.#e.key}] streamed reply finalization failed; using text:`,x)}if(!Q)try{let x=typeof this.#A.sendDelivery=="function"?await this.#A.sendDelivery(A,En(N,G)):await this.#A.sendText(A,N);j=Ut({deliveryId:o,presentation:x?.presentation??`${this.#e.key}-text`,providerMessageIds:Xa(x),deliveryOutcome:x?.deliveryOutcome,reason:x?.reason})}catch(x){Y=x}let K=await this.#C(A,o,_,j);if(Y&&!K.userVisible)throw Y;return K.userVisible&&(this.#o.messagesReplied+=1,this.#o.lastReplyAt=new Date().toISOString(),this.#o.lastError=null),K.receipt}catch(c){if(c?.code==="turn-stopped"){if(s)try{await s.finish("\u5DF2\u505C\u6B62\u3002")}catch{s.cancel?.()}return}if(this.#s?.aborted){s?.cancel?.();return}this.#o.lastError=c?.message??String(c);let I=async f=>{if(typeof s?.fail!="function")return!1;try{let Q=await s.fail(f);return!!Q&&Q.deliveryOutcome!=="failed"}catch(Q){return this.#i.warn?.(`[dsh-im:${this.#e.key}] unable to finalize the failed stream:`,Q),!1}},E=ji(c);if(E){if(await I(E))return;s?.cancel?.();try{await this.#A.sendText(A,E)}catch(f){this.#i.error?.(`[dsh-im:${this.#e.key}] failed to send the image error reply:`,f)}return}let B=Xi(c);if(B){if(await I(B))return;s?.cancel?.();try{await this.#A.sendText(A,B)}catch(f){this.#i.error?.(`[dsh-im:${this.#e.key}] failed to send the file error reply:`,f)}return}if(this.#i.error?.(`[dsh-im:${this.#e.key}] failed to process a message:`,c),await I("\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"))return;s?.cancel?.();try{await this.#A.sendText(A,"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002")}catch(f){this.#i.error?.(`[dsh-im:${this.#e.key}] failed to send the safe error reply:`,f)}}finally{await Promise.allSettled([this.#M(e),this.#h.closeRoute(e)])}}async#B(r,o,i,e,a){if(this.#s?.aborted)return;let A=this.#a.get(e),t=a.claimedReplyMessageId===o;if(!A||A!==a||A.submitting)return t&&(!A||A!==a)?this.#y(r,o):this.#d(r,o,i,e,{releaseMessageId:!1});if(this.#t.hasSeen(o))return;if(await this.#t.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString(),r.kind==="group"&&r.addressed!==!0){this.#o.messagesRejected+=1,this.#o.lastRejectedAt=new Date().toISOString();return}let s=r.replyTarget,g=ka(r.content);if(!g||Er(r)||Zt(r)){try{await this.#A.sendText(s,"\u8BF7\u7528\u6587\u5B57\u56DE\u7B54\u5F53\u524D\u95EE\u9898\u3002")}catch(E){this.#i.error?.(`[dsh-im:${this.#e.key}] failed to reject a non-text interaction reply:`,E)}return}let c=this.#a.get(e);if(!c||c!==a||c.submitting)return t&&(!c||c!==a)?this.#y(r,o,{alreadyRecorded:!0}):this.#d(r,o,i,e,{releaseMessageId:!1,alreadyRecorded:!0});if(c.target=s,c.needsPresentation){let E=c.presentationTask!==null;try{await this.#f(c)}catch(f){this.#o.lastError=`${this.#e.label}\u4EA4\u4E92\u95EE\u9898\u53D1\u9001\u5931\u8D25\u3002`,this.#i.error?.(`[dsh-im:${this.#e.key}] failed to retry an interaction question:`,f),c.interaction.reconnect?.();return}let B=this.#a.get(e);if(!B||B!==a||B.submitting)return t&&(!B||B!==a)?this.#y(r,o,{alreadyRecorded:!0}):this.#d(r,o,i,e,{releaseMessageId:!1,alreadyRecorded:!0});if(!E)return}let I=c.questions[c.index];if(I){if(c.answers.push(za(I,g)),c.index+=1,c.indexthis.#A.sendText(e,c)});if(r?.kind!=="question")return;let A=r?.payload?.questions,t=ka(r?.interactionId)||ka(r?.rpcId);if(!ka(r?.rpcId)||!t||!ka(r?.sessionId)||!Array.isArray(A)||A.length===0||A.some(c=>!Ja(c))){this.#i.warn?.(`[dsh-im:${this.#e.key}] ignored an invalid Harness question interaction`);return}if(r.recovered===!0){await this.#N(r,`${this.#e.label} safely cancelled an interaction left by an earlier client.`);try{await this.#A.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(c){this.#i.error?.(`[dsh-im:${this.#e.key}] failed to send an interaction recovery notice:`,c)}return}let s=this.#a.get(o);if(s?.interactionId===t){s.interaction=r,s.needsPresentation&&await this.#f(s);return}if(this.#n.has(t))return;if(s){this.#i.warn?.(`[dsh-im:${this.#e.key}] cancelled a second pending Harness question`),await this.#N(r,`${this.#e.label} is already handling another user interaction.`);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,submitting:!1,needsPresentation:!0,presentationTask:null};this.#a.set(o,g),this.#n.set(t,o),await this.#f(g)}async#m(r){if(r?.kind==="approval"){await this.#h.handleResolved(r);return}let o=ka(r?.interactionId);if(r?.kind!=="question"||!o)return;let i=this.#n.get(o);i&&this.#w(i,o)}#f(r){if(r.presentationTask)return r.presentationTask;let o=r.questions[r.index];if(!o)return Promise.resolve();let i=(async()=>{await this.#A.sendText(r.target,qa(o,r.index,r.questions.length,{requiresMention:r.requiresMention})),r.needsPresentation=!1})();return r.presentationTask=i,i.then(()=>{r.presentationTask===i&&(r.presentationTask=null)},()=>{r.presentationTask===i&&(r.presentationTask=null)}),i}async#y(r,o,{alreadyRecorded:i=!1}={}){if(!i){if(this.#t.hasSeen(o))return;await this.#t.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString()}try{await this.#A.sendText(r.replyTarget,ZH)}catch(e){this.#i.error?.(`[dsh-im:${this.#e.key}] failed to send an expired interaction notice:`,e)}}#D(r,o){let i=this.#a.get(r);return!i||o!==void 0&&i.interactionId!==o?null:(this.#a.delete(r),this.#n.delete(i.interactionId),i)}#w(r,o){return this.#D(r,o)!==null}async#N(r,o){try{await r.respond({ok:!1,error:{code:"cancelled",message:o,details:{}}},{signal:AbortSignal.timeout(5e3)})}catch(i){if(i?.code!=="interaction-not-pending")throw i}}async#M(r){let o=this.#D(r);if(!(!o||o.kind!=="question"))try{await this.#N(o.interaction,`The ${this.#e.label} interaction ended before the user answered.`)}catch(i){this.#i.warn?.(`[dsh-im:${this.#e.key}] failed to cancel a pending Harness interaction:`,i)}}};var Rb=Object.freeze({key:"discord",label:"Discord",connectionLabel:" Gateway \u957F\u8FDE\u63A5"}),WB=class extends dn{constructor(r){super({descriptor:Rb,...r})}};var Td=class extends Ql{constructor(r){super({...r,descriptor:Rb,inspectToken:r.inspectToken??WH,deriveIdentity:jH,maskPlatformId:JH})}};var ZB=class extends or{constructor(r){super({...r,rpcIdPrefix:"discord",logPrefix:"dsh-discord"})}};function hn(n,r){let o=typeof n=="string"?n.trim():"";if(!o)return[];let i=[],e=o;for(;e.length>r;){let a=e.lastIndexOf(` +`));return}if(!c&&!I&&E==="/status"){await this.#r.ensureRunning({signal:this.#s}),await this.#A.sendText(A,`${this.#e.label}\u673A\u5668\u4EBA\u4E0E DeepSeek Harness \u8FDE\u63A5\u6B63\u5E38\u3002`);return}let B=!c&&!I?await zi(t,this.#r,e):null;if(B){for(let x of B.messages??[B.message])await this.#A.sendText(A,x);return}if(!c&&!I&&E==="/new"){await this.#t.clearSession(e),await this.#A.sendText(A,"\u5DF2\u5F00\u542F\u65B0\u4F1A\u8BDD\u3002\u8BF7\u53D1\u9001\u4F60\u7684\u95EE\u9898\u3002");return}let f=!c&&!I?await Ji(t,this.#r,this.#t,e,{signal:this.#s}):null;if(f){await this.#A.sendText(A,f.message);return}await this.#A.sendTyping?.(A).catch(x=>{this.#i.warn?.(`[dsh-im:${this.#e.key}] typing indicator failed:`,x)});let Q=!1;if(typeof this.#A.openDeliveryStream=="function")try{s=await this.#A.openDeliveryStream(A),g=!0}catch(x){this.#i.warn?.(`[dsh-im:${this.#e.key}] unable to start a semantic reply stream; using final delivery:`,x)}else if(typeof this.#A.openStream=="function")try{s=await this.#A.openStream(A)}catch(x){this.#i.warn?.(`[dsh-im:${this.#e.key}] unable to start a streamed reply; using text:`,x)}let y=c?await Yi(r,{signal:this.#s}):void 0,{answer:m,artifacts:_=[]}=await Za({harness:this.#r,state:this.#t,key:e,text:t,content:y,createOptions:this.#s?{signal:this.#s}:void 0,existsOptions:this.#s?{signal:this.#s}:void 0,askOptions:{timeoutMs:this.#I,signal:this.#s,control:{owner:this,key:e},onUpdate:s?async x=>{let J=x.type==="text"?x.text:x.type==="tool"?`\u6B63\u5728\u4F7F\u7528${x.name}\u2026`:x.text;if(J){let F=x.type==="text"?"markdown":"plain";await s.update(g?En(J,F):J)}}:void 0,onInteraction:x=>this.#Q(x,{key:e,actor:i,target:A,requiresMention:r.kind==="group"}),onInteractionResolved:x=>this.#m(x),files:r.files}}),N=!ka(m)&&_.length>0?XH:m,G=N===XH&&!ka(m)?"plain":"markdown",Y=null,j=null;if(s)try{let x=await s.finish(g?En(N,G):N);Q=!0,j=Ut({deliveryId:o,presentation:x?.presentation??s.presentation??`${this.#e.key}-stream`,providerMessageIds:[...Xa(s),...Xa(x)],deliveryOutcome:x?.deliveryOutcome,reason:x?.reason})}catch(x){s.cancel?.(),this.#i.warn?.(`[dsh-im:${this.#e.key}] streamed reply finalization failed; using text:`,x)}if(!Q)try{let x=typeof this.#A.sendDelivery=="function"?await this.#A.sendDelivery(A,En(N,G)):await this.#A.sendText(A,N);j=Ut({deliveryId:o,presentation:x?.presentation??`${this.#e.key}-text`,providerMessageIds:Xa(x),deliveryOutcome:x?.deliveryOutcome,reason:x?.reason})}catch(x){Y=x}if(j?.deliveryOutcome==="failed"){let x=j.reason??"text-delivery-failed";Y=new Error(`Final text delivery failed (${x})`),Y.code=x}let K=await this.#C(A,o,_,j);if(Y&&!K.userVisible)throw Y.deliveryReceipt=K.receipt,Y;return K.userVisible&&(this.#o.messagesReplied+=1,this.#o.lastReplyAt=new Date().toISOString(),this.#o.lastError=null),K.receipt}catch(c){if(c?.code==="turn-stopped"){if(s)try{await s.finish("\u5DF2\u505C\u6B62\u3002")}catch{s.cancel?.()}return}if(this.#s?.aborted){s?.cancel?.();return}this.#o.lastError=c?.message??String(c);let I=async f=>{if(typeof s?.fail!="function")return!1;try{let Q=await s.fail(f);return!!Q&&Q.deliveryOutcome!=="failed"}catch(Q){return this.#i.warn?.(`[dsh-im:${this.#e.key}] unable to finalize the failed stream:`,Q),!1}},E=ji(c);if(E){if(await I(E))return;s?.cancel?.();try{await this.#A.sendText(A,E)}catch(f){this.#i.error?.(`[dsh-im:${this.#e.key}] failed to send the image error reply:`,f)}return}let B=Xi(c);if(B){if(await I(B))return;s?.cancel?.();try{await this.#A.sendText(A,B)}catch(f){this.#i.error?.(`[dsh-im:${this.#e.key}] failed to send the file error reply:`,f)}return}if(this.#i.error?.(`[dsh-im:${this.#e.key}] failed to process a message:`,c),await I("\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"))return c.deliveryReceipt;s?.cancel?.();try{await this.#A.sendText(A,"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002")}catch(f){this.#i.error?.(`[dsh-im:${this.#e.key}] failed to send the safe error reply:`,f)}return c.deliveryReceipt}finally{await Promise.allSettled([this.#M(e),this.#h.closeRoute(e)])}}async#B(r,o,i,e,a){if(this.#s?.aborted)return;let A=this.#a.get(e),t=a.claimedReplyMessageId===o;if(!A||A!==a||A.submitting)return t&&(!A||A!==a)?this.#y(r,o):this.#d(r,o,i,e,{releaseMessageId:!1});if(this.#t.hasSeen(o))return;if(await this.#t.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString(),r.kind==="group"&&r.addressed!==!0){this.#o.messagesRejected+=1,this.#o.lastRejectedAt=new Date().toISOString();return}let s=r.replyTarget,g=ka(r.content);if(!g||Er(r)||Zt(r)){try{await this.#A.sendText(s,"\u8BF7\u7528\u6587\u5B57\u56DE\u7B54\u5F53\u524D\u95EE\u9898\u3002")}catch(E){this.#i.error?.(`[dsh-im:${this.#e.key}] failed to reject a non-text interaction reply:`,E)}return}let c=this.#a.get(e);if(!c||c!==a||c.submitting)return t&&(!c||c!==a)?this.#y(r,o,{alreadyRecorded:!0}):this.#d(r,o,i,e,{releaseMessageId:!1,alreadyRecorded:!0});if(c.target=s,c.needsPresentation){let E=c.presentationTask!==null;try{await this.#f(c)}catch(f){this.#o.lastError=`${this.#e.label}\u4EA4\u4E92\u95EE\u9898\u53D1\u9001\u5931\u8D25\u3002`,this.#i.error?.(`[dsh-im:${this.#e.key}] failed to retry an interaction question:`,f),c.interaction.reconnect?.();return}let B=this.#a.get(e);if(!B||B!==a||B.submitting)return t&&(!B||B!==a)?this.#y(r,o,{alreadyRecorded:!0}):this.#d(r,o,i,e,{releaseMessageId:!1,alreadyRecorded:!0});if(!E)return}let I=c.questions[c.index];if(I){if(c.answers.push(za(I,g)),c.index+=1,c.indexthis.#A.sendText(e,c)});if(r?.kind!=="question")return;let A=r?.payload?.questions,t=ka(r?.interactionId)||ka(r?.rpcId);if(!ka(r?.rpcId)||!t||!ka(r?.sessionId)||!Array.isArray(A)||A.length===0||A.some(c=>!Ja(c))){this.#i.warn?.(`[dsh-im:${this.#e.key}] ignored an invalid Harness question interaction`);return}if(r.recovered===!0){await this.#N(r,`${this.#e.label} safely cancelled an interaction left by an earlier client.`);try{await this.#A.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(c){this.#i.error?.(`[dsh-im:${this.#e.key}] failed to send an interaction recovery notice:`,c)}return}let s=this.#a.get(o);if(s?.interactionId===t){s.interaction=r,s.needsPresentation&&await this.#f(s);return}if(this.#n.has(t))return;if(s){this.#i.warn?.(`[dsh-im:${this.#e.key}] cancelled a second pending Harness question`),await this.#N(r,`${this.#e.label} is already handling another user interaction.`);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,submitting:!1,needsPresentation:!0,presentationTask:null};this.#a.set(o,g),this.#n.set(t,o),await this.#f(g)}async#m(r){if(r?.kind==="approval"){await this.#h.handleResolved(r);return}let o=ka(r?.interactionId);if(r?.kind!=="question"||!o)return;let i=this.#n.get(o);i&&this.#w(i,o)}#f(r){if(r.presentationTask)return r.presentationTask;let o=r.questions[r.index];if(!o)return Promise.resolve();let i=(async()=>{await this.#A.sendText(r.target,qa(o,r.index,r.questions.length,{requiresMention:r.requiresMention})),r.needsPresentation=!1})();return r.presentationTask=i,i.then(()=>{r.presentationTask===i&&(r.presentationTask=null)},()=>{r.presentationTask===i&&(r.presentationTask=null)}),i}async#y(r,o,{alreadyRecorded:i=!1}={}){if(!i){if(this.#t.hasSeen(o))return;await this.#t.markSeen(o),this.#o.messagesReceived+=1,this.#o.lastMessageAt=new Date().toISOString()}try{await this.#A.sendText(r.replyTarget,ZH)}catch(e){this.#i.error?.(`[dsh-im:${this.#e.key}] failed to send an expired interaction notice:`,e)}}#D(r,o){let i=this.#a.get(r);return!i||o!==void 0&&i.interactionId!==o?null:(this.#a.delete(r),this.#n.delete(i.interactionId),i)}#w(r,o){return this.#D(r,o)!==null}async#N(r,o){try{await r.respond({ok:!1,error:{code:"cancelled",message:o,details:{}}},{signal:AbortSignal.timeout(5e3)})}catch(i){if(i?.code!=="interaction-not-pending")throw i}}async#M(r){let o=this.#D(r);if(!(!o||o.kind!=="question"))try{await this.#N(o.interaction,`The ${this.#e.label} interaction ended before the user answered.`)}catch(i){this.#i.warn?.(`[dsh-im:${this.#e.key}] failed to cancel a pending Harness interaction:`,i)}}};var Rb=Object.freeze({key:"discord",label:"Discord",connectionLabel:" Gateway \u957F\u8FDE\u63A5"}),WB=class extends dn{constructor(r){super({descriptor:Rb,...r})}};var Td=class extends Ql{constructor(r){super({...r,descriptor:Rb,inspectToken:r.inspectToken??WH,deriveIdentity:jH,maskPlatformId:JH})}};var ZB=class extends or{constructor(r){super({...r,rpcIdPrefix:"discord",logPrefix:"dsh-discord"})}};function hn(n,r){let o=typeof n=="string"?n.trim():"";if(!o)return[];let i=[],e=o;for(;e.length>r;){let a=e.lastIndexOf(` `,r);anull,logger:t=console}){let s,g=[],c=null,I=null,E=null,B=!1,f=n,Q=()=>{B||I!==null||E||!c||(I=setTimeout(()=>{I=null;let y=c;c=null;let m=hn(y,r)[0]??n;E=Promise.resolve(m===f?void 0:e(s,m)).then(()=>{f=m}).catch(_=>t.warn?.("[dsh-im] streamed message update failed:",_)).finally(()=>{E=null,Q()})},o),I?.unref?.())};return{get messageId(){return s},get providerMessageIds(){return[...g]},async start(){return s=await i(n),(typeof s=="string"&&s.trim()||Number.isSafeInteger(s))&&g.push(String(s)),this},update(y){B||typeof y!="string"||!y.trim()||(c=y,Q())},async finish(y){if(B)throw new Error("Message stream is already closed");B=!0,I!==null&&clearTimeout(I),I=null,c=null,await E?.catch(()=>{});let m=hn(y,r),_=m[0]??"\u5904\u7406\u5B8C\u6210\u3002";_!==f&&await e(s,_),f=_;for(let N of m.slice(1)){let G=await a(N),Y=A(G);(typeof Y=="string"&&Y.trim()||Number.isSafeInteger(Y))&&g.push(String(Y))}},cancel(){B=!0,c=null,I!==null&&clearTimeout(I),I=null}}}async function $H(n){try{await n?.body?.cancel?.()}catch{}}function ele(n,r){return!Array.isArray(r)||r.some(o=>typeof o=="string"&&(n.hostname===o||o.startsWith(".")&&(n.hostname===o.slice(1)||n.hostname.endsWith(o))))}async function yl(n,{fetchImpl:r=fetch,headers:o,signal:i,allowedHosts:e}={}){let a=new URL(n);if(a.protocol!=="https:")throw new Error("File download URL must use HTTPS");if(!ele(a,e))throw new Error("File download URL is not hosted by the messaging platform");let A=await r(a,{method:"GET",headers:o,signal:i,redirect:"manual"});if(Number.isInteger(A?.status)&&A.status>=300&&A.status<400){await $H(A);let t=new Error(`File download redirect was blocked (HTTP ${A.status})`);throw t.code="file-redirect-blocked",t}if(!A?.ok){await $H(A);let t=new Error(`File download failed with HTTP ${A?.status??"unknown"}`);throw t.code="file-http-error",t.status=A?.status,t}if(A.body?.[Symbol.asyncIterator])return{stream:A.body};if(typeof A.arrayBuffer=="function"){let t=Buffer.from(await A.arrayBuffer());return{stream:(async function*(){yield t})()}}throw new Error("File download returned no readable body")}var Ale=4609,eK=Object.freeze([1e3,3e3,5e3,1e4,3e4]),tle=new Set(["image/jpeg","image/png","image/webp","image/gif"]),rle=new Map([[".jpg","image/jpeg"],[".jpeg","image/jpeg"],[".png","image/png"],[".webp","image/webp"],[".gif","image/gif"]]),AK=Object.freeze(["cdn.discordapp.com"]);function ale(n){let r=new URL(n);if(r.protocol!=="wss:")throw new Error("Discord returned an insecure Gateway URL");return r.searchParams.set("v","10"),r.searchParams.set("encoding","json"),r.href}function kb(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("Discord WebSocket does not support events")}function ile(n){return typeof n=="string"?n:typeof n?.data=="string"?n.data:Buffer.isBuffer(n)?n.toString("utf8"):Buffer.isBuffer(n?.data)?n.data.toString("utf8"):null}function ole(n){if(n===4004){let o=new Error("Discord Bot Token \u65E0\u6548\uFF0C\u8BF7\u91CD\u65B0\u586B\u5199\u3002");return o.code="discord-401",o}if(n===4013||n===4014){let o=new Error("Discord Gateway Intents \u914D\u7F6E\u4E0D\u6B63\u786E\uFF0C\u8BF7\u68C0\u67E5 Developer Portal \u7684 Bot \u8BBE\u7F6E\u3002");return o.code="discord-intents",o}let r=new Error(`Discord Gateway closed (${n||"unknown"})`);return r.code="discord-gateway-closed",r}function nle(n,r){return typeof n!="string"?"":n.replace(new RegExp(`<@!?${r}>`,"g"),"").trim()}function tK(n){let r=typeof n?.content_type=="string"?n.content_type.split(";",1)[0].trim().toLowerCase():"";if(tle.has(r))return r;let o=typeof n?.filename=="string"?n.filename.toLowerCase():"";for(let[i,e]of rle)if(o.endsWith(i))return e;return null}function rK(n){return Number.isSafeInteger(n)&&n>=0?n:void 0}function sle(n,r){let o=tK(n);return!o||typeof n?.url!="string"?null:{name:typeof n.filename=="string"?n.filename:void 0,mediaType:o,size:rK(n.size),load:i=>xi(n.url,{...i,fetchImpl:r,allowedHosts:AK})}}function gle(n,r){if(tK(n)||typeof n?.url!="string"||!n.url)return null;let o=typeof n.content_type=="string"&&n.content_type?n.content_type.split(";",1)[0].trim().toLowerCase():void 0;return{name:typeof n.filename=="string"&&n.filename?n.filename:String(n.id??"discord-file"),...o?{mediaType:o}:{},size:rK(n.size),load:({signal:i}={})=>yl(n.url,{fetchImpl:r,signal:i,allowedHosts:AK})}}function cle(n,r,{fetchImpl:o=fetch}={}){if(!n?.id||!n?.channel_id||!n?.author?.id)return null;let i=!n.guild_id,e=i||n.mentions?.some(a=>String(a?.id)===String(r));return{messageId:String(n.id),senderId:String(n.author.id),senderIsBot:n.author.bot===!0,kind:i?"direct":"group",conversationId:String(n.channel_id),content:nle(n.content??"",r),images:Array.isArray(n.attachments)?n.attachments.map(a=>sle(a,o)).filter(Boolean):[],files:Array.isArray(n.attachments)?n.attachments.map(a=>gle(a,o)).filter(Boolean):[],addressed:e,replyTarget:{channelId:String(n.channel_id),replyToMessageId:String(n.id)},connectionTestTarget:{channelId:String(n.channel_id)}}}var vb=class{#e;#A;constructor({api:r,signal:o}){this.#e=r,this.#A=o}async sendText(r,o){let i=hn(o,1900),e=[];for(let[a,A]of i.entries()){let t=await this.#e.createMessage({channelId:r.channelId,content:A,replyToMessageId:a===0?r.replyToMessageId:void 0,signal:this.#A});typeof t?.id=="string"&&t.id&&e.push(t.id)}return{providerMessageIds:e}}sendTyping(r){return this.#e.sendTyping({channelId:r.channelId,signal:this.#A})}sendFile(r,o){return this.#e.createFileMessage({channelId:r.channelId,file:o,replyToMessageId:r.replyToMessageId,signal:this.#A})}async openStream(r){return XB({limit:1900,create:async i=>(await this.#e.createMessage({channelId:r.channelId,content:i,replyToMessageId:r.replyToMessageId,signal:this.#A})).id,edit:(i,e)=>this.#e.editMessage({channelId:r.channelId,messageId:i,content:e,signal:this.#A}),sendRemainder:i=>this.#e.createMessage({channelId:r.channelId,content:i,signal:this.#A}),messageIdForResult:i=>i?.id}).start()}};function Ile(){return{startedAt:null,ready:!1,connectionState:"idle",harnessReachable:!1,lastCheckedAt:null,lastConnectedAt:null,lastError:null,...mi()}}var Rd=class{#e;#A;#r;#t;#o;#i;#I;#s;#l;#a;#n=Ile();#g=null;#c=null;#p=null;#h=null;#d=null;#E=null;#C=null;#u=null;#B=null;#Q=!0;#m=null;#f=0;#y=0;#D=!0;#w=null;constructor({config:r,token:o,harness:i,state:e,logger:a=console,replyTimeoutMs:A=6e5,connectTimeoutMs:t=2e4,createApi:s=I=>new Nd(I),createWebSocket:g=I=>new WebSocket(I),random:c=Math.random}){if(!r||!o||!i||!e)throw new TypeError("DiscordRuntime requires config, token, Harness, and state");if(typeof g!="function")throw new TypeError("DiscordRuntime requires WebSocket");this.#e=r,this.#A=o,this.#r=i,this.#t=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("Discord 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.#w?this.#w:(this.#w=this.#N().finally(()=>{this.#w=null}),this.#w)}async#N(){await this.stop(),this.#D=!1,this.#C=null,this.#E=null,this.#u=null,this.#f=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.#s({token:this.#A});this.#g=o;try{let[i,e]=await Promise.all([o.getCurrentUser({signal:r.signal}),o.getGatewayBot({signal:r.signal})]);if(String(i?.id??"")!==this.#e.platformId||i?.bot!==!0)throw new Error("Discord token identity does not match the saved bot");this.#d=e?.url;let a=new vb({api:o,signal:r.signal});this.#c=new WB({bot:a,harness:this.#r,state:this.#t,status:this.#n,logger:this.#o,replyTimeoutMs:this.#i,signal:r.signal});let A;try{await Promise.race([this.#M(!1),new Promise((t,s)=>{A=setTimeout(()=>s(new Error("Discord Gateway did not become ready in time")),this.#I)})])}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}}#M(r){if(this.#D)return Promise.reject(new Error("Discord runtime is stopped"));let o=++this.#y,i=ale(r&&this.#E?this.#E:this.#d),e=this.#l(i);this.#h=e;let a=!1;return new Promise((A,t)=>{let s=()=>{if(a||o!==this.#y)return;a=!0,this.#f=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()};kb(e,"message",g=>{if(o!==this.#y||this.#D)return;let c=ile(g);if(!c)return;let I;try{I=JSON.parse(c)}catch{this.#o.warn?.("[dsh-im:discord] ignored malformed Gateway JSON");return}if(Number.isSafeInteger(I.s)&&(this.#u=I.s),I.op===10){this.#O(I.d?.heartbeat_interval,e,o),r&&this.#C?this.#T(e,{op:6,d:{token:this.#A,session_id:this.#C,seq:this.#u}}):this.#T(e,{op:2,d:{token:this.#A,intents:Ale,properties:{os:process.platform,browser:"dsh-im",device:"dsh-im"}}});return}if(I.op===11){this.#Q=!0,this.#n.lastCheckedAt=Date.now();return}if(I.op===1){this.#b(e);return}if(I.op===7){e.close(4e3,"Reconnect requested");return}if(I.op===9){I.d!==!0&&(this.#C=null,this.#E=null,this.#u=null),e.close(4e3,"Invalid session");return}if(I.op===0){if(I.t==="READY")this.#C=I.d?.session_id??null,this.#E=I.d?.resume_gateway_url??null,s();else if(I.t==="RESUMED")s();else if(I.t==="MESSAGE_CREATE"){let E=cle(I.d,this.#e.platformId),B=this.#c;E&&B&&B.accept(E).catch(f=>{o!==this.#y||this.#D||this.#o.error?.(`[dsh-im:discord] bot ${this.#e.botId} message handling failed:`,f)})}}}),kb(e,"close",(g={})=>{if(o!==this.#y)return;if(this.#_(),this.#h===e&&(this.#h=null),this.#D){a||t(new DOMException("Stopped","AbortError"));return}let c=ole(Number(g.code)||0);if(this.#n.ready=!1,this.#n.connectionState="connecting",this.#n.lastError=c.message,a||(a=!0,t(c)),c.code==="discord-401"||c.code==="discord-intents"){this.#n.connectionState="failed";return}this.#P()}),kb(e,"error",()=>{o!==this.#y||this.#D||(this.#n.lastError="Discord Gateway WebSocket error")})})}#T(r,o){r.readyState===1&&r.send(JSON.stringify(o))}#O(r,o,i){if(this.#_(),!Number.isFinite(r)||r<1e3){o.close(4e3,"Invalid heartbeat interval");return}this.#Q=!0;let e=a=>{this.#B=setTimeout(()=>{if(!(this.#D||i!==this.#y||this.#h!==o)){if(!this.#Q){o.close(4e3,"Heartbeat was not acknowledged");return}this.#b(o),e(r)}},a),this.#B?.unref?.()};e(Math.floor(r*this.#a()))}#b(r){this.#Q=!1,this.#T(r,{op:1,d:this.#u})}#_(){this.#B!==null&&clearTimeout(this.#B),this.#B=null,this.#Q=!0}#P(){if(this.#D||this.#m!==null)return;let r=eK[Math.min(this.#f,eK.length-1)];this.#f+=1,this.#m=setTimeout(()=>{this.#m=null,this.#M(!!this.#C).catch(o=>{this.#D||(this.#o.warn?.("[dsh-im:discord] Gateway reconnect failed:",o),this.#P())})},r),this.#m?.unref?.()}async stop(){this.#D=!0,this.#y+=1,this.#p?.abort(),this.#p=null,this.#m!==null&&clearTimeout(this.#m),this.#m=null,this.#_();let r=this.#h,o=this.#c;this.#h=null,this.#c=null,this.#g=null;try{r&&r.readyState<2&&r.close(1e3,"Plugin stopped")}catch(i){this.#o.warn?.(`[dsh-im:discord] bot ${this.#e.botId} failed to close Gateway:`,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}};import{mkdir as lle,readFile as ple,rename as Ele,unlink as dle,writeFile as hle}from"node:fs/promises";import{dirname as Cle}from"node:path";var $B=Object.freeze({version:1,sessions:{},seenMessageIds:[],cursor:null});function ule(n){if(!n||typeof n!="object")return structuredClone($B);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"&&o&&typeof i=="string"&&i&&(r[o]=i);return{version:1,sessions:r,seenMessageIds:Array.isArray(n.seenMessageIds)?n.seenMessageIds.filter(o=>typeof o=="string"&&o).slice(-1e3):[],cursor:Number.isSafeInteger(n.cursor)&&n.cursor>=0?n.cursor:null}}var Cn=class{#e;#A=structuredClone($B);#r=Promise.resolve();constructor(r){this.#e=r}async load(){try{this.#A=ule(JSON.parse(await ple(this.#e,"utf8")))}catch(r){if(r?.code!=="ENOENT")throw r;this.#A=structuredClone($B),await this.#t()}return this}sessionFor(r){return this.#A.sessions[r]??null}async setSession(r,o){this.#A.sessions[r]=o,await this.#t()}async clearSession(r){delete this.#A.sessions[r],await this.#t()}async clearSessions(){this.#A.sessions={},await this.#t()}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.#t())}cursor(){return this.#A.cursor}async setCursor(r){if(!Number.isSafeInteger(r)||r<0)throw new TypeError("Invalid update cursor");this.#A.cursor=r,await this.#t()}snapshot(){return structuredClone(this.#A)}async remove(){try{await dle(this.#e)}catch(r){if(r?.code!=="ENOENT")throw r}this.#A=structuredClone($B)}async#t(){let r=`${JSON.stringify(this.#A,null,2)} `,o=this.#r.then(async()=>{await lle(Cle(this.#e),{recursive:!0,mode:448});let i=`${this.#e}.tmp`;await hle(i,r,{encoding:"utf8",mode:384}),await Ele(i,this.#e)});this.#r=o.then(()=>{},()=>{}),await o}};var ef=class extends Cn{};import{unlink as fle}from"node:fs/promises";import{homedir as Qle}from"node:os";import{join as kd,resolve as kc}from"node:path";var aK=Object.freeze([250,1e3,3e3,5e3,1e4,3e4]);function Ble(n){if(!Array.isArray(n)||n.length===0)return[...aK];let r=n.filter(o=>Number.isFinite(o)&&o>=0);return r.length>0?r:[...aK]}var Pb=class{#e;#A;#r;#t;#o;#i;#I;#s;#l=null;#a=null;#n=0;#g=!1;#c=!1;#p;#h;constructor({channel:r,controller:o,harness:i,logger:e=console,retryDelaysMs:a,healthyIntervalMs:A=15e3,setTimeoutImpl:t=setTimeout,clearTimeoutImpl:s=clearTimeout}){if(!r)throw new TypeError("TokenConnectionSupervisor requires a channel");if(!o||typeof o.initialize!="function"||typeof o.status!="function")throw new TypeError("TokenConnectionSupervisor requires a controller");if(!i||typeof i.ensureRunning!="function")throw new TypeError("TokenConnectionSupervisor requires a Harness client");this.#e=r,this.#A=o,this.#r=i,this.#t=e,this.#o=Ble(a),this.#i=Number.isFinite(A)&&A>=0?A:15e3,this.#I=t,this.#s=s,this.#p=new Promise(g=>{this.#h=g})}get ready(){return this.#p}start(){return this.#c||this.#g?this:(this.#c=!0,this.#d(0),this)}async close(){this.#g||(this.#g=!0,this.#l!==null&&this.#s(this.#l),this.#l=null,await this.#a?.catch(()=>{}),this.#h?.(null),this.#h=null)}#d(r){this.#g||(this.#l=this.#I(()=>{this.#l=null,this.#E()},r),this.#l?.unref?.())}async#E(){if(this.#g||this.#a)return;let r=this.#C();this.#a=r;try{await r}finally{this.#a===r&&(this.#a=null)}}async#C(){try{if(await this.#r.ensureRunning(),this.#g)return;let r=await this.#A.initialize();if(this.#g)return;this.#h?.(r),this.#h=null;let{configured:o,connected:i}=r.totals;if(i65535)throw new Error("dsh-im token channel requires an initialized DSH webServer port");return new URL(`http://127.0.0.1:${o}`)}function Gb(n,r){let o=kc(n.dshHome??process.env.DSH_HOME??kd(Qle(),".dsh")),i=kc(n.dataDir??kd(o,"integrations",`dsh-${r}`));return{config:kc(n.configPath??kd(i,"config.json")),bots:kc(n.botsDir??kd(i,"bots")),workspaces:kc(n.workspacesPath??kd(i,"workspaces.json"))}}async function Af(n,r,o,i){let{channel:e,ConfigStore:a,StateStore:A,HarnessClient:t,Controller:s,Runtime:g,runtimeOptions:c}=i;if(!n?.credentials)throw new TypeError(`dsh-im ${e} requires ctx.credentials`);if(!n?.webServer)throw new TypeError(`dsh-im ${e} requires ctx.webServer`);let I=o.ConfigStore??a,E=o.StateStore??A,B=o.HarnessClient??t,f=o.Controller??s,Q=o.Runtime??g,y=typeof c=="function"?c(r):{};if(!y||typeof y!="object"||Array.isArray(y))throw new TypeError(`dsh-im ${e} runtimeOptions must return an object`);let m=o.createConnectionSupervisor??ml,_=typeof n.logger=="function"?n.logger(`dsh-im:${e}`):n.logger??console,N=()=>ha(n),G=Gb(r,e),Y=await new I(G.config).load(),j=kc(r.workspace??process.cwd()),K=o.WorkspaceStore??Kr,x=o.workspaces??await new K(G.workspaces,{defaultWorkspace:j}).load(),J=Y.list();await x.reconcile(J.map(ue=>ue.botId)),await Promise.all(J.map(ue=>x.ensure(ue.botId,{defaultAgentPreset:r.agentPreset})));let F=typeof Y.remove=="function"?Ca(Y,{workspaces:x}):Y,k=new Map,z=ue=>kc(G.bots,ue,"state.json"),H=async ue=>{let ve=k.get(ue);return ve||(ve=await new E(z(ue)).load(),k.set(ue,ve)),ve},M=fa(n,o.commandExecutor),{controlExecutor:v,sessionMaintenanceExecutor:W,fileIngressExecutor:se}=Qa(n,{controlExecutor:o.controlExecutor,sessionMaintenanceExecutor:o.sessionMaintenanceExecutor,fileIngressExecutor:o.fileIngressExecutor}),Qe=new B({baseUrl:vd(n.webServer,r.harnessBaseUrl),workspace:j,autostart:!1,dshBin:r.dshBin??"dsh",...M?{commandExecutor:M}:{},...v?{controlExecutor:v}:{},...W?{sessionMaintenanceExecutor:W}:{},...se?{fileIngressExecutor:se}:{}}),be=new f({credentials:n.credentials,configStore:F,logger:_,...o.inspectToken?{inspectToken:o.inspectToken}:{},createRuntime:async({botId:ue,config:ve,token:ze})=>{let tA=await H(ue);await x.ensure(ue,{defaultAgentPreset:r.agentPreset});let Ge=ua(Qe,{botId:ue,workspaces:x,state:tA,agentPresetCatalog:N});return new Q({...y,config:ve,token:ze,harness:Ge.harness,state:Ge.state,replyTimeoutMs:r.replyTimeoutMs??6e5,connectTimeoutMs:r.connectTimeoutMs??2e4,logger:{error:(...aA)=>_.error?.(`[${ue}]`,...aA),warn:(...aA)=>_.warn?.(`[${ue}]`,...aA),info:(...aA)=>_.info?.(`[${ue}]`,...aA),debug:(...aA)=>_.debug?.(`[${ue}]`,...aA)}})},deleteState:async({botId:ue})=>{let ve=k.get(ue);if(k.delete(ue),ve&&typeof ve.remove=="function")await ve.remove();else try{await fle(z(ue))}catch(ze){if(ze?.code!=="ENOENT")throw ze}}}),Be=Ba(be,{workspaces:x,stateFor:H,agentPresetCatalog:N}),De=m({channel:e,controller:Be,harness:Qe,logger:_,retryDelaysMs:r.retryDelaysMs,healthyIntervalMs:r.healthyIntervalMs}).start();return{controller:Be,ready:De.ready,async close(){await De.close(),await Be.close(),Qe.stopManagedProcess()}}}function Fb(n,r={},o={}){return Af(n,r,o,{channel:"discord",ConfigStore:VB,StateStore:ef,HarnessClient:ZB,Controller:Td,Runtime:Rd})}var Da=Object.freeze({status:"connection.status",bindCredentials:"bot.bind-credentials",reconnectBot:"bot.reconnect",deleteBot:"bot.delete",setWorkspace:ei,setAgentPreset:Ai}),yle=Object.freeze(Object.values(Da)),mle=new Set(["token","botToken","tokenRef","platformId","secret","secretRef"]);function Ub(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}function tf(n,r){return Ub(n)&&Object.keys(n).every(o=>r.includes(o))}function iK(n){return typeof n=="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(n)}function Sle(n){return typeof n=="string"&&n.trim().length>=20&&n.length<=4096}function Dle(n,r){return Ub(r)?n===Da.status?tf(r,[])?null:"connection.status does not accept fields.":n===Da.bindCredentials?tf(r,["token"])&&Sle(r.token)?null:"bot.bind-credentials requires a Bot Token.":n===Da.reconnectBot?tf(r,["botId","sendTest"])&&iK(r.botId)&&(r.sendTest===void 0||typeof r.sendTest=="boolean")?null:"bot.reconnect requires a botId.":n===Da.deleteBot?tf(r,["botId","confirm"])&&iK(r.botId)&&r.confirm===!0?null:"bot.delete requires a botId and confirm=true.":n===Da.setWorkspace?ya(r)?null:"\u8BF7\u8F93\u5165\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84\u3002":n===Da.setAgentPreset?Sa(r)?null:"\u8BF7\u9009\u62E9 Agent Preset\u3002":"Unknown bot endpoint.":"Payload must be an object."}function Lb(n){if(Array.isArray(n))return n.map(Lb);if(!Ub(n))return n;let r={};for(let[o,i]of Object.entries(n))mle.has(o)||(r[o]=Lb(i));return r}function wle(n,r){let o=ma(r);return o||(r?.code==="webhook-configured"?{code:"webhook-configured",message:r.message}:r?.code==="telegram-401"||r?.code==="discord-401"?{code:"invalid-token",message:`${n} Bot Token \u65E0\u6548\uFF0C\u8BF7\u91CD\u65B0\u586B\u5199\u3002`}:r?.code==="discord-intents"?{code:"discord-intents",message:r.message}:{code:`${n.toLowerCase()}-operation-failed`,message:`${n} \u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`})}function rf(n,{channel:r}){for(let o of["status","bindCredentials","reconnectBot","deleteBot"])if(typeof n?.[o]!="function")throw new TypeError(`A complete ${r} controller is required (${o})`);return async(o,i,e)=>{if(e?.aborted)return{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}};if(!yle.includes(o))return{ok:!1,error:{code:"bad-request",message:`Unknown ${r} endpoint.`}};let a=Dle(o,i);if(a)return{ok:!1,error:{code:"bad-request",message:a}};try{let A;if(o===Da.status)A=await n.status();else if(o===Da.bindCredentials)A=await n.bindCredentials(i);else if(o===Da.reconnectBot){if(A=await n.reconnectBot(i.botId),e?.aborted)return{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}};if(i.sendTest===!0){let t=null;try{if(A?.bots?.find(s=>s?.botId===i.botId)?.connected!==!0){let s=new Error("Bot is not connected");throw s.code="test-target-unavailable",s}if(typeof n.sendConnectionTest!="function"){let s=new Error("Connection test is unavailable");throw s.code="test-target-unavailable",s}await n.sendConnectionTest(i.botId)}catch(s){t=s}A={...A,testMessage:Wt(t)}}}else if(o===Da.setWorkspace){if(typeof n.updateWorkspace!="function")throw new Error("Workspace update is unavailable");A=await n.updateWorkspace(i.botId,i.workspace)}else if(o===Da.setAgentPreset){if(typeof n.updateAgentPreset!="function")throw new Error("Agent preset update is unavailable");A=await n.updateAgentPreset(i.botId,i.agentPreset)}else A=await n.deleteBot(i.botId);return e?.aborted?{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}:{ok:!0,value:Lb(A)}}catch(A){return e?.aborted?{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}:{ok:!1,error:wle(r,A)}}}}function oK(n,r,{channel:o,rpcChannel:i,authority:e}){if(!n?.connection?.rpc||typeof n.connection.rpc.handle!="function")throw new TypeError("DSH Host Connection RPC is required");return n.connection.rpc.handle(i,rf(r,{channel:o}),{authority:Cr(e)})}var nK="/discord",sK=Da,Ole=Object.freeze(Object.values(sK));function af(n,r,o){return oK(n,r,{channel:"Discord",rpcChannel:nK,authority:o})}async function gK(n,r={}){if(r?.controller)return af(n,r.controller,r.rpcAuthority);let o=await Fb(n,r,r.internals??{}),i=af(n,o.controller,r.rpcAuthority);return n.effect(()=>async()=>o.close(),"dsh-im: close Discord bot connections"),i}import{homedir as Vle}from"node:os";import{join as Yb,resolve as jb}from"node:path";import{mkdir as ble,readFile as Mle,rename as _le,unlink as Nle,writeFile as Tle}from"node:fs/promises";import{dirname as Rle,isAbsolute as kle}from"node:path";var Sl="office-harness.v1",cK="/office",Dl=Object.freeze({status:"connection.status",configure:"connector.configure",reconnect:"connector.reconnect",test:"connector.test",remove:"connector.remove"}),Po=Object.freeze({stream:"/api/harness/connector/stream",heartbeat:"/api/harness/connector/heartbeat",job:"/api/harness/connector/jobs/:id",accept:"/api/harness/connector/jobs/:id/accept",renew:"/api/harness/connector/jobs/:id/renew",progress:"/api/harness/connector/jobs/:id/progress",approval:"/api/harness/connector/jobs/:id/approval",result:"/api/harness/connector/jobs/:id/result",fail:"/api/harness/connector/jobs/:id/fail"});function Pd(n){let r=new URL(typeof n=="string"?n.trim():""),o=r.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(r.hostname);if(r.protocol!=="https:"&&!o)throw new TypeError("AI Office URL must use HTTPS (HTTP is allowed only for loopback testing)");if(r.username||r.password||r.search||r.hash)throw new TypeError("AI Office URL must be a bare origin");return r.pathname="/",r}function of(n){let r=Pd(n);return Object.fromEntries(Object.entries(Po).map(([o,i])=>[o,new URL(i,r).toString()]))}var vle=/^[a-z][a-z0-9-]{1,63}$/,Ple=/^[a-z0-9][a-z0-9_-]{2,63}$/i;function wl(n){return typeof n=="string"&&n.trim()?n.trim():null}function IK(n,{kind:r}){if(!n||typeof n!="object"||Array.isArray(n))return null;let o={};for(let[i,e]of Object.entries(n)){let a=wl(i),A=wl(e);if(!a||!vle.test(a)||!A||r==="workspace"&&!kle(A)||r==="preset"&&A.length>8e3)return null;o[a]=A}return Object.freeze(o)}function nf(n){if(!n||typeof n!="object"||Array.isArray(n))return null;let r;try{r=Pd(n.baseUrl).origin}catch{return null}let o=wl(n.deviceId),i=wl(n.deviceTokenRef),e=IK(n.workspaces??{},{kind:"workspace"}),a=IK(n.instructionPresets??{},{kind:"preset"}),A=Number(n.maxConcurrency??1),t=Number(n.heartbeatSeconds??30);return!o||!Ple.test(o)||!i||!/^DSH_OFFICE_DEVICE_TOKEN_[A-F0-9]{24}$/.test(i)||!e||!a||!Number.isInteger(A)||A<1||A>4||!Number.isInteger(t)||t<10||t>300?null:Object.freeze({version:1,protocolVersion:Sl,baseUrl:r,deviceId:o,deviceTokenRef:i,maxConcurrency:A,heartbeatSeconds:t,workspaces:e,instructionPresets:a,createdAt:wl(n.createdAt)??new Date().toISOString(),updatedAt:wl(n.updatedAt)??new Date().toISOString()})}var sf=class{#e;#A=null;#r=Promise.resolve();constructor(r){this.#e=r}async load(){try{let r=nf(JSON.parse(await Mle(this.#e,"utf8")));if(!r)throw new Error("dsh-im AI Office config is invalid");this.#A=r}catch(r){if(r?.code!=="ENOENT")throw r;this.#A=null}return this}get(){return this.#A?structuredClone(this.#A):null}async save(r){let o=nf(r);if(!o)throw new Error("Refusing to persist invalid AI Office configuration");let i=this.#r.then(async()=>{await ble(Rle(this.#e),{recursive:!0,mode:448});let e=`${this.#e}.tmp`;await Tle(e,`${JSON.stringify(o,null,2)} `,{mode:384}),await _le(e,this.#e),this.#A=o});return this.#r=i.then(()=>{},()=>{}),await i,this.get()}async clear(){let r=this.#r.then(async()=>{try{await Nle(this.#e)}catch(o){if(o?.code!=="ENOENT")throw o}this.#A=null});this.#r=r.then(()=>{},()=>{}),await r}};import{createHash as qle}from"node:crypto";import{setTimeout as jle}from"node:timers/promises";function Hb(n,r){let o=new Error(`AI Office ${n} failed: HTTP ${r.status}`);return o.code=r.status===401?"invalid-device-token":r.status===404?"office-hook-unavailable":r.status===409?"office-job-conflict":"office-transport-failed",o}function Ol(n,r="office-transport-failed",o){let i=new Error(n,o===void 0?void 0:{cause:o});return i.code=r,i}function Kb(n,r){return r?.aborted||n?.name==="AbortError"}function Gle(n){let r="message",o,i=[];for(let a of n.split(/\r?\n/))a.startsWith("event:")?r=a.slice(6).trim()||"message":a.startsWith("id:")?o=a.slice(3).trim()||void 0:a.startsWith("data:")&&i.push(a.slice(5).trimStart());if(i.length===0)return null;let e;try{e=JSON.parse(i.join(` diff --git a/src/channels/shared/text-harness-bridge.mjs b/src/channels/shared/text-harness-bridge.mjs index 74e505c..418d57e 100644 --- a/src/channels/shared/text-harness-bridge.mjs +++ b/src/channels/shared/text-harness-bridge.mjs @@ -558,10 +558,18 @@ export class TextHarnessBridge { textDeliveryError = error; } } + if (textReceipt?.deliveryOutcome === 'failed') { + const reason = textReceipt.reason ?? 'text-delivery-failed'; + textDeliveryError = new Error(`Final text delivery failed (${reason})`); + textDeliveryError.code = reason; + } // A failed final text must not discard an already registered result file. // Settle the independent attachment path before surfacing the text error. const delivery = await this.#deliverArtifacts(target, messageId, artifacts, textReceipt); - if (textDeliveryError && !delivery.userVisible) throw textDeliveryError; + if (textDeliveryError && !delivery.userVisible) { + textDeliveryError.deliveryReceipt = delivery.receipt; + throw textDeliveryError; + } if (delivery.userVisible) { this.#status.messagesReplied += 1; this.#status.lastReplyAt = new Date().toISOString(); @@ -626,7 +634,9 @@ export class TextHarnessBridge { return; } this.#logger.error?.(`[dsh-im:${this.#descriptor.key}] failed to process a message:`, error); - if (await presentStreamFailure('消息处理失败,请稍后重试。')) return; + if (await presentStreamFailure('消息处理失败,请稍后重试。')) { + return error.deliveryReceipt; + } stream?.cancel?.(); try { await this.#bot.sendText(target, '消息处理失败,请稍后重试。'); @@ -636,6 +646,7 @@ export class TextHarnessBridge { sendError, ); } + return error.deliveryReceipt; } finally { await Promise.allSettled([ this.#cancelPendingInteraction(conversationKey), diff --git a/test/channels/telegram/rich-message.test.mjs b/test/channels/telegram/rich-message.test.mjs index 4a0336e..3d5dff6 100644 --- a/test/channels/telegram/rich-message.test.mjs +++ b/test/channels/telegram/rich-message.test.mjs @@ -510,6 +510,131 @@ test('Telegram uncertain final delivery records unknown and never sends a fallba assert.deepEqual(groupResult.providerMessageIds, ['1202']); }); +test('shared bridge surfaces a definite final delivery failure without rerunning Prompt or artifacts', async (t) => { + for (const safeReplyFails of [false, true]) { + await t.test(safeReplyFails ? 'safe reply also fails' : 'safe reply succeeds', async () => { + const safeReplies = []; + let prompts = 0; + const stream = { + providerMessageIds: ['failed-placeholder'], + presentation: 'telegram-regular', + async update() {}, + async finish() { + return { + presentation: 'telegram-rich-final', + providerMessageIds: ['failed-placeholder'], + deliveryOutcome: 'failed', + reason: 'telegram-403', + }; + }, + async fail() { + return undefined; + }, + cancel() {}, + }; + const bridge = new TelegramHarnessBridge({ + bot: { + sendTyping: async () => {}, + openDeliveryStream: async () => stream, + sendText: async (_target, text) => { + safeReplies.push(text); + if (safeReplyFails) throw rejected(403); + return { message_id: 1261 }; + }, + }, + state: memoryState(), + logger: { warn() {}, error() {} }, + harness: { + createSession: async () => 'session-rich-artifact', + ask: async () => { + prompts += 1; + return '## final'; + }, + }, + }); + + const receipt = await bridge.accept({ + messageId: `definite-failure-${safeReplyFails}`, + senderId: 'rich-user', + kind: 'direct', + conversationId: `definite-failure-${safeReplyFails}`, + content: 'return a file', + addressed: true, + replyTarget: { chatId: 42, chatType: 'private', replyToMessageId: 44 }, + }); + + assert.equal(prompts, 1); + assert.deepEqual(safeReplies, ['消息处理失败,请稍后重试。']); + assert.equal(receipt.deliveryOutcome, 'failed'); + assert.equal(receipt.reason, 'telegram-403'); + assert.deepEqual(receipt.artifacts, []); + assert.match(bridge.status.lastError, /telegram-403/); + }); + } +}); + +test('shared bridge settles each artifact once before containing a definite text failure', async (t) => { + const artifact = await committedArtifact(t); + const sentFiles = []; + let prompts = 0; + let safeReplies = 0; + const bridge = new TelegramHarnessBridge({ + bot: { + sendTyping: async () => {}, + openDeliveryStream: async () => ({ + providerMessageIds: ['failed-placeholder'], + presentation: 'telegram-regular', + async update() {}, + async finish() { + return { + presentation: 'telegram-rich-final', + providerMessageIds: ['failed-placeholder'], + deliveryOutcome: 'failed', + reason: 'telegram-403', + }; + }, + cancel() {}, + }), + sendText: async () => { + safeReplies += 1; + return { message_id: 1261 }; + }, + sendFile: async (_target, file) => { + sentFiles.push(file.fileName); + return { message_id: 1262 }; + }, + }, + state: memoryState(), + logger: { warn() {}, error() {} }, + harness: { + createSession: async () => 'session-rich-artifact', + ask: async (_sessionId, _text, options) => { + prompts += 1; + await options.onArtifact(artifact); + return '## final with file'; + }, + }, + }); + + const receipt = await bridge.accept({ + messageId: 'definite-failure-with-file', + senderId: 'rich-user', + kind: 'direct', + conversationId: 'definite-failure-with-file', + content: 'return a file', + addressed: true, + replyTarget: { chatId: 42, chatType: 'private', replyToMessageId: 44 }, + }); + + assert.equal(prompts, 1); + assert.equal(safeReplies, 0); + assert.deepEqual(sentFiles, ['result.txt']); + assert.equal(receipt.deliveryOutcome, 'failed'); + assert.deepEqual(receipt.artifacts, [{ + artifactId: 'telegram-rich-artifact', outcome: 'sent', + }]); +}); + test('Telegram distinguishes pre-dispatch abort from an uncertain in-flight abort', async () => { const before = new AbortController(); const beforeReason = new DOMException('stopped before dispatch', 'AbortError');