diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ea9ddf..b2e96d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,12 @@ This file records the notable changes in each dsh-im release. Its format follows - 非视觉模型收到图片时不再直接报错丢图:宿主以 `MODEL_DOES_NOT_SUPPORT_IMAGES` 拒绝带图片的 prompt 后,自动把同一批图片字节按入站文件管线落盘到 Session 工作区,并以"原文本 + 工具分析指引 + `` 清单"的纯文本 prompt 复用同一 rpcId 重试一次,使非视觉模型仍可通过 run_code/pwsh 等工具识图;视觉模型与文件消息行为不变,其余图片错误仍按原样提示。 Sending an image to a non-vision model no longer fails outright: when the Host rejects an image-bearing prompt with `MODEL_DOES_NOT_SUPPORT_IMAGES`, the same image bytes are automatically staged into the Session workspace through the inbound-file pipeline and retried once as a text-only prompt (original text plus tool-analysis guidance and the `` manifest) under the same rpcId, so non-vision models can still inspect images via tools such as run_code/pwsh. Vision models and file messages are unchanged, and other image errors keep their existing messages. + +### Fixed / 修复 + +- 非视觉模型图片回退在文件落盘阶段收到取消信号时,现在会保留调用方的取消原因并停止处理,不再误报模型不支持图片。 + When image fallback for a non-vision model is cancelled while staging files, it now preserves the caller's cancellation reason and stops instead of reporting that the model does not support images. + ## [4.5.0] - 2026-09-01 ### Added / 新增 diff --git a/docs/方案/入站图片非视觉模型文件回退方案.md b/docs/方案/入站图片非视觉模型文件回退方案.md index 844314d..93e04b0 100644 --- a/docs/方案/入站图片非视觉模型文件回退方案.md +++ b/docs/方案/入站图片非视觉模型文件回退方案.md @@ -1,8 +1,8 @@ # 入站图片非视觉模型文件回退方案 -> 状态:已实施(P0);Windows 本机全量回归通过,真机验收待各渠道补做 +> 状态:已实施(P0);Ubuntu CI、包产物验证与真实宿主非视觉模型端到端验收通过 > -> 日期:2026-06-14 +> 日期:2026-09-01 > > 关联:[出站图片原生呈现落地方案](./出站图片原生呈现落地方案.md)、[渠道原生能力建设方案](./渠道原生能力建设方案.md) @@ -21,14 +21,15 @@ IM 对话中,非视觉模型收到图片时当前是**准入即硬失败**: 与出站方案同样的原则:复用现有 `inbound-file` 生命周期(落盘、清单、turn 结束清理),不新增配置开关、不新增产物类型。 -### 1.1 实施结果(2026-06-14) +### 1.1 实施结果(2026-09-01) 本方案已按上述最小边界落地: - `src/channels/shared/image-prompt.mjs` 新增 `IMAGE_FILE_FALLBACK_PROMPT`(模型侧指引,含英文翻译)、`imageFileSourcesFromContent()`(图片内容块 → 文件源,含扩展名映射与文件名清洗)、`contentWithoutImages()`、`isModelImageRejection()`。 - `src/channels/shared/harness-client.mjs` 的 `ask()` 将原入站文件落盘逻辑抽为 `#stageWorkspaceFiles()`;`session.prompt` 被以 `MODEL_DOES_NOT_SUPPORT_IMAGES` 拒绝且 content 含图片块时,把同一批字节经该管线落盘、重建纯文本 prompt(原文本 + 指引 + 合并后的单个 `` 清单)并复用同一 `promptRpcId` 重试一次;重试不再回退,落盘失败时保留原始错误文案。staged 批次统一进入既有 turn 结束清理。 -- `test/image-fallback.test.mjs` 新增 8 个用例:转换/判定辅助函数、完整回退(复用 rpcId、字节一致、清单正确、turn 后清理)、混合消息合并清单、非模型原因不回退、落盘失败保留原错误、重试失败不再重试、纯文本路径不变。 +- `test/image-fallback.test.mjs` 新增 9 个用例:转换/判定辅助函数、完整回退(复用 rpcId、字节一致、清单正确、turn 后清理)、混合消息合并清单、非模型原因不回退、落盘失败保留原错误、落盘期间保留调用方取消语义、重试失败不再重试、纯文本路径不变。 - 回归:Windows 本机以逐文件直跑方式执行全部 137 个测试文件,失败集与改动前基线(git worktree 对照)完全一致(23 个均为仓库既有的 Windows/沙箱环境性失败,CI 在 ubuntu 上通过);`npm run build` 通过;`verify-package` 的可执行位检查在 Windows 上为既有环境性失败。 +- 端到端:在真实 DSH 宿主进程内通过生产 `apiProxy` 与 `fileIngressExecutor` 链路向文本模型注入入站 PNG,验证了宿主拒绝、图片落盘、纯文本重试与模型工具分析的完整路径。 ## 2. 现状与根因 @@ -176,14 +177,15 @@ export function imageFileSourcesFromContent(content) { 1. 首次 `session.prompt` 返回 `attachment-error/MODEL_DOES_NOT_SUPPORT_IMAGES` → 断言:调用了第二次 prompt;第二次 content 无 image part;含 `` 清单与回退提示文本;文件以正确扩展名写入 executor 收到的 sources;仅重试一次。 2. 混合消息(图片+文件)→ 断言只出现**一个**合并后的 `` 块。 3. 其他拒绝原因(`IMAGE_TOO_LARGE`、`TOO_MANY_IMAGES` 等)→ 不回退,原错误冒泡,用户文案不变。 -4. 重试再失败(如 executor 抛 `inbound-file-ingress-unavailable`)→ 报错且 staged 文件被清理。 -5. 纯文本/纯文件消息 → 不触发任何回退路径(回归)。 -6. turn 正常结束后 staged 图片文件被 cleanup(复用 `test/inbound-file.test.mjs` 的断言模式)。 +4. 落盘失败时保留原始模型拒绝错误;落盘期间取消时保留调用方的取消原因。 +5. 回退 prompt 再次失败 → 仅报错一次,不继续重试。 +6. 纯文本/纯文件消息 → 不触发任何回退路径(回归)。 +7. turn 正常结束后 staged 图片文件被 cleanup(复用 `test/inbound-file.test.mjs` 的断言模式)。 ### 6.2 回归与手工验收 - `npm run check` 全量通过。 -- 真机:绑定非视觉模型的会话发一张图 → 收到 Agent 基于工具分析的回复而非报错;视觉模型会话发图 → 行为与现状一致(原生多模态);发 zip → 行为不变。 +- 真实宿主端到端:文本模型收到图片后走完“模态拒绝 → 工作区文件落盘 → 纯文本重试 → 工具分析”链路并正常回复;视觉模型与普通文件消息的原有行为保持不变。 ## 7. 上游反馈(非本仓库范围) diff --git a/lib/index.js b/lib/index.js index 7f85540..5a6022b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -226,7 +226,7 @@ ${o}`:o}function vo(n){return n instanceof ko?n.userMessage:null}import{createHa ${x}`:q;m&&await y.finish(z)||await this.#L(g,z,this.#G(t))}catch{this.#n.error?.("[dsh-dingtalk] failed to send the safe error reply")}}finally{await this.#j(e),await this.#f.closeRoute(e)}}async#M(t,o,i,e,a,A){this.#g?.throwIfAborted();let r=this.#d.get(e),s=a.claimedReplyMessageId===o;if(!r||r!==a||r.submitting)return s&&(!r||r!==a)?this.#k(t,o):this.#D(t,o,i,e,{releaseMessageId:!1,statusReaction:A});if(this.#l.hasSeen(o))return;if(await this.#l.markSeen(o),Kr(this.#a,"messagesReceived"),this.#a.lastMessageAt=new Date().toISOString(),String(t.conversationType)==="2"&&t.isInAtList!==!0){Kr(this.#a,"messagesIgnored");return}let g;try{g=lc(t.sessionWebhook)}catch{Kr(this.#a,"messagesRejected"),this.#a.lastRejectedAt=new Date().toISOString(),this.#a.lastError=w("\u9489\u9489\u6D88\u606F\u6CA1\u6709\u5B89\u5168\u7684\u56DE\u590D\u5730\u5740\u3002");return}let c=t?.msgtype==="text"?ZA(t?.text?.content):null;if(!c){try{await this.#L(g,w("\u8BF7\u7528\u6587\u5B57\u56DE\u7B54\u5F53\u524D\u95EE\u9898\u3002"),this.#G(t))}catch{this.#n.error?.("[dsh-dingtalk] failed to reject a non-text interaction reply")}return}let I=this.#d.get(e);if(!I||I!==a||I.submitting){if(s&&(!I||I!==a)){try{await this.#L(g,w(V_),this.#G(t))}catch{this.#n.error?.("[dsh-dingtalk] failed to send an expired interaction notice")}return}return this.#D(t,o,i,e,{releaseMessageId:!1,alreadyRecorded:!0,statusReaction:A})}if(I.sessionWebhook=g,I.needsPresentation){try{await this.#F(I)}catch{this.#m(A,"error"),this.#a.lastError=w("\u9489\u9489\u4EA4\u4E92\u95EE\u9898\u53D1\u9001\u5931\u8D25\u3002"),this.#n.error?.("[dsh-dingtalk] failed to retry an interaction question"),I.interaction.reconnect?.()}return}let p=I.questions[I.index];if(p){if(I.answers.push(bi(p,c)),I.index+=1,I.indexthis.#L(e,c)})||t?.kind!=="question")return;let A=t?.payload?.questions,r=typeof t?.interactionId=="string"?t.interactionId:t?.rpcId;if(typeof t.rpcId!="string"||typeof r!="string"||typeof t.sessionId!="string"||!Array.isArray(A)||A.length===0||A.some(c=>!Di(c))){this.#n.warn?.("[dsh-dingtalk] ignored an invalid Harness question interaction");return}if(t.recovered===!0){await t.respond({ok:!1,error:{code:"cancelled",message:"DingTalk safely cancelled an interaction left by an earlier client.",details:{}}});try{await this.#L(e,w("\u68C0\u6D4B\u5230\u8FD9\u4E2A Session \u4E2D\u9057\u7559\u7684\u5F85\u56DE\u7B54\u95EE\u9898\uFF0C\u5DF2\u5B89\u5168\u53D6\u6D88\u5E76\u7EE7\u7EED\u5904\u7406\u4F60\u521A\u624D\u7684\u6D88\u606F\u3002"))}catch{this.#n.error?.("[dsh-dingtalk] failed to send an interaction recovery notice")}return}let s=this.#d.get(o);if(s?.interactionId===r){s.interaction=t,s.needsPresentation&&await this.#F(s);return}if(this.#h.has(r))return;if(s){this.#n.warn?.("[dsh-dingtalk] cancelled a second pending Harness question"),await t.respond({ok:!1,error:{code:"cancelled",message:"DingTalk is already handling another user interaction.",details:{}}});return}let g={kind:"question",interactionId:r,sessionId:t.sessionId,interaction:t,actor:i,requiresMention:a,questions:A,answers:[],index:0,sessionWebhook:e,queue:null,claimedReplyMessageId:null,submitting:!1,needsPresentation:!0};this.#d.set(o,g),this.#h.set(g.interactionId,o),await this.#F(g)}async#N(t){if(await this.#f.handleResolved(t))return;let o=t?.interactionId;if(t?.kind!=="question"||typeof o!="string")return;let i=this.#h.get(o);i&&this.#U(i,o)}async#F(t){let o=t.questions[t.index];o&&(await this.#L(t.sessionWebhook,Oi(o,t.index,t.questions.length,{requiresMention:t.requiresMention})),t.needsPresentation=!1)}async#k(t,o){if(this.#l.hasSeen(o))return;await this.#l.markSeen(o),Kr(this.#a,"messagesReceived"),this.#a.lastMessageAt=new Date().toISOString();let i;try{i=lc(t.sessionWebhook)}catch{Kr(this.#a,"messagesRejected"),this.#a.lastRejectedAt=new Date().toISOString();return}try{await this.#L(i,w(V_),this.#G(t))}catch{this.#n.error?.("[dsh-dingtalk] failed to send an expired interaction notice")}}#K(t,o){let i=this.#d.get(t);return!i||o!==void 0&&i.interactionId!==o?null:(this.#d.delete(t),this.#h.delete(i.interactionId),i)}#U(t,o){return this.#K(t,o)!==null}async#j(t){let o=this.#K(t);if(!(!o||o.kind!=="question"))try{await o.interaction.respond({ok:!1,error:{code:"cancelled",message:"The DingTalk interaction ended before the user answered.",details:{}}},{signal:AbortSignal.timeout(5e3)})}catch(i){i?.code!=="interaction-not-pending"&&this.#n.warn?.("[dsh-dingtalk] failed to cancel a pending Harness interaction")}}#x(){typeof this.#l.pendingSenders=="function"&&(this.#a.pendingSenders=this.#l.pendingSenders())}#G(t){let o=P1(t);return String(t?.conversationType)==="2"&&o?{atUserIds:[o]}:void 0}async#L(t,o,i){let e=[];for(let a of rY(o,this.#c)){this.#g?.throwIfAborted();let A=await this.#e.sendText({clientId:this.#A,clientSecret:this.#t,sessionWebhook:t,text:a,at:i,signal:this.#g});e.push(...Ur(A))}return e}async#z(t,o,i,e,a){let A=async(s,g)=>kue(await this.#e[s]({clientId:this.#A,clientSecret:this.#t,target:t,file:g,signal:this.#g})),r=await Po({artifacts:e,baseReceipt:a,deliveryId:i,aggregatePresentation:a?"dingtalk-text-and-files":"dingtalk-files",channelKey:"dingtalk",signal:this.#g,sendImage:typeof this.#e.sendImage=="function"?s=>A("sendImage",s):void 0,sendFile:typeof this.#e.sendFile=="function"?s=>A("sendFile",s):void 0,onFailure:(s,g)=>tt(this.#a,g,{userMessage:Hue(s?.fileName,g),reason:g?.code}),sendFailureNotice:(s,g,c)=>this.#L(o,Et(c)),logger:this.#n});return this.#a.artifactsSent=(this.#a.artifactsSent??0)+r.artifactsSent,this.#a.artifactSendErrors=(this.#a.artifactSendErrors??0)+r.artifactSendErrors,{receipt:r.receipt,userVisible:r.userVisible,artifactSendErrors:r.artifactSendErrors}}};function jQ(n){return typeof n=="string"&&n.trim()?n.trim():null}function H1(n){let t=Array.isArray(n?.approvedSenders)?n.approvedSenders:n?.approvedSenders instanceof Set?[...n.approvedSenders]:[];return new Set(t.map(o=>jQ(typeof o=="string"?o:o?.staffId)).filter(Boolean))}function jue(n){return H1(n).size}function K1(n){return n?.connected===!0||n?.socket?.readyState===1}function Jue(n,t){return new Promise((o,i)=>{if(t.aborted){i(t.reason);return}let e=()=>i(t.reason);t.addEventListener("abort",e,{once:!0}),Promise.resolve(n).then(a=>{t.removeEventListener("abort",e),o(a)},a=>{t.removeEventListener("abort",e),i(a)})})}async function que(n,t,o){for(;;){if(o?.throwIfAborted(),K1(n))return;await new Promise((i,e)=>{let a=setTimeout(()=>{o?.removeEventListener("abort",A),i()},t),A=()=>{clearTimeout(a),e(o.reason??new DOMException("Aborted","AbortError"))};o?.addEventListener("abort",A,{once:!0})})}}async function zue(n,t,o,i){let e=AbortSignal.timeout(t),a=AbortSignal.any([i,e]),A=!1,r=Promise.resolve().then(()=>n.connect()).finally(()=>{A=!0});try{await Jue(r,a),await que(n,o,a)}catch(s){throw a.aborted?(A||r.then(()=>n.disconnect()).catch(()=>{}),i.aborted?i.reason:new Error(`DingTalk Stream handshake timed out after ${t}ms`)):s}}async function Vue({clientId:n,clientSecret:t}){let{DWClient:o,TOPIC_ROBOT:i}=await import("dingtalk-stream");return{client:new o({clientId:n,clientSecret:t,endpoint:"https://api.dingtalk.com",autoReconnect:!1,keepAlive:!0,debug:!1}),topic:i}}function Wue({pendingSenders:n=[],approvedSenders:t=0}={}){return{startedAt:null,ready:!1,dingtalkStreamState:"idle",harnessReachable:!1,lastConnectedAt:null,lastCheckedAt:null,lastCallbackAt:null,authorizationMode:"sender-staff-id-approval",approvedSenderCount:t,...Z_({pendingSenders:n})}}var XE=class{#e;#A;#t;#r;#l;#o;#i;#a;#n;#s;#p;#c;#g;#I;#d=null;#h=null;#u=null;#C=null;#B=null;#f=null;#Q=new Set;constructor({config:t,clientSecret:o,harness:i,state:e,contextEnhancement:a,accessPolicy:A,logger:r=console,replyTimeoutMs:s=6e5,maxMessageChars:g=4e3,connectTimeoutMs:c=15e3,connectPollIntervalMs:I=25,api:p=aY(),streamFactory:B=Vue}){if(!t||!jQ(t.clientId)||!jQ(o))throw new TypeError("DingtalkRuntime requires app credentials");if(!i||!e)throw new TypeError("DingtalkRuntime requires Harness and state");if(typeof B!="function")throw new TypeError("streamFactory must be a function");this.#e=t,this.#A=o.trim(),this.#t=i,this.#r=e,this.#l=a,this.#o=A,this.#i=r,this.#a=s,this.#n=g,this.#s=c,this.#p=I,this.#c=p,this.#g=B,this.#I=Wue({pendingSenders:this.#y(),approvedSenders:jue(t)})}get status(){if(this.#h){let t=this.#h.status;Object.assign(this.#I,t)}else this.#I.pendingSenders=this.#y();return structuredClone(this.#I)}pendingSender(t){return typeof this.#r.pendingSender=="function"?this.#r.pendingSender(t):null}pendingSenders(){return this.#y()}async start(){return this.#d&&this.#I.ready?this.status:this.#C?this.#C:(this.#C=this.#S().finally(()=>{this.#C=null}),this.#C)}async#S(){await this.stop();let t=new AbortController;this.#f=t;let{signal:o}=t;this.#I.startedAt=new Date().toISOString(),this.#I.dingtalkStreamState="connecting",this.#I.lastError=null;try{if(await this.#t.ensureRunning({signal:o}),this.#I.harnessReachable=!0,typeof this.#r.removePendingSenderByStaffId=="function"){for(let A of H1(this.#e))await this.#r.removePendingSenderByStaffId(A);this.#I.pendingSenders=this.#y()}this.#h=new YQ({api:this.#c,clientId:this.#e.clientId,clientSecret:this.#A,approvedSenders:this.#e.approvedSenders,harness:this.#t,state:this.#r,contextEnhancement:this.#l,accessPolicy:this.#o,status:this.#I,logger:this.#i,replyTimeoutMs:this.#a,maxMessageChars:this.#n,signal:o});let i=await this.#g({clientId:this.#e.clientId,clientSecret:this.#A});if(o.throwIfAborted(),this.#d=i?.client??i,this.#u=i?.topic??i?.TOPIC_ROBOT??"/v1.0/im/bot/messages/get",!this.#d||typeof this.#d.registerCallbackListener!="function"||typeof this.#d.connect!="function"||typeof this.#d.disconnect!="function"||typeof this.#d.socketCallBackResponse!="function")throw new TypeError("streamFactory returned an invalid DingTalk Stream client");let e=this.#d,a=this.#h;return e.registerCallbackListener(this.#u,A=>{if(this.#d!==e||this.#h!==a)return;let r=jQ(A?.headers?.messageId);if(r)try{e.socketCallBackResponse(r,{success:!0})}catch{this.#i.warn?.("[dsh-dingtalk] unable to acknowledge an inbound callback")}let s=this.#l,g;try{g=s?.getSettings?.()}catch{}let c=Promise.resolve().then(async()=>{if(this.#h!==a)return;let I;try{I=typeof A?.data=="string"?JSON.parse(A.data):A?.data}catch{this.#I.lastError=w("\u9489\u9489\u6D88\u606F\u683C\u5F0F\u65E0\u6548\u3002"),this.#i.warn?.("[dsh-dingtalk] ignored an invalid callback payload");return}if(!I||typeof I!="object")return;this.#I.lastCallbackAt=Date.now();let p=aa({getSettings:()=>g,get botId(){return s?.botId}},I.conversationType==="1"||I.conversationType===1?"direct":I.conversationType==="2"||I.conversationType===2?"group":null);await a.accept(I,{contextSnapshot:p})}).catch(()=>{o.aborted||this.#h!==a||(this.#I.lastError=w("\u9489\u9489\u6D88\u606F\u5904\u7406\u5931\u8D25\u3002"),this.#i.error?.("[dsh-dingtalk] callback processing failed"))}).finally(()=>this.#Q.delete(c));this.#Q.add(c)}),await zue(e,this.#s,this.#p,o),this.#I.ready=!0,this.#I.dingtalkStreamState="connected",this.#I.lastConnectedAt=Date.now(),this.#I.lastCheckedAt=Date.now(),this.#I.lastError=null,this.#B=setInterval(()=>{let A=K1(e);this.#I.ready=A,this.#I.dingtalkStreamState=A?"connected":"reconnecting",this.#I.lastCheckedAt=Date.now(),A&&(this.#I.lastError=null)},1e3),this.#B.unref?.(),this.status}catch(i){let e=o.aborted;throw this.#I.ready=!1,this.#I.dingtalkStreamState=e?"idle":"failed",this.#I.lastError=e?null:i?.message??String(i),await this.stop({preserveError:!e}),i}}async stop({preserveError:t=!1}={}){let o=t?this.#I.lastError:null,i=this.#f;this.#f=null,i?.abort(new DOMException("DingTalk runtime stopped","AbortError")),this.#B&&clearInterval(this.#B),this.#B=null,this.#I.ready=!1;let e=this.#d;if(this.#d=null,this.#u=null,e)try{await e.disconnect()}catch{this.#i.warn?.("[dsh-dingtalk] DingTalk Stream disconnect failed")}return await Promise.allSettled([...this.#Q]),this.#Q.clear(),this.#h&&await this.#h.waitForIdle(),this.#h=null,this.#I.dingtalkStreamState=t?"failed":"idle",this.#I.lastError=o,this.status}async sendConnectionTest(t){return cc({state:this.#r,text:t,channelLabel:w("\u9489\u9489\u673A\u5668\u4EBA"),send:async({sessionWebhook:o},i)=>{if(!this.#I.ready||!this.#f)throw new Error("DingTalk runtime is not connected");await this.#c.sendText({clientId:this.#e.clientId,clientSecret:this.#A,sessionWebhook:o,text:i,signal:this.#f.signal})}})}async sendProactiveText(t,o,{signal:i}={}){let e=typeof t?.route?.userId=="string"?t.route.userId.trim():"",a=typeof t?.route?.openConversationId=="string"?t.route.openConversationId.trim():"";if(t?.kind==="user"&&(!e||a)||t?.kind==="group"&&(!a||e)||t?.kind!=="user"&&t?.kind!=="group"){let A=new TypeError("Invalid DingTalk proactive delivery target");throw A.code="invalid-target",A}if(!this.#I.ready||!this.#f){let A=new Error("DingTalk runtime is not connected");throw A.code="bot-not-connected",A}i?.throwIfAborted();try{await this.#c.sendRobotText({clientId:this.#e.clientId,clientSecret:this.#A,target:{type:t.kind,robotCode:this.#e.clientId,...t.kind==="user"?{userId:e}:{openConversationId:a}},text:o,signal:i??this.#f.signal})}catch(A){if(A?.code!=="send-rejected")throw A;let r=new Error("DingTalk rejected the proactive delivery target",{cause:A});throw r.code="target-rejected",r}return{sent:!0}}#y(){return typeof this.#r.pendingSenders=="function"?this.#r.pendingSenders():[]}};import{spawn as oCe}from"node:child_process";import{randomUUID as X_}from"node:crypto";import{isAbsolute as nCe}from"node:path";import{isAbsolute as Zue}from"node:path";var Xue=256,$ue=/[\p{White_Space}\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u,eCe=/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;function $E(n,t){let o=new Error(t);return o.code=n,o}function ACe(n){if(typeof n!="string"||!n||n.length>Xue||$ue.test(n))throw $E("session-id-invalid","A non-empty, safe session id is required");return n}function tCe(n,t){if(!Array.isArray(t?.items)||!Array.isArray(t?.archivedSessionIds)||t.archivedSessionIds.some(i=>typeof i!="string"||!i))throw new Error("Harness returned an invalid response for workspace.list");let o=[];for(let i of t.items){if(typeof i?.workspaceId!="string"||!i.workspaceId||typeof i.path!="string"||!Zue(i.path)||!Array.isArray(i.sessionIds)||i.sessionIds.some(e=>typeof e!="string"||!e))throw new Error("Harness returned an invalid response for workspace.list");for(let e of i.sessionIds)e===n&&o.push(i)}if(o.length===0)throw $E("session-not-registered","The session is not registered to a Harness workspace");if(o.length!==1)throw $E("session-workspace-ambiguous","The session is registered to more than one Harness workspace");if(eCe.test(o[0].path))throw new Error("Harness returned an unsafe workspace path for the session");return{workspace:o[0],archived:t.archivedSessionIds.includes(n)}}function rCe(n,t){if(!Array.isArray(t?.items)||t.items.some(a=>typeof a?.sessionId!="string"||!a.sessionId))throw new Error("Harness returned an invalid response for session.list");let o=t.items.filter(a=>a.sessionId===n);if(o.length===0)throw $E("session-summary-unavailable","The session is no longer available from Harness");if(o.length!==1)throw new Error("Harness returned duplicate session summaries for session.list");let[i]=o;if(i.origin==="subagent")throw $E("session-subagent-unsupported","Subagent sessions cannot be adopted as a bot conversation");if(i.origin!==void 0)throw new Error("Harness returned an invalid session origin for session.list");let e=i.projections?.values?.title;if(e!=null&&typeof e!="string")throw new Error("Harness returned an invalid session title for session.list");return{title:typeof e=="string"?e:null}}async function x1(n,t,o={},i=3e4){let e=ACe(t);await n.ensureRunning(o);let a=await n.rpc("workspace.list",{},i,o),{workspace:A,archived:r}=tCe(e,a),s=rCe(e,await n.rpc("session.list",{},i,o)),g=await n.rpc("session.create",{workspaceId:A.workspaceId,sessionId:e},i,o);if(!g||g.sessionId!==e)throw new Error("Harness returned an invalid response for session.create");return{sessionId:e,workspace:A.path,title:s.title,archived:r}}function eu(n,t,o){try{let i=n?.(t);i&&typeof i.then=="function"&&Promise.resolve(i).catch(o)}catch(i){o(i)}}async function aCe({apiProxy:n,rpcId:t,signal:o,onOpen:i,onEnvelope:e}){let a=new AbortController,A=()=>a.abort(o.reason),r,s,g,c=I=>{a.signal.aborted||(g=I,A())};o.addEventListener("abort",A,{once:!0});try{for(r=n.events.mux({rpcId:t,payload:{}},a.signal)[Symbol.asyncIterator](),s=Promise.resolve(r.next()),a.signal.aborted||eu(i,A,c);!a.signal.aborted;){let{value:I,done:p}=await s;if(p||a.signal.aborted)break;eu(e,{type:"server-request",rpcId:I?.rpcId,method:I?.payload?.type,payload:I?.payload},c),a.signal.aborted||(s=Promise.resolve(r.next()))}}catch(I){c(I)}finally{let I=a.signal.aborted;o.removeEventListener("abort",A),A(),await s?.catch(()=>{});try{await r?.return?.()}catch(p){I||(g??=p)}}if(g)throw g}function iCe({baseUrl:n,createWebSocket:t,signal:o,onOpen:i,onEnvelope:e,onMalformed:a}){let A=new URL("/api/events.mux",n);return A.protocol=A.protocol==="https:"?"wss:":"ws:",new Promise((r,s)=>{let g=t(A.toString()),c=!1,I=!1,p=_=>{if(!I){I=!0,g.removeEventListener("open",f),g.removeEventListener("message",Q),g.removeEventListener("close",y),g.removeEventListener("error",m),o.removeEventListener("abort",T);try{(g.readyState===0||g.readyState===1)&&g.close()}catch{}_&&!o.aborted?s(_):r()}},B=()=>p(),f=()=>{I||c||(c=!0,eu(i,B,p))},Q=_=>{if(I)return;let R;try{if(typeof _.data!="string")throw new Error("binary WebSocket frame");R=JSON.parse(_.data)}catch(x){eu(a,x,()=>{});return}eu(e,R,p)},y=()=>p(c?void 0:new Error("Harness event mux WebSocket closed before opening")),m=()=>p(new Error(c?"Harness event mux WebSocket failed":"Harness event mux WebSocket failed before opening")),T=()=>p();g.addEventListener("open",f),g.addEventListener("message",Q),g.addEventListener("close",y,{once:!0}),g.addEventListener("error",m,{once:!0}),o.addEventListener("abort",T,{once:!0}),o.aborted&&T()})}async function Y1(n){if(!n.signal.aborted)return n.baseUrl!==void 0&&n.baseUrl!==null?iCe(n):aCe(n)}var sCe=new Map,gCe=new WeakMap,cCe=64;async function ICe(n){let t=n?.body;if(!t||typeof t.getReader!="function")return null;let o=t.getReader(),i=[],e=0;try{for(;;){let{done:r,value:s}=await o.read();if(r)break;if(!(s instanceof Uint8Array)||e+s.byteLength>cCe)return null;i.push(s),e+=s.byteLength}}catch{return null}finally{try{await o.cancel()}catch{}}let a=new Uint8Array(e),A=0;for(let r of i)a.set(r,A),A+=r.byteLength;return new TextDecoder().decode(a)}function lCe(n){if(n==="localhost"||n==="[::1]")return!0;let t=n.split(".");return t.length===4&&t[0]==="127"&&t.every(o=>/^\d{1,3}$/.test(o)&&Number(o)<=255)}async function pCe(n,t){return n.status===401?"harness-auth-required":n.status===407?"harness-proxy-auth-required":n.status===403?(await ICe(n))?.trim()!=="forbidden"?"harness-request-forbidden":lCe(t)?"harness-loopback-forbidden":"harness-host-untrusted":n.status===404?"harness-api-not-found":"harness-http-failed"}function W1(n){let t=typeof n=="string"?sCe:gCe,o=t.get(n);return o||(o={ownerships:new Map,claims:new Map,controls:new WeakMap,nextOrder:0},t.set(n,o)),o}function j1(n,t){return new Promise((o,i)=>{let e=()=>t.removeEventListener("abort",a),a=()=>{e(),i(t.reason)};if(t.aborted){a();return}t.addEventListener("abort",a,{once:!0}),Promise.resolve().then(()=>(t.throwIfAborted(),n())).then(A=>{e(),o(A)},A=>{e(),i(A)})})}function J1(n){let t=typeof n?.owner;return t!=="object"&&t!=="function"||n.owner===null||typeof n.key!="string"||!n.key?null:{owner:n.owner,key:n.key}}function $_(n){return n!==null&&typeof n=="object"&&typeof n.provider=="string"&&!!n.provider&&typeof n.model=="string"&&!!n.model&&(n.reasoningEffort===void 0||typeof n.reasoningEffort=="string"&&!!n.reasoningEffort)}function dCe(n){return n!==null&&typeof n=="object"&&Array.isArray(n.efforts)&&n.efforts.length>0&&n.efforts.every(t=>t!==null&&typeof t=="object"&&typeof t.id=="string"&&!!t.id&&typeof t.name=="string"&&!!t.name&&(t.description===void 0||typeof t.description=="string"))&&(n.defaultEffort===void 0||typeof n.defaultEffort=="string"&&!!n.defaultEffort)}function q1(n,t,{session:o=!1}={}){if(!n||typeof n!="object"||!Array.isArray(n.groups)||!Array.isArray(n.failures))throw new Error(`Harness returned an invalid response for ${t}`);for(let i of n.groups){if(!i||typeof i!="object"||typeof i.id!="string"||!i.id||typeof i.name!="string"||!i.name||!Array.isArray(i.models))throw new Error(`Harness returned an invalid response for ${t}`);for(let e of i.models)if(!e||typeof e!="object"||typeof e.id!="string"||!e.id||typeof e.name!="string"||!e.name||e.description!==void 0&&typeof e.description!="string"||e.reasoning!==void 0&&!dCe(e.reasoning))throw new Error(`Harness returned an invalid response for ${t}`)}for(let i of n.failures)if(!i||typeof i!="object"||typeof i.id!="string"||!i.id||typeof i.name!="string"||!i.name||i.message!==void 0&&typeof i.message!="string")throw new Error(`Harness returned an invalid response for ${t}`);if(o&&(!$_(n.current)||typeof n.routable!="boolean"))throw new Error(`Harness returned an invalid response for ${t}`);return n}function z1(){let n=new Error("Harness turn was stopped before producing a text reply");return n.code="turn-stopped",n}function hCe(n){return Array.isArray(n?.items)?n.items.flatMap(t=>typeof t?.path=="string"&&nCe(t.path)?[t.path]:[]):[]}function ECe(n,t){if(!Array.isArray(t?.items)||!Array.isArray(t?.archivedSessionIds))throw new Error("Harness returned an invalid response for workspace.list");let o=t.items.find(i=>i?.path===n);if(!o)return null;if(!Array.isArray(o.sessionIds)||o.sessionIds.some(i=>typeof i!="string"))throw new Error("Harness returned invalid session IDs for workspace.list");return o}function uCe(n){if(typeof n=="number"&&Number.isFinite(n))return n<1e12?n*1e3:n;if(typeof n=="string"&&n){let t=Date.parse(n);if(!Number.isNaN(t))return t}return null}function CCe(n){if(!n||typeof n!="object")return null;let t=[n?.header?.lastActivityAt,n?.header?.updatedAt,n?.header?.createdAt,n?.projections?.values?.lastActivityAt,n?.projections?.values?.updatedAt,n?.projections?.values?.createdAt,n?.lastActivityAt,n?.updatedAt,n?.createdAt];for(let o of t){let i=uCe(o);if(i!==null)return i}return null}function BCe(n,t,o){if(!Array.isArray(o?.items))throw new Error("Harness returned an invalid response for session.list");let i=new Set(t),e=new Map(o.items.flatMap(a=>typeof a?.sessionId=="string"?[[a.sessionId,a]]:[]));return{workspace:n.path,sessions:n.sessionIds.map(a=>{let A=e.get(a),r=A?.projections?.values?.title,s={sessionId:a,title:typeof r=="string"?r:null,archived:i.has(a),blank:A?.blank===!0,origin:A?.origin==="subagent"?"subagent":null,summaryAvailable:A!==void 0},g=A?.projections?.asOfSeq;Number.isSafeInteger(g)&&g>=-1&&(s.lastSeq=g);let c=CCe(A);return c!==null&&(s.time=c),s})}}function Au(n,t){return new Promise((o,i)=>{if(t?.aborted){i(t.reason??new DOMException("Aborted","AbortError"));return}let e=setTimeout(()=>{t?.removeEventListener("abort",a),o()},n),a=()=>{clearTimeout(e),i(t.reason??new DOMException("Aborted","AbortError"))};t?.addEventListener("abort",a,{once:!0})})}function fCe(n){return(n?.data?.message?.content??[]).filter(t=>t.type==="text"&&typeof t.text=="string").map(t=>t.text).join(` `).trim()}function Pi(n){return typeof n=="string"&&n.trim()?n.trim():null}function QCe(n){if(!n||typeof n!="object")return null;let t=Pi(n.message);if(t)return t;let o=Pi(n.name),i=Pi(n.code);return o||i?[o??"Error",i].filter(Boolean).join(": "):null}function Z1(n,t){let o=[...t].map(i=>i?.event??i).filter(Boolean).sort((i,e)=>(i.seq??-1)-(e.seq??-1));for(let i of o){let e=i.seq??-1;if(e<=n.lastSeq)continue;if(n.lastSeq=e,i.type==="turn/start"){let A=i.data?.turn??null;n.active&&A!==n.turn&&(n.active=!1),n.turn!==null&&A!==n.turn&&(n.completed=!0),n.openTurn=A;continue}if(i.type==="user/message"&&i.data?.source?.rpcId===n.promptRpcId){n.active=!0,n.started=!0,n.completed=!1,n.turn=i.data?.turn??n.openTurn;continue}if(i.type==="turn/end"&&i.data?.turn===n.turn){n.active=!1,n.completed=!0;continue}let a=null;if(i.type==="tool/call"&&n.active&&i.data?.turn===n.turn&&typeof i.data?.callId=="string"&&i.data.callId)a={callId:i.data.callId,name:i.data?.name,arguments:i.data?.arguments};else if(i.type==="tool/code-dispatch-start"&&n.active&&typeof i.data?.subCallId=="string"&&i.data.subCallId){let A;try{A=JSON.stringify(i.data?.arguments)}catch{A=void 0}a={callId:i.data.subCallId,name:i.data?.name,arguments:A}}a&&n.toolCalls.set(a.callId,Object.freeze(a))}}function X1(n,t,o=[]){if(!n||!["object","function","string"].includes(typeof n)||typeof t!="string"||!t)return!1;let i=W1(n).ownerships.get(t);if(!i||i.size===0)return!1;for(let e of i)Z1(e,o);return[...i].some(e=>e.active&&!e.completed&&typeof e.reconnect=="function")}var Kp=class{#e;#A;#t=null;#r=null;#l=new Map;#o="";#i=!1;#a=null;#n=new Map;#s=null;constructor({promptRpcId:t,afterSeq:o=-1}){this.#e=t,this.#A=o}get finished(){return this.#i}get answer(){return this.#o.trim()}get reason(){return this.#a}get tracking(){return this.#r!==null&&!this.#i}get turn(){return this.#r}consumeAll(t){let o=[],i=a=>{if(a.type==="text"&&o.length>0&&o[o.length-1].type==="text"){o[o.length-1]=a;return}o.push(a)},e=[...t].map(a=>a?.event??a).filter(Boolean).sort((a,A)=>(a.seq??-1)-(A.seq??-1));for(let a of e){let A=a.seq??-1;if(!(A<=this.#A)){if(this.#A=A,a.type==="turn/start"&&(this.#t=a.data?.turn??null),a.type==="user/message"&&a.data?.source?.rpcId===this.#e){this.#r=this.#t;continue}if(this.#r!==null){if(a.type==="turn/end"){if(a.data?.turn!==this.#r)continue;this.#i=!0,this.#a=a.data?.reason??null,this.#t=null;continue}if(a.data?.turn===this.#r){if(a.type==="assistant/chunk"&&a.data?.chunk?.type==="text-delta"){let r=a.data?.step??0,s=a.data.chunk.index??0,g=`${r}:${s}`;this.#l.set(g,(this.#l.get(g)??"")+a.data.chunk.text);let c=`${r}:`,I=[...this.#l.entries()].filter(([p])=>p.startsWith(c)).sort(([p],[B])=>Number(p.split(":")[1])-Number(B.split(":")[1])).map(([,p])=>p).join(` -`).trim();I&&I!==this.#o&&(this.#o=I,i({type:"text",text:I}));continue}if(a.type==="assistant/message"){let r=fCe(a);r&&r!==this.#o&&(this.#o=r,i({type:"text",text:r}));continue}if(a.type==="tool/call"){let r=Pi(a.data?.name)??w("\u5DE5\u5177"),s=Pi(a.data?.callId)??Pi(a.data?.subCallId);s&&this.#n.set(s,r),this.#s=r,i({type:"tool",name:r,...s?{callId:s}:{}})}else if(a.type==="tool/result"){let r=Pi(a.data?.message?.source?.callId)??Pi(a.data?.callId)??Pi(a.data?.subCallId),s=(r?this.#n.get(r):null)??this.#s,g=QCe(a.data?.error);i({type:"status",text:w("\u6B63\u5728\u6574\u7406\u7ED3\u679C\u2026"),...s?{toolName:s}:{},...g?{error:g}:{}})}}}}}return o}consume(t){return this.consumeAll(t).at(-1)??null}},mn=class extends Error{constructor(t,o){super(`${t}: ${o?.message??"unknown Harness RPC error"}`),this.name="HarnessRpcError",this.method=t,this.code=o?.code??"internal",this.details=o?.details??{}}},ns=class extends Error{constructor(t,o,{cause:i,status:e}={}){let a=Number.isInteger(e)?`, HTTP ${e}`:"";super(`Harness ${o} transport failed (${t}${a})`,{cause:i}),this.name="HarnessTransportError",this.code=t,this.method=o,Number.isInteger(e)&&(this.status=e)}},eT=class extends Error{constructor(t){super("Harness health RPC was rejected",{cause:t}),this.name="HarnessHealthError",this.code="harness-rpc-rejected",this.method="host.describe"}},xp=class extends Error{constructor(t,o){super(o),this.name="HarnessInteractionError",this.code=t}},ss=class extends Error{constructor(t,{reason:o,providerCode:i}={}){super(`Harness turn failed (${t})`),this.name="HarnessTurnError",this.code=t,this.promptAccepted=!0,o&&typeof o=="object"&&(this.reason=o),typeof i=="string"&&i&&(this.providerCode=i)}};function V1(n){let t=Pi(n?.kind)??Pi(n);if(t==="error"){let o=n?.error??n?.failure;return new ss("harness-turn-failed",{reason:n,providerCode:Pi(o?.code)??void 0})}return t==="max-tokens"?new ss("model-max-tokens",{reason:n}):t==="blocked"?new ss("turn-blocked",{reason:n}):["interrupted","stopped","cancelled","canceled"].includes(t)?new ss("turn-interrupted",{reason:n}):t==="aborted"?new ss("turn-aborted",{reason:n}):t==="completed"?new ss("model-empty-response",{reason:n}):new ss("harness-turn-failed",{reason:n})}function yCe(n){return n==null?!0:(Pi(n?.kind)??Pi(n))==="completed"}var Qr=class{#e;#A;#t;#r;#l;#o;#i;#a;#n;#s;#p;#c;#g;#I;#d;#h=null;#u;#C;#B;#f;constructor({baseUrl:t,apiProxy:o,interactionScope:i=o,workspace:e,agentPreset:a,autostart:A=!1,dshBin:r="dsh",fetchImpl:s=fetch,createWebSocket:g=m=>new WebSocket(m),interactionReconnectDelayMs:c=500,rpcIdPrefix:I="im",logPrefix:p="dsh-im",commandExecutor:B,controlExecutor:f,sessionMaintenanceExecutor:Q,fileIngressExecutor:y}){if(typeof g!="function")throw new TypeError("createWebSocket must be a function");if(!Number.isFinite(c)||c<0)throw new TypeError("interactionReconnectDelayMs must be a non-negative number");if(typeof I!="string"||!I.trim())throw new TypeError("rpcIdPrefix must be a non-empty string");if(typeof p!="string"||!p.trim())throw new TypeError("logPrefix must be a non-empty string");if(B!==void 0&&typeof B!="function")throw new TypeError("commandExecutor must be a function");if(f!==void 0&&typeof f!="function")throw new TypeError("controlExecutor must be a function");if(Q!==void 0&&typeof Q!="function")throw new TypeError("sessionMaintenanceExecutor must be a function");if(y!==void 0&&typeof y!="function")throw new TypeError("fileIngressExecutor must be a function");if(this.#e=t===void 0?null:new URL(t),this.#A=this.#e?null:o,!this.#e&&(!this.#A||typeof this.#A!="object"))throw new TypeError("HarnessClient requires the current Host apiProxy or an explicit baseUrl");if(this.#A&&(!i||!["object","function"].includes(typeof i)))throw new TypeError("interactionScope must identify the current Host");this.#t=e,this.#r=a??void 0,this.#l=!!(this.#e&&A),this.#o=r,this.#i=s,this.#a=g,this.#n=c,this.#s=I.trim(),this.#p=p.trim(),this.#c=B,this.#g=f,this.#I=Q,this.#d=y,this.#u=W1(this.#e?.origin??i),this.#C=this.#u.ownerships,this.#B=this.#u.claims,this.#f=this.#u.controls}async rpc(t,o={},i=3e4,e={}){let a=e.rpcId??`${this.#s}-${X_()}`,A=AbortSignal.timeout(i),r=e.signal?AbortSignal.any([e.signal,A]):A,s;try{if(this.#A){let[g,c,I]=t.split("."),p={host:"host",workspace:"workspace",session:"sessions",llm:"llm"}[g],B=p&&this.#A[p];if(I!==void 0||!Object.hasOwn(B??{},c)||typeof B[c]!="function")throw new ns("harness-api-not-found",t);s={type:"server-response",...await j1(()=>B[c]({rpcId:a,payload:o},r),r)}}else{let g=await this.#i(new URL(`/api/${t}`,this.#e),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({type:"client-request",rpcId:a,method:t,payload:o}),signal:r});if(!g.ok){let c=await pCe(g,this.#e.hostname);throw new ns(c,t,{status:g.status})}try{s=await g.json()}catch(c){throw new ns("harness-response-invalid",t,{cause:c})}}}catch(g){throw e.signal?.aborted||g instanceof ns?g:new ns(A.aborted?"harness-timeout":"harness-connect-failed",t,{cause:g})}if(s?.type!=="server-response"||s?.rpcId!==a)throw new ns("harness-response-invalid",t,{cause:new Error(`Harness returned an invalid response for ${t}`)});if(!s.result||typeof s.result!="object"||typeof s.result.ok!="boolean")throw new ns("harness-response-invalid",t,{cause:new Error(`Harness returned an invalid result for ${t}`)});if(!s.result?.ok)throw new mn(t,s.result?.error);return s.result.value}async health(t={}){try{return await this.rpc("host.describe",{},5e3,t),!0}catch(o){throw o instanceof mn?new eT(o):o}}async ensureRunning(t={}){try{return await this.health(t)}catch(e){if(!this.#l)throw e}if(!this.#h||this.#h.exitCode!==null){let e=this.#e.port||(this.#e.protocol==="https:"?"443":"80");this.#h=oCe(this.#o,["web","--host",this.#e.hostname,"--port",e],{cwd:this.#t,env:process.env,stdio:["ignore","inherit","inherit"]}),this.#h.on("error",a=>{console.error(`[${this.#p}] failed to start Harness:`,a.message)})}let o=Date.now()+6e4,i;for(;Date.now(){let r=A&&i.signal?AbortSignal.any([A,i.signal]):A??i.signal;return this.rpc("session.selectModel",{sessionId:t,provider:o.provider,model:o.model,...o.reasoningEffort===void 0?{}:{reasoningEffort:o.reasoningEffort}},3e4,r?{...i,signal:r}:i)},a=this.#I?await this.#I({sessionId:t,operation:e}):await e();if(!a||typeof a!="object"||!$_(a.selected))throw new Error("Harness returned an invalid response for session.selectModel");return a}async isSessionRunning(t,o={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");await this.ensureRunning(o);let i=await this.rpc("session.list",{},3e4,o);if(!i||typeof i!="object"||!Array.isArray(i.items))throw new Error("Harness returned an invalid response for session.list");for(let e of i.items)if(!e||typeof e!="object"||typeof e.sessionId!="string"||!e.sessionId||typeof e.running!="boolean")throw new Error("Harness returned an invalid response for session.list");return i.items.find(e=>e.sessionId===t)?.running??!1}async adoptWorkspaceSession(t,o={}){return x1(this,t,o)}async workspaceId(t={}){let{workspace:o=this.#t,...i}=t,{items:e}=await this.rpc("workspace.list",{},3e4,i),a=e.find(r=>r.path===o);return a?a.workspaceId:(await this.rpc("workspace.create",{path:o},3e4,i)).workspace.workspaceId}async createSession(t={}){let{agentPreset:o,...i}=t;await this.ensureRunning(i);let a={workspaceId:await this.workspaceId(i)},A=o!==void 0?o:this.#r;return A!=null&&(a.agentPreset=A),(await this.rpc("session.create",a,3e4,i)).sessionId}async renameSession(t,o,i={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");if(typeof o!="string"||!o.trim())throw new TypeError("session title is required");return this.rpc("session.rename",{sessionId:t,title:o},3e4,i)}async executeCommand(t,o,i={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");if(typeof o!="string"||!o)throw new TypeError("command line is required");if(!this.#c){let e=new Error("Harness command execution is unavailable");throw e.code="commands-unavailable",e}try{return await this.#c(t,o,i)}catch(e){throw e?.failure&&typeof e.failure=="object"?new mn("commands.execute",e.failure):e}}async readSessionHistory(t,{maxMessages:o=50,beforeSeq:i,timeoutMs:e=1e4,...a}={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");if(!Number.isSafeInteger(o)||o<1||i!==void 0&&(!Number.isSafeInteger(i)||i<0))throw new TypeError("Invalid history pagination");return this.rpc("session.history",{sessionId:t,maxMessages:o,...i===void 0?{}:{beforeSeq:i}},e,a)}async sessionExists(t,o={}){try{return await this.rpc("session.history",{sessionId:t,maxMessages:1},3e4,o),!0}catch(i){if(i instanceof mn&&i.code==="session-not-found")return!1;throw i}}async respondInteraction(t,o,i={}){if(typeof t!="string"||!t)throw new TypeError("rpcId is required");if(!o||typeof o!="object"||typeof o.ok!="boolean")throw new TypeError("A Harness RPC result is required");let e=AbortSignal.timeout(i.timeoutMs??3e4),a=i.signal?AbortSignal.any([i.signal,e]):e,A={type:"client-response",rpcId:t,result:o},r;if(this.#A)r=await j1(()=>this.#A.respond(A),a);else{let g=await this.#i(new URL("/api/respond",this.#e),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(A),signal:a});if(!g.ok)throw new Error(`Harness transport respond failed: HTTP ${g.status}`);r=await g.json()}if(r?.accepted===!0)return r;if(r?.accepted!==!1||r.reason!=="bad-response"&&r.reason!=="not-pending")throw new Error("Harness returned an invalid interaction response receipt");let s=r.reason;throw new xp(`interaction-${s}`,`Harness interaction response was rejected (${s})`)}async watchInteractions(t,{signal:o,onInteraction:i,onResolved:e,onOpen:a,ownership:A}={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");if(!o||typeof o.addEventListener!="function")throw new TypeError("watchInteractions requires an AbortSignal");if(i!==void 0&&typeof i!="function")throw new TypeError("onInteraction must be a function");if(e!==void 0&&typeof e!="function")throw new TypeError("onResolved must be a function");if(a!==void 0&&typeof a!="function")throw new TypeError("onOpen must be a function");for(;!o.aborted;){try{await this.#M(t,{signal:o,onInteraction:i,onResolved:e,onOpen:a,ownership:A})}catch(r){if(o.aborted)return;console.warn(`[${this.#p}] Harness interaction stream disconnected:`,r.message)}if(o.aborted)return;try{await Au(this.#n,o)}catch{if(o.aborted)return;throw new Error("Harness interaction reconnect wait failed")}}}#Q(t,o){let i=this.#C.get(t)??new Set;o.order=this.#u.nextOrder,this.#u.nextOrder+=1,i.add(o),this.#C.set(t,i)}#S(t,o){let i=this.#C.get(t);i?.delete(o),i?.size===0&&this.#C.delete(t);for(let[e,a]of this.#B)a.ownership===o&&this.#B.delete(e)}#y(t){if(!t.control)return;let o=this.#f.get(t.control.owner);o||(o=new Map,this.#f.set(t.control.owner,o));let i=o.get(t.control.key)??new Set;i.add(t),o.set(t.control.key,i)}#m(t){if(!t.control)return;let o=this.#f.get(t.control.owner),i=o?.get(t.control.key);i?.delete(t),i?.size===0&&o.delete(t.control.key),o?.size===0&&this.#f.delete(t.control.owner)}#D(t,o){let i=J1(o);return i?[...this.#f.get(i.owner)?.get(i.key)??[]].filter(a=>a.sessionId===t).sort((a,A)=>a.order-A.order):[]}#E(t,o){return this.#D(t,o).find(i=>i.started&&i.active&&!i.completed&&i.turn!==null)??null}async#R(t,o,i){let e=this.#D(t,o);if(e.length===0)return null;try{let a=await this.rpc("session.history",{sessionId:t,maxMessages:50},3e4,i);this.#_(t,a.events??[])}catch(a){if(!(a instanceof mn)||a.code!=="session-not-found")throw a;for(let A of e)A.active=!1,A.completed=!0;return null}return this.#E(t,o)}async hasActiveTurn(t,o,i={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");return!!await this.#R(t,o,i)}async stopActiveTurn(t,o,i={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");let e=await this.#R(t,o,i);if(!e)return!1;if(e.stopRequested)return!0;if(this.#E(t,o)!==e)return!1;e.stopRequested=!0;try{if(this.#g){let a=this.#g({sessionId:t,expectedTurn:e.turn,promptRpcId:e.promptRpcId,action:"stop"});if(a&&typeof a.then=="function")throw new TypeError("controlExecutor must return synchronously");if(a!==void 0){if(typeof a!="boolean")throw new TypeError("controlExecutor must return a boolean or undefined");return a||(e.stopRequested=!1),a}}return await this.rpc("session.cancel",{sessionId:t,keepInbox:!0},3e4,i),!0}catch(a){if(this.#E(t,o)===e&&(e.stopRequested=!1),a instanceof mn&&a.code==="session-not-found")return!1;throw a}}async steerActiveTurn(t,o,i,e={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");if(typeof o!="string"||!o.trim())throw new TypeError("Steering text is required");let a=await this.#R(t,i,e);if(!a||a.stopRequested||this.#E(t,i)!==a)return!1;if(this.#g){let A=this.#g({sessionId:t,expectedTurn:a.turn,promptRpcId:a.promptRpcId,action:"steer",text:o});if(A&&typeof A.then=="function")throw new TypeError("controlExecutor must return synchronously");if(A!==void 0){if(typeof A!="boolean")throw new TypeError("controlExecutor must return a boolean or undefined");return A}}return await this.rpc("session.prompt",{sessionId:t,mode:"steer",content:[{type:"text",text:o}],clientTimeZone:Intl.DateTimeFormat().resolvedOptions().timeZone},3e4,e),!0}#_(t,o){for(let i of this.#C.get(t)??[])Z1(i,o)}async#O(t,o){let i=await this.rpc("session.history",{sessionId:t,maxMessages:50},3e4,{signal:o});this.#_(t,i.events??[])}#w(t,o,i){let e=this.#B.get(o);if(e&&this.#C.get(t)?.has(e.ownership))return e;let a=[...this.#C.get(t)??[]],A=a.filter(s=>s.active).sort((s,g)=>s.order-g.order);if(A.length>0)return{ownership:A[0],recovered:!1};let r=a.filter(s=>!s.started&&!s.completed).sort((s,g)=>s.order-g.order)[0]??null;return r?{ownership:r,recovered:!0}:null}async#T(t,o,i){if(!this.#d)throw new ko("inbound-file-ingress-unavailable","Harness file ingress is unavailable in this Host process.");let a=(await this.rpc("session.list",{},3e4,{signal:i}))?.items?.find(A=>A?.sessionId===t)?.cwd;return this.#d({sessionId:t,workspace:a,files:o,signal:i})}async ask(t,o,i={}){typeof i=="number"&&(i={timeoutMs:i});let e=i.timeoutMs??6e5,a=i.signal,A=typeof i.onUpdate=="function"?i.onUpdate:null,r=i.progressMode==="all"?"all":"latest",s=typeof i.onArtifact=="function"?i.onArtifact:null,g=typeof i.onInteraction=="function"?i.onInteraction:void 0,c=typeof i.onInteractionResolved=="function"?i.onInteractionResolved:void 0,I=J1(i.control),p=Array.isArray(i.files)?i.files.filter(Boolean):[];await this.ensureRunning({signal:a});let B=await this.rpc("session.history",{sessionId:t,maxMessages:1},3e4,{signal:a}),f=Math.max(-1,...(B.events??[]).map(({event:V})=>V.seq??-1)),Q=`${this.#s}-${X_()}`,y=new Kp({promptRpcId:Q,afterSeq:f}),m=g||c?new AbortController:null,T=m?a?AbortSignal.any([a,m.signal]):m.signal:null,_=m||I?{sessionId:t,promptRpcId:Q,active:!1,started:!1,completed:!1,stopRequested:!1,turn:null,openTurn:null,lastSeq:f,reconnect:null,order:-1,toolCalls:new Map,control:I}:null,R=null,x=!1,F=0,L=[],q=!1,z=!1,H=async()=>{if(!s||x||y.turn===null)return F;x=!0;let V=FI.take(t,y.turn,{signal:a});for(let U of V)try{await s(U),F+=1}catch(N){FI.release(U),console.warn(`[${this.#p}] ignored an artifact handoff failure:`,N.message)}return F};_&&(this.#Q(t,_),this.#y(_));let G=FI.openConsumer(t,Q);try{let V=o;if(p.length>0){let J=await this.#T(t,p,a);L.push(J),o=L_(o,J)}if(T){let J,X=new Promise(ie=>{J=ie});R=this.watchInteractions(t,{signal:T,onInteraction:g,onResolved:c,onOpen:J,ownership:_}),R.catch(()=>{}),await Promise.race([X,Au(3e4,T).then(()=>{throw new Error("Harness interaction stream did not open within 30 seconds")})])}let U=typeof o=="string"?[{type:"text",text:o}]:o;if(!Array.isArray(U)||U.length===0)throw new TypeError("Harness prompt content is required");let N=Intl.DateTimeFormat().resolvedOptions().timeZone,k=J=>this.rpc("session.prompt",{sessionId:t,mode:"queue",content:J,clientTimeZone:N},3e4,{rpcId:Q,signal:a});try{await k(U)}catch(J){let X=Wx(J)?zx(U):[];if(X.length===0)throw J;let ie;try{ie=await this.#T(t,X,a)}catch(Se){throw a?.aborted||console.warn(`[${this.#p}] unable to restage rejected images as workspace files:`,Se?.message??String(Se)),J}L.push(ie);let oe=L_([...Vx(typeof V=="string"?[{type:"text",text:V}]:V),{type:"text",text:w(Jx)}],{files:L.flatMap(Se=>Se?.files??[])});await k(oe)}q=!0;try{let J=Date.now()+e;for(;Date.now()0)return"";throw _?.stopRequested?z1():V1(y.reason)}throw new ss("harness-reply-timeout")}catch(J){if(!_?.stopRequested)throw J;if(y.answer)return y.answer;throw J?.code==="turn-stopped"?J:z1()}}finally{if(!q||z)for(let V of L)try{await V?.cleanup?.()}catch(U){console.warn(`[${this.#p}] unable to clean inbound files:`,U.message)}G(),_&&(this.#m(_),this.#S(t,_)),y.turn!==null&&FI.discard(t,y.turn),m?.abort(new DOMException("Harness turn finished","AbortError")),R&&await R.catch(()=>{})}}async#M(t,{signal:o,onInteraction:i,onResolved:e,onOpen:a,ownership:A}){let r=!1,s=null,g=Promise.resolve(),c=A==null,I=[],p=()=>{},B=m=>{p=m,A&&(A.reconnect=p);try{a?.()}catch(T){console.warn(`[${this.#p}] ignored an interaction open callback failure:`,T.message)}A&&this.#O(t,o).then(()=>{if(!r){c=!0;for(let T of I.splice(0))Q(T)}}).catch(T=>{s??=T,p()})},f=(m,T)=>{m&&(g=g.then(()=>m(T)).catch(_=>{s??=_,p()}))},Q=m=>{let T=m.payload;if(A&&T.type==="session/event"){this.#_(t,[T.event]);return}if(T.type==="question/requested"||T.type==="approval/requested"){let _=T.type==="question/requested"?"question":"approval",R=_==="question"?m.rpcId:T.approvalId,x=`${_}:${R}`;if(A){let L=this.#w(t,x,_);if(L?.ownership!==A)return;this.#B.set(x,L)}let F=_==="approval"&&A&&typeof T.callId=="string"?this.#B.get(x)?.ownership.toolCalls.get(T.callId):void 0;f(i,Object.freeze({kind:_,interactionId:R,rpcId:m.rpcId,sessionId:t,payload:T,recovered:A?this.#B.get(x)?.recovered===!0:!1,...F?{toolCall:F}:{},reconnect:p,respond:(L,q={})=>this.respondInteraction(m.rpcId,L,{...q,signal:q.signal??o})}));return}if(T.type==="question/resolved"||T.type==="approval/resolved"){let _=T.type==="question/resolved"?"question":"approval",R=_==="question"?T.questionRpcId:T.approvalId,x=`${_}:${R}`;if(A){if(this.#B.get(x)?.ownership!==A)return;this.#B.delete(x)}f(e,Object.freeze({kind:_,interactionId:R,sessionId:t,outcome:T.outcome,payload:T}))}},y=m=>{try{let T=m?.payload;if(m?.type!=="server-request"||typeof m.rpcId!="string"||!T||typeof T!="object"||m.method!==T.type)throw new Error("invalid server-request envelope");if(T.sessionId!==t)return;c?Q(m):I.push(m)}catch(T){console.warn(`[${this.#p}] ignored a malformed Harness interaction frame:`,T.message)}};try{await this.#P({signal:o,onOpen:B,onEnvelope:y})}finally{r=!0,A?.reconnect===p&&(A.reconnect=null),o.aborted||await g}if(!o.aborted&&s)throw s}async watchHarnessEvents({signal:t,onSessionEvent:o,onReconnect:i}={}){if(typeof o!="function")throw new TypeError("watchHarnessEvents requires onSessionEvent");if(!t||typeof t.addEventListener!="function")throw new TypeError("watchHarnessEvents requires an AbortSignal");if(i!==void 0&&typeof i!="function")throw new TypeError("onReconnect must be a function");for(;!t.aborted;){try{await this.#P({signal:t,onOpen:()=>{try{i?.()}catch(e){console.warn(`[${this.#p}] mux reconnect hook failed:`,e.message)}},onEnvelope:e=>{try{let a=e?.payload;if(e?.type!=="server-request"||!a||typeof a!="object"||e.method!==a.type||a.type!=="session/event"||typeof a.sessionId!="string"||!a.event||typeof a.event!="object")return;o({sessionId:a.sessionId,event:a.event})}catch(a){console.warn(`[${this.#p}] ignored a malformed global mux frame:`,a.message)}}})}catch(e){if(t.aborted)return;console.warn(`[${this.#p}] Harness event mux disconnected:`,e.message)}if(t.aborted)return;try{await Au(this.#n,t)}catch{if(t.aborted)return;throw new Error("Harness event mux reconnect wait failed")}}}#P(t){return Y1({apiProxy:this.#A,baseUrl:this.#e,createWebSocket:this.#a,rpcId:`${this.#s}-${X_()}`,...t,onMalformed:o=>{console.warn(`[${this.#p}] ignored a malformed Harness mux frame:`,o.message)}})}stopManagedProcess(){this.#h?.exitCode===null&&this.#h.kill("SIGTERM")}};var JQ=class extends Qr{constructor(t){super({...t,rpcIdPrefix:"dingtalk",logPrefix:"dsh-dingtalk"})}};import{mkdir as mCe,readFile as SCe,realpath as wCe,rename as DCe,stat as OCe,unlink as bCe,writeFile as MCe}from"node:fs/promises";import{dirname as _Ce,isAbsolute as A3,resolve as ru}from"node:path";var itA=Object.freeze({version:1,workspaces:Object.freeze({})});function LI(n){let t=new Error(n);return t.code=Sa,t}async function AT(n){return ru(await wCe(n))}async function TCe(n,t){if(n===t)return!0;try{return await AT(n)===await AT(t)}catch{return!1}}function jt(n){if(typeof n!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(n))throw new TypeError("Invalid bot id");return n}function pi(n,t,o){let i=new Error(t,o);return i.code=n,i}function qQ(n){if(typeof n!="string"||!/^[A-Za-z0-9._:@-]{1,128}$/.test(n))throw pi("invalid-target","Invalid target id");return n}function NCe(n){if(!n||typeof n!="object"||Array.isArray(n))throw pi("invalid-target","Invalid target route");try{let t=JSON.stringify(n);if(t===void 0)throw new TypeError("route is not JSON");let o=JSON.parse(t);if(!o||typeof o!="object"||Array.isArray(o))throw new TypeError("route is not an object");return o}catch(t){throw pi("invalid-target","Invalid target route",{cause:t})}}function UI(n,{targetId:t}={}){if(!n||typeof n!="object"||Array.isArray(n))throw pi("invalid-target","Invalid delivery target");let o=t===void 0?new Set(["targetId","name","kind","route"]):new Set(["name","kind","route"]);if(Object.keys(n).some(a=>!o.has(a)))throw pi("invalid-target","Invalid delivery target");let i=qQ(t??n.targetId);if(typeof n.kind!="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(n.kind))throw pi("invalid-target","Invalid target kind");let e;if(n.name!==void 0){if(typeof n.name!="string"||!n.name.trim()||n.name.trim().length>80)throw pi("invalid-target","Invalid target name");e=n.name.trim()}return{targetId:i,...e===void 0?{}:{name:e},kind:n.kind,route:NCe(n.route)}}function RCe(n){let t=Object.create(null);if(n===void 0)return t;if(!n||typeof n!="object"||Array.isArray(n))return null;try{for(let[o,i]of Object.entries(n)){if(jt(o),!i||typeof i!="object"||Array.isArray(i))return null;let e=Object.create(null);for(let[a,A]of Object.entries(i)){let r=UI(A,{targetId:a}),{targetId:s,...g}=r;e[a]=g}t[o]=e}}catch{return null}return t}function kCe(n,t){let o=Object.create(null);if(n===void 0)return o;if(!n||typeof n!="object"||Array.isArray(n)){for(let i of Object.keys(t))o[i]=null;return o}for(let[i,e]of Object.entries(n))try{jt(i),o[i]=Hp(e)}catch{}return o}function vCe(n){if(!n||![1,2].includes(n.version)||!n.workspaces||typeof n.workspaces!="object"||Array.isArray(n.workspaces))return null;let t={};for(let[r,s]of Object.entries(n.workspaces)){if(!/^[A-Za-z0-9_-]{1,128}$/.test(r)||typeof s!="string"||!A3(s))return null;t[r]=ru(s)}let o={};if(n.agentPresets!==void 0){if(!n.agentPresets||typeof n.agentPresets!="object"||Array.isArray(n.agentPresets))return null;for(let[r,s]of Object.entries(n.agentPresets)){if(!/^[A-Za-z0-9_-]{1,128}$/.test(r))return null;try{let g=vp(s);if(!g)return null;o[r]=g}catch{return null}}}let i=Object.create(null);if(n.contextEnhancement&&typeof n.contextEnhancement=="object"&&!Array.isArray(n.contextEnhancement))for(let[r,s]of Object.entries(n.contextEnhancement))/^[A-Za-z0-9_-]{1,128}$/.test(r)&&(i[r]=Gp(s));if(n.version===1&&n.deliveryTargets!==void 0)return null;let e=RCe(n.deliveryTargets);if(!e)return null;let a=kCe(n.accessPolicies,t);return{version:n.accessPolicies===void 0?n.version:2,workspaces:t,agentPresets:o,contextEnhancement:i,deliveryTargets:e,accessPolicies:a}}function PCe({version:n,workspaces:t,agentPresets:o,contextEnhancement:i,deliveryTargets:e,accessPolicies:a}){let A={version:n,workspaces:t};return Object.keys(o).length>0&&(A.agentPresets=o),Object.keys(i).length>0&&(A.contextEnhancement=i),n>=2&&Object.keys(e).length>0&&(A.deliveryTargets=e),n>=2&&Object.keys(a).length>0&&(A.accessPolicies=a),A}async function GCe(n,t){await mCe(_Ce(n),{recursive:!0,mode:448});let o=`${n}.tmp`;await MCe(o,`${JSON.stringify(t,null,2)} +`).trim();I&&I!==this.#o&&(this.#o=I,i({type:"text",text:I}));continue}if(a.type==="assistant/message"){let r=fCe(a);r&&r!==this.#o&&(this.#o=r,i({type:"text",text:r}));continue}if(a.type==="tool/call"){let r=Pi(a.data?.name)??w("\u5DE5\u5177"),s=Pi(a.data?.callId)??Pi(a.data?.subCallId);s&&this.#n.set(s,r),this.#s=r,i({type:"tool",name:r,...s?{callId:s}:{}})}else if(a.type==="tool/result"){let r=Pi(a.data?.message?.source?.callId)??Pi(a.data?.callId)??Pi(a.data?.subCallId),s=(r?this.#n.get(r):null)??this.#s,g=QCe(a.data?.error);i({type:"status",text:w("\u6B63\u5728\u6574\u7406\u7ED3\u679C\u2026"),...s?{toolName:s}:{},...g?{error:g}:{}})}}}}}return o}consume(t){return this.consumeAll(t).at(-1)??null}},mn=class extends Error{constructor(t,o){super(`${t}: ${o?.message??"unknown Harness RPC error"}`),this.name="HarnessRpcError",this.method=t,this.code=o?.code??"internal",this.details=o?.details??{}}},ns=class extends Error{constructor(t,o,{cause:i,status:e}={}){let a=Number.isInteger(e)?`, HTTP ${e}`:"";super(`Harness ${o} transport failed (${t}${a})`,{cause:i}),this.name="HarnessTransportError",this.code=t,this.method=o,Number.isInteger(e)&&(this.status=e)}},eT=class extends Error{constructor(t){super("Harness health RPC was rejected",{cause:t}),this.name="HarnessHealthError",this.code="harness-rpc-rejected",this.method="host.describe"}},xp=class extends Error{constructor(t,o){super(o),this.name="HarnessInteractionError",this.code=t}},ss=class extends Error{constructor(t,{reason:o,providerCode:i}={}){super(`Harness turn failed (${t})`),this.name="HarnessTurnError",this.code=t,this.promptAccepted=!0,o&&typeof o=="object"&&(this.reason=o),typeof i=="string"&&i&&(this.providerCode=i)}};function V1(n){let t=Pi(n?.kind)??Pi(n);if(t==="error"){let o=n?.error??n?.failure;return new ss("harness-turn-failed",{reason:n,providerCode:Pi(o?.code)??void 0})}return t==="max-tokens"?new ss("model-max-tokens",{reason:n}):t==="blocked"?new ss("turn-blocked",{reason:n}):["interrupted","stopped","cancelled","canceled"].includes(t)?new ss("turn-interrupted",{reason:n}):t==="aborted"?new ss("turn-aborted",{reason:n}):t==="completed"?new ss("model-empty-response",{reason:n}):new ss("harness-turn-failed",{reason:n})}function yCe(n){return n==null?!0:(Pi(n?.kind)??Pi(n))==="completed"}var Qr=class{#e;#A;#t;#r;#l;#o;#i;#a;#n;#s;#p;#c;#g;#I;#d;#h=null;#u;#C;#B;#f;constructor({baseUrl:t,apiProxy:o,interactionScope:i=o,workspace:e,agentPreset:a,autostart:A=!1,dshBin:r="dsh",fetchImpl:s=fetch,createWebSocket:g=m=>new WebSocket(m),interactionReconnectDelayMs:c=500,rpcIdPrefix:I="im",logPrefix:p="dsh-im",commandExecutor:B,controlExecutor:f,sessionMaintenanceExecutor:Q,fileIngressExecutor:y}){if(typeof g!="function")throw new TypeError("createWebSocket must be a function");if(!Number.isFinite(c)||c<0)throw new TypeError("interactionReconnectDelayMs must be a non-negative number");if(typeof I!="string"||!I.trim())throw new TypeError("rpcIdPrefix must be a non-empty string");if(typeof p!="string"||!p.trim())throw new TypeError("logPrefix must be a non-empty string");if(B!==void 0&&typeof B!="function")throw new TypeError("commandExecutor must be a function");if(f!==void 0&&typeof f!="function")throw new TypeError("controlExecutor must be a function");if(Q!==void 0&&typeof Q!="function")throw new TypeError("sessionMaintenanceExecutor must be a function");if(y!==void 0&&typeof y!="function")throw new TypeError("fileIngressExecutor must be a function");if(this.#e=t===void 0?null:new URL(t),this.#A=this.#e?null:o,!this.#e&&(!this.#A||typeof this.#A!="object"))throw new TypeError("HarnessClient requires the current Host apiProxy or an explicit baseUrl");if(this.#A&&(!i||!["object","function"].includes(typeof i)))throw new TypeError("interactionScope must identify the current Host");this.#t=e,this.#r=a??void 0,this.#l=!!(this.#e&&A),this.#o=r,this.#i=s,this.#a=g,this.#n=c,this.#s=I.trim(),this.#p=p.trim(),this.#c=B,this.#g=f,this.#I=Q,this.#d=y,this.#u=W1(this.#e?.origin??i),this.#C=this.#u.ownerships,this.#B=this.#u.claims,this.#f=this.#u.controls}async rpc(t,o={},i=3e4,e={}){let a=e.rpcId??`${this.#s}-${X_()}`,A=AbortSignal.timeout(i),r=e.signal?AbortSignal.any([e.signal,A]):A,s;try{if(this.#A){let[g,c,I]=t.split("."),p={host:"host",workspace:"workspace",session:"sessions",llm:"llm"}[g],B=p&&this.#A[p];if(I!==void 0||!Object.hasOwn(B??{},c)||typeof B[c]!="function")throw new ns("harness-api-not-found",t);s={type:"server-response",...await j1(()=>B[c]({rpcId:a,payload:o},r),r)}}else{let g=await this.#i(new URL(`/api/${t}`,this.#e),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({type:"client-request",rpcId:a,method:t,payload:o}),signal:r});if(!g.ok){let c=await pCe(g,this.#e.hostname);throw new ns(c,t,{status:g.status})}try{s=await g.json()}catch(c){throw new ns("harness-response-invalid",t,{cause:c})}}}catch(g){throw e.signal?.aborted||g instanceof ns?g:new ns(A.aborted?"harness-timeout":"harness-connect-failed",t,{cause:g})}if(s?.type!=="server-response"||s?.rpcId!==a)throw new ns("harness-response-invalid",t,{cause:new Error(`Harness returned an invalid response for ${t}`)});if(!s.result||typeof s.result!="object"||typeof s.result.ok!="boolean")throw new ns("harness-response-invalid",t,{cause:new Error(`Harness returned an invalid result for ${t}`)});if(!s.result?.ok)throw new mn(t,s.result?.error);return s.result.value}async health(t={}){try{return await this.rpc("host.describe",{},5e3,t),!0}catch(o){throw o instanceof mn?new eT(o):o}}async ensureRunning(t={}){try{return await this.health(t)}catch(e){if(!this.#l)throw e}if(!this.#h||this.#h.exitCode!==null){let e=this.#e.port||(this.#e.protocol==="https:"?"443":"80");this.#h=oCe(this.#o,["web","--host",this.#e.hostname,"--port",e],{cwd:this.#t,env:process.env,stdio:["ignore","inherit","inherit"]}),this.#h.on("error",a=>{console.error(`[${this.#p}] failed to start Harness:`,a.message)})}let o=Date.now()+6e4,i;for(;Date.now(){let r=A&&i.signal?AbortSignal.any([A,i.signal]):A??i.signal;return this.rpc("session.selectModel",{sessionId:t,provider:o.provider,model:o.model,...o.reasoningEffort===void 0?{}:{reasoningEffort:o.reasoningEffort}},3e4,r?{...i,signal:r}:i)},a=this.#I?await this.#I({sessionId:t,operation:e}):await e();if(!a||typeof a!="object"||!$_(a.selected))throw new Error("Harness returned an invalid response for session.selectModel");return a}async isSessionRunning(t,o={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");await this.ensureRunning(o);let i=await this.rpc("session.list",{},3e4,o);if(!i||typeof i!="object"||!Array.isArray(i.items))throw new Error("Harness returned an invalid response for session.list");for(let e of i.items)if(!e||typeof e!="object"||typeof e.sessionId!="string"||!e.sessionId||typeof e.running!="boolean")throw new Error("Harness returned an invalid response for session.list");return i.items.find(e=>e.sessionId===t)?.running??!1}async adoptWorkspaceSession(t,o={}){return x1(this,t,o)}async workspaceId(t={}){let{workspace:o=this.#t,...i}=t,{items:e}=await this.rpc("workspace.list",{},3e4,i),a=e.find(r=>r.path===o);return a?a.workspaceId:(await this.rpc("workspace.create",{path:o},3e4,i)).workspace.workspaceId}async createSession(t={}){let{agentPreset:o,...i}=t;await this.ensureRunning(i);let a={workspaceId:await this.workspaceId(i)},A=o!==void 0?o:this.#r;return A!=null&&(a.agentPreset=A),(await this.rpc("session.create",a,3e4,i)).sessionId}async renameSession(t,o,i={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");if(typeof o!="string"||!o.trim())throw new TypeError("session title is required");return this.rpc("session.rename",{sessionId:t,title:o},3e4,i)}async executeCommand(t,o,i={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");if(typeof o!="string"||!o)throw new TypeError("command line is required");if(!this.#c){let e=new Error("Harness command execution is unavailable");throw e.code="commands-unavailable",e}try{return await this.#c(t,o,i)}catch(e){throw e?.failure&&typeof e.failure=="object"?new mn("commands.execute",e.failure):e}}async readSessionHistory(t,{maxMessages:o=50,beforeSeq:i,timeoutMs:e=1e4,...a}={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");if(!Number.isSafeInteger(o)||o<1||i!==void 0&&(!Number.isSafeInteger(i)||i<0))throw new TypeError("Invalid history pagination");return this.rpc("session.history",{sessionId:t,maxMessages:o,...i===void 0?{}:{beforeSeq:i}},e,a)}async sessionExists(t,o={}){try{return await this.rpc("session.history",{sessionId:t,maxMessages:1},3e4,o),!0}catch(i){if(i instanceof mn&&i.code==="session-not-found")return!1;throw i}}async respondInteraction(t,o,i={}){if(typeof t!="string"||!t)throw new TypeError("rpcId is required");if(!o||typeof o!="object"||typeof o.ok!="boolean")throw new TypeError("A Harness RPC result is required");let e=AbortSignal.timeout(i.timeoutMs??3e4),a=i.signal?AbortSignal.any([i.signal,e]):e,A={type:"client-response",rpcId:t,result:o},r;if(this.#A)r=await j1(()=>this.#A.respond(A),a);else{let g=await this.#i(new URL("/api/respond",this.#e),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(A),signal:a});if(!g.ok)throw new Error(`Harness transport respond failed: HTTP ${g.status}`);r=await g.json()}if(r?.accepted===!0)return r;if(r?.accepted!==!1||r.reason!=="bad-response"&&r.reason!=="not-pending")throw new Error("Harness returned an invalid interaction response receipt");let s=r.reason;throw new xp(`interaction-${s}`,`Harness interaction response was rejected (${s})`)}async watchInteractions(t,{signal:o,onInteraction:i,onResolved:e,onOpen:a,ownership:A}={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");if(!o||typeof o.addEventListener!="function")throw new TypeError("watchInteractions requires an AbortSignal");if(i!==void 0&&typeof i!="function")throw new TypeError("onInteraction must be a function");if(e!==void 0&&typeof e!="function")throw new TypeError("onResolved must be a function");if(a!==void 0&&typeof a!="function")throw new TypeError("onOpen must be a function");for(;!o.aborted;){try{await this.#M(t,{signal:o,onInteraction:i,onResolved:e,onOpen:a,ownership:A})}catch(r){if(o.aborted)return;console.warn(`[${this.#p}] Harness interaction stream disconnected:`,r.message)}if(o.aborted)return;try{await Au(this.#n,o)}catch{if(o.aborted)return;throw new Error("Harness interaction reconnect wait failed")}}}#Q(t,o){let i=this.#C.get(t)??new Set;o.order=this.#u.nextOrder,this.#u.nextOrder+=1,i.add(o),this.#C.set(t,i)}#S(t,o){let i=this.#C.get(t);i?.delete(o),i?.size===0&&this.#C.delete(t);for(let[e,a]of this.#B)a.ownership===o&&this.#B.delete(e)}#y(t){if(!t.control)return;let o=this.#f.get(t.control.owner);o||(o=new Map,this.#f.set(t.control.owner,o));let i=o.get(t.control.key)??new Set;i.add(t),o.set(t.control.key,i)}#m(t){if(!t.control)return;let o=this.#f.get(t.control.owner),i=o?.get(t.control.key);i?.delete(t),i?.size===0&&o.delete(t.control.key),o?.size===0&&this.#f.delete(t.control.owner)}#D(t,o){let i=J1(o);return i?[...this.#f.get(i.owner)?.get(i.key)??[]].filter(a=>a.sessionId===t).sort((a,A)=>a.order-A.order):[]}#E(t,o){return this.#D(t,o).find(i=>i.started&&i.active&&!i.completed&&i.turn!==null)??null}async#R(t,o,i){let e=this.#D(t,o);if(e.length===0)return null;try{let a=await this.rpc("session.history",{sessionId:t,maxMessages:50},3e4,i);this.#_(t,a.events??[])}catch(a){if(!(a instanceof mn)||a.code!=="session-not-found")throw a;for(let A of e)A.active=!1,A.completed=!0;return null}return this.#E(t,o)}async hasActiveTurn(t,o,i={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");return!!await this.#R(t,o,i)}async stopActiveTurn(t,o,i={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");let e=await this.#R(t,o,i);if(!e)return!1;if(e.stopRequested)return!0;if(this.#E(t,o)!==e)return!1;e.stopRequested=!0;try{if(this.#g){let a=this.#g({sessionId:t,expectedTurn:e.turn,promptRpcId:e.promptRpcId,action:"stop"});if(a&&typeof a.then=="function")throw new TypeError("controlExecutor must return synchronously");if(a!==void 0){if(typeof a!="boolean")throw new TypeError("controlExecutor must return a boolean or undefined");return a||(e.stopRequested=!1),a}}return await this.rpc("session.cancel",{sessionId:t,keepInbox:!0},3e4,i),!0}catch(a){if(this.#E(t,o)===e&&(e.stopRequested=!1),a instanceof mn&&a.code==="session-not-found")return!1;throw a}}async steerActiveTurn(t,o,i,e={}){if(typeof t!="string"||!t)throw new TypeError("sessionId is required");if(typeof o!="string"||!o.trim())throw new TypeError("Steering text is required");let a=await this.#R(t,i,e);if(!a||a.stopRequested||this.#E(t,i)!==a)return!1;if(this.#g){let A=this.#g({sessionId:t,expectedTurn:a.turn,promptRpcId:a.promptRpcId,action:"steer",text:o});if(A&&typeof A.then=="function")throw new TypeError("controlExecutor must return synchronously");if(A!==void 0){if(typeof A!="boolean")throw new TypeError("controlExecutor must return a boolean or undefined");return A}}return await this.rpc("session.prompt",{sessionId:t,mode:"steer",content:[{type:"text",text:o}],clientTimeZone:Intl.DateTimeFormat().resolvedOptions().timeZone},3e4,e),!0}#_(t,o){for(let i of this.#C.get(t)??[])Z1(i,o)}async#O(t,o){let i=await this.rpc("session.history",{sessionId:t,maxMessages:50},3e4,{signal:o});this.#_(t,i.events??[])}#w(t,o,i){let e=this.#B.get(o);if(e&&this.#C.get(t)?.has(e.ownership))return e;let a=[...this.#C.get(t)??[]],A=a.filter(s=>s.active).sort((s,g)=>s.order-g.order);if(A.length>0)return{ownership:A[0],recovered:!1};let r=a.filter(s=>!s.started&&!s.completed).sort((s,g)=>s.order-g.order)[0]??null;return r?{ownership:r,recovered:!0}:null}async#T(t,o,i){if(!this.#d)throw new ko("inbound-file-ingress-unavailable","Harness file ingress is unavailable in this Host process.");let a=(await this.rpc("session.list",{},3e4,{signal:i}))?.items?.find(A=>A?.sessionId===t)?.cwd;return this.#d({sessionId:t,workspace:a,files:o,signal:i})}async ask(t,o,i={}){typeof i=="number"&&(i={timeoutMs:i});let e=i.timeoutMs??6e5,a=i.signal,A=typeof i.onUpdate=="function"?i.onUpdate:null,r=i.progressMode==="all"?"all":"latest",s=typeof i.onArtifact=="function"?i.onArtifact:null,g=typeof i.onInteraction=="function"?i.onInteraction:void 0,c=typeof i.onInteractionResolved=="function"?i.onInteractionResolved:void 0,I=J1(i.control),p=Array.isArray(i.files)?i.files.filter(Boolean):[];await this.ensureRunning({signal:a});let B=await this.rpc("session.history",{sessionId:t,maxMessages:1},3e4,{signal:a}),f=Math.max(-1,...(B.events??[]).map(({event:V})=>V.seq??-1)),Q=`${this.#s}-${X_()}`,y=new Kp({promptRpcId:Q,afterSeq:f}),m=g||c?new AbortController:null,T=m?a?AbortSignal.any([a,m.signal]):m.signal:null,_=m||I?{sessionId:t,promptRpcId:Q,active:!1,started:!1,completed:!1,stopRequested:!1,turn:null,openTurn:null,lastSeq:f,reconnect:null,order:-1,toolCalls:new Map,control:I}:null,R=null,x=!1,F=0,L=[],q=!1,z=!1,H=async()=>{if(!s||x||y.turn===null)return F;x=!0;let V=FI.take(t,y.turn,{signal:a});for(let U of V)try{await s(U),F+=1}catch(N){FI.release(U),console.warn(`[${this.#p}] ignored an artifact handoff failure:`,N.message)}return F};_&&(this.#Q(t,_),this.#y(_));let G=FI.openConsumer(t,Q);try{let V=o;if(p.length>0){let J=await this.#T(t,p,a);L.push(J),o=L_(o,J)}if(T){let J,X=new Promise(ie=>{J=ie});R=this.watchInteractions(t,{signal:T,onInteraction:g,onResolved:c,onOpen:J,ownership:_}),R.catch(()=>{}),await Promise.race([X,Au(3e4,T).then(()=>{throw new Error("Harness interaction stream did not open within 30 seconds")})])}let U=typeof o=="string"?[{type:"text",text:o}]:o;if(!Array.isArray(U)||U.length===0)throw new TypeError("Harness prompt content is required");let N=Intl.DateTimeFormat().resolvedOptions().timeZone,k=J=>this.rpc("session.prompt",{sessionId:t,mode:"queue",content:J,clientTimeZone:N},3e4,{rpcId:Q,signal:a});try{await k(U)}catch(J){let X=Wx(J)?zx(U):[];if(X.length===0)throw J;let ie;try{ie=await this.#T(t,X,a)}catch(Se){throw a?.aborted?a.reason??Se:(console.warn(`[${this.#p}] unable to restage rejected images as workspace files:`,Se?.message??String(Se)),J)}L.push(ie);let oe=L_([...Vx(typeof V=="string"?[{type:"text",text:V}]:V),{type:"text",text:w(Jx)}],{files:L.flatMap(Se=>Se?.files??[])});await k(oe)}q=!0;try{let J=Date.now()+e;for(;Date.now()0)return"";throw _?.stopRequested?z1():V1(y.reason)}throw new ss("harness-reply-timeout")}catch(J){if(!_?.stopRequested)throw J;if(y.answer)return y.answer;throw J?.code==="turn-stopped"?J:z1()}}finally{if(!q||z)for(let V of L)try{await V?.cleanup?.()}catch(U){console.warn(`[${this.#p}] unable to clean inbound files:`,U.message)}G(),_&&(this.#m(_),this.#S(t,_)),y.turn!==null&&FI.discard(t,y.turn),m?.abort(new DOMException("Harness turn finished","AbortError")),R&&await R.catch(()=>{})}}async#M(t,{signal:o,onInteraction:i,onResolved:e,onOpen:a,ownership:A}){let r=!1,s=null,g=Promise.resolve(),c=A==null,I=[],p=()=>{},B=m=>{p=m,A&&(A.reconnect=p);try{a?.()}catch(T){console.warn(`[${this.#p}] ignored an interaction open callback failure:`,T.message)}A&&this.#O(t,o).then(()=>{if(!r){c=!0;for(let T of I.splice(0))Q(T)}}).catch(T=>{s??=T,p()})},f=(m,T)=>{m&&(g=g.then(()=>m(T)).catch(_=>{s??=_,p()}))},Q=m=>{let T=m.payload;if(A&&T.type==="session/event"){this.#_(t,[T.event]);return}if(T.type==="question/requested"||T.type==="approval/requested"){let _=T.type==="question/requested"?"question":"approval",R=_==="question"?m.rpcId:T.approvalId,x=`${_}:${R}`;if(A){let L=this.#w(t,x,_);if(L?.ownership!==A)return;this.#B.set(x,L)}let F=_==="approval"&&A&&typeof T.callId=="string"?this.#B.get(x)?.ownership.toolCalls.get(T.callId):void 0;f(i,Object.freeze({kind:_,interactionId:R,rpcId:m.rpcId,sessionId:t,payload:T,recovered:A?this.#B.get(x)?.recovered===!0:!1,...F?{toolCall:F}:{},reconnect:p,respond:(L,q={})=>this.respondInteraction(m.rpcId,L,{...q,signal:q.signal??o})}));return}if(T.type==="question/resolved"||T.type==="approval/resolved"){let _=T.type==="question/resolved"?"question":"approval",R=_==="question"?T.questionRpcId:T.approvalId,x=`${_}:${R}`;if(A){if(this.#B.get(x)?.ownership!==A)return;this.#B.delete(x)}f(e,Object.freeze({kind:_,interactionId:R,sessionId:t,outcome:T.outcome,payload:T}))}},y=m=>{try{let T=m?.payload;if(m?.type!=="server-request"||typeof m.rpcId!="string"||!T||typeof T!="object"||m.method!==T.type)throw new Error("invalid server-request envelope");if(T.sessionId!==t)return;c?Q(m):I.push(m)}catch(T){console.warn(`[${this.#p}] ignored a malformed Harness interaction frame:`,T.message)}};try{await this.#P({signal:o,onOpen:B,onEnvelope:y})}finally{r=!0,A?.reconnect===p&&(A.reconnect=null),o.aborted||await g}if(!o.aborted&&s)throw s}async watchHarnessEvents({signal:t,onSessionEvent:o,onReconnect:i}={}){if(typeof o!="function")throw new TypeError("watchHarnessEvents requires onSessionEvent");if(!t||typeof t.addEventListener!="function")throw new TypeError("watchHarnessEvents requires an AbortSignal");if(i!==void 0&&typeof i!="function")throw new TypeError("onReconnect must be a function");for(;!t.aborted;){try{await this.#P({signal:t,onOpen:()=>{try{i?.()}catch(e){console.warn(`[${this.#p}] mux reconnect hook failed:`,e.message)}},onEnvelope:e=>{try{let a=e?.payload;if(e?.type!=="server-request"||!a||typeof a!="object"||e.method!==a.type||a.type!=="session/event"||typeof a.sessionId!="string"||!a.event||typeof a.event!="object")return;o({sessionId:a.sessionId,event:a.event})}catch(a){console.warn(`[${this.#p}] ignored a malformed global mux frame:`,a.message)}}})}catch(e){if(t.aborted)return;console.warn(`[${this.#p}] Harness event mux disconnected:`,e.message)}if(t.aborted)return;try{await Au(this.#n,t)}catch{if(t.aborted)return;throw new Error("Harness event mux reconnect wait failed")}}}#P(t){return Y1({apiProxy:this.#A,baseUrl:this.#e,createWebSocket:this.#a,rpcId:`${this.#s}-${X_()}`,...t,onMalformed:o=>{console.warn(`[${this.#p}] ignored a malformed Harness mux frame:`,o.message)}})}stopManagedProcess(){this.#h?.exitCode===null&&this.#h.kill("SIGTERM")}};var JQ=class extends Qr{constructor(t){super({...t,rpcIdPrefix:"dingtalk",logPrefix:"dsh-dingtalk"})}};import{mkdir as mCe,readFile as SCe,realpath as wCe,rename as DCe,stat as OCe,unlink as bCe,writeFile as MCe}from"node:fs/promises";import{dirname as _Ce,isAbsolute as A3,resolve as ru}from"node:path";var itA=Object.freeze({version:1,workspaces:Object.freeze({})});function LI(n){let t=new Error(n);return t.code=Sa,t}async function AT(n){return ru(await wCe(n))}async function TCe(n,t){if(n===t)return!0;try{return await AT(n)===await AT(t)}catch{return!1}}function jt(n){if(typeof n!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(n))throw new TypeError("Invalid bot id");return n}function pi(n,t,o){let i=new Error(t,o);return i.code=n,i}function qQ(n){if(typeof n!="string"||!/^[A-Za-z0-9._:@-]{1,128}$/.test(n))throw pi("invalid-target","Invalid target id");return n}function NCe(n){if(!n||typeof n!="object"||Array.isArray(n))throw pi("invalid-target","Invalid target route");try{let t=JSON.stringify(n);if(t===void 0)throw new TypeError("route is not JSON");let o=JSON.parse(t);if(!o||typeof o!="object"||Array.isArray(o))throw new TypeError("route is not an object");return o}catch(t){throw pi("invalid-target","Invalid target route",{cause:t})}}function UI(n,{targetId:t}={}){if(!n||typeof n!="object"||Array.isArray(n))throw pi("invalid-target","Invalid delivery target");let o=t===void 0?new Set(["targetId","name","kind","route"]):new Set(["name","kind","route"]);if(Object.keys(n).some(a=>!o.has(a)))throw pi("invalid-target","Invalid delivery target");let i=qQ(t??n.targetId);if(typeof n.kind!="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(n.kind))throw pi("invalid-target","Invalid target kind");let e;if(n.name!==void 0){if(typeof n.name!="string"||!n.name.trim()||n.name.trim().length>80)throw pi("invalid-target","Invalid target name");e=n.name.trim()}return{targetId:i,...e===void 0?{}:{name:e},kind:n.kind,route:NCe(n.route)}}function RCe(n){let t=Object.create(null);if(n===void 0)return t;if(!n||typeof n!="object"||Array.isArray(n))return null;try{for(let[o,i]of Object.entries(n)){if(jt(o),!i||typeof i!="object"||Array.isArray(i))return null;let e=Object.create(null);for(let[a,A]of Object.entries(i)){let r=UI(A,{targetId:a}),{targetId:s,...g}=r;e[a]=g}t[o]=e}}catch{return null}return t}function kCe(n,t){let o=Object.create(null);if(n===void 0)return o;if(!n||typeof n!="object"||Array.isArray(n)){for(let i of Object.keys(t))o[i]=null;return o}for(let[i,e]of Object.entries(n))try{jt(i),o[i]=Hp(e)}catch{}return o}function vCe(n){if(!n||![1,2].includes(n.version)||!n.workspaces||typeof n.workspaces!="object"||Array.isArray(n.workspaces))return null;let t={};for(let[r,s]of Object.entries(n.workspaces)){if(!/^[A-Za-z0-9_-]{1,128}$/.test(r)||typeof s!="string"||!A3(s))return null;t[r]=ru(s)}let o={};if(n.agentPresets!==void 0){if(!n.agentPresets||typeof n.agentPresets!="object"||Array.isArray(n.agentPresets))return null;for(let[r,s]of Object.entries(n.agentPresets)){if(!/^[A-Za-z0-9_-]{1,128}$/.test(r))return null;try{let g=vp(s);if(!g)return null;o[r]=g}catch{return null}}}let i=Object.create(null);if(n.contextEnhancement&&typeof n.contextEnhancement=="object"&&!Array.isArray(n.contextEnhancement))for(let[r,s]of Object.entries(n.contextEnhancement))/^[A-Za-z0-9_-]{1,128}$/.test(r)&&(i[r]=Gp(s));if(n.version===1&&n.deliveryTargets!==void 0)return null;let e=RCe(n.deliveryTargets);if(!e)return null;let a=kCe(n.accessPolicies,t);return{version:n.accessPolicies===void 0?n.version:2,workspaces:t,agentPresets:o,contextEnhancement:i,deliveryTargets:e,accessPolicies:a}}function PCe({version:n,workspaces:t,agentPresets:o,contextEnhancement:i,deliveryTargets:e,accessPolicies:a}){let A={version:n,workspaces:t};return Object.keys(o).length>0&&(A.agentPresets=o),Object.keys(i).length>0&&(A.contextEnhancement=i),n>=2&&Object.keys(e).length>0&&(A.deliveryTargets=e),n>=2&&Object.keys(a).length>0&&(A.accessPolicies=a),A}async function GCe(n,t){await mCe(_Ce(n),{recursive:!0,mode:448});let o=`${n}.tmp`;await MCe(o,`${JSON.stringify(t,null,2)} `,{encoding:"utf8",mode:384}),await DCe(o,n)}async function $1(n){if(typeof n!="string"||!n.trim()||!A3(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 t=ru(n.trim()),o;try{o=await OCe(t)}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 t}var oa=class{#e;#A;#t=1;#r={};#l={};#o={};#i=Object.create(null);#a=Object.create(null);#n=new Map;#s=1;#p=new Map;#c=1;#g=new Map;#I=new WeakMap;#d=new Set;#h=Promise.resolve();#u=new Map;constructor(t,{defaultWorkspace:o=process.cwd()}={}){if(typeof t!="string"||!t)throw new TypeError("workspace store path is required");this.#e=t,this.#A=ru(o)}async load(){try{let t=vCe(JSON.parse(await SCe(this.#e,"utf8")));if(!t)throw new Error("dsh-im workspace config is invalid");this.#t=t.version,this.#r=t.workspaces,this.#l=t.agentPresets,this.#o=t.contextEnhancement,this.#i=t.deliveryTargets,this.#a=t.accessPolicies}catch(t){if(t?.code!=="ENOENT")throw t;this.#t=1,this.#r={},this.#l={},this.#o={},this.#i=Object.create(null),this.#a=Object.create(null)}this.#n.clear(),this.#s=1,this.#p.clear(),this.#c=1,this.#g.clear(),this.#d.clear();for(let t of Object.keys(this.#r))this.#n.set(t,this.#C()),this.#p.set(t,this.#B());return this}has(t){let o=jt(t);return Object.hasOwn(this.#r,o)&&!this.#g.has(o)}incarnationFor(t){return this.#p.get(jt(t))??null}workspaceFor(t){return this.#r[jt(t)]??this.#A}agentPresetFor(t){return this.#l[jt(t)]??null}contextEnhancementFor(t){let o=jt(t);return this.has(o)&&Object.hasOwn(this.#o,o)?this.#o[o]:v_}accessPolicyFor(t){let o=jt(t);return this.has(o)&&Object.hasOwn(this.#a,o)?this.#a[o]:null}listDeliveryTargets(t){let o=jt(t);if(!this.has(o))throw pi("unknown-bot","Unknown bot");return Object.entries(this.#i[o]??{}).map(([i,e])=>UI(e,{targetId:i})).sort((i,e)=>i.targetId.localeCompare(e.targetId))}deliveryTargetFor(t,o){let i=jt(t),e=qQ(o);if(!this.has(i))throw pi("unknown-bot","Unknown bot");let a=this.#i[i]?.[e];return a?UI(a,{targetId:e}):null}async createDeliveryTarget(t,o){let i=jt(t),e=UI(o);return this.#S(i,async()=>{if(!this.has(i))throw pi("unknown-bot","Unknown bot");if(Object.hasOwn(this.#i[i]??{},e.targetId))throw pi("target-conflict","Target already exists");let{targetId:a,...A}=e,r={...this.#i,[i]:{...this.#i[i]??{},[a]:A}};return await this.#y(this.#o,r,2),this.#i=r,this.#t=2,UI(A,{targetId:a})})}async updateDeliveryTarget(t,o,i){let e=jt(t),a=qQ(o),A=UI(i,{targetId:a});return this.#S(e,async()=>{if(!this.has(e))throw pi("unknown-bot","Unknown bot");if(!Object.hasOwn(this.#i[e]??{},a))throw pi("unknown-target","Unknown target");let{targetId:r,...s}=A,g={...this.#i,[e]:{...this.#i[e],[a]:s}};return await this.#y(this.#o,g,2),this.#i=g,this.#t=2,UI(s,{targetId:a})})}async deleteDeliveryTarget(t,o){let i=jt(t),e=qQ(o);return this.#S(i,async()=>{if(!this.has(i))throw pi("unknown-bot","Unknown bot");if(!Object.hasOwn(this.#i[i]??{},e))throw pi("unknown-target","Unknown target");let a={...this.#i[i]};delete a[e];let A={...this.#i};return Object.keys(a).length>0?A[i]=a:delete A[i],await this.#y(this.#o,A,2),this.#i=A,this.#t=2,!0})}generationFor(t){return this.#n.get(jt(t))??null}async whenIdle(){await this.#h}async whenBotIdle(t){let o=jt(t);for(;;){let i=this.#u.get(o);if(!i||(await i,this.#u.get(o)===i))return}}async ensure(t,{workspace:o=this.#A,defaultAgentPreset:i,initialAccessPolicy:e}={}){let a=jt(t),A=ru(o);return this.#S(a,async()=>{let r=!this.#r[a],s=e!==void 0&&!Object.hasOwn(this.#a,a);if(r||s){let g=s?dc(e):void 0,c=r?vp(i):null,I=Object.hasOwn(this.#l,a),p=this.#l[a],B=s?{...this.#a,[a]:g}:this.#a;r&&(this.#r[a]=A,c&&(this.#l[a]=c),this.#n.set(a,this.#C()),this.#p.set(a,this.#B()));let f=s?2:this.#t;try{await this.#y(this.#o,this.#i,f,B)}catch(Q){throw r&&(delete this.#r[a],I?this.#l[a]=p:delete this.#l[a],this.#n.delete(a),this.#p.delete(a)),Q}this.#a=B,this.#t=f}else this.#n.has(a)||this.#n.set(a,this.#C());return this.#r[a]})}async setWorkspace(t,o,{clearSessions:i,incarnation:e}={}){let a=jt(t);if(!this.has(a)||e!==void 0&&e!==this.incarnationFor(a)){let r=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw r.code="workspace-bot-not-found",r}let A=await $1(o);return this.#S(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 r=this.#r[a];this.#n.set(a,this.#C()),await i?.(),this.#r[a]=A;try{await this.#y()}catch(s){throw this.#r[a]=r,s}return A})}async setAgentPreset(t,o,{incarnation:i}={}){let e=jt(t);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=vp(o);return this.#S(e,async()=>{if(!this.has(e)||i!==void 0&&i!==this.incarnationFor(e)){let r=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw r.code="workspace-bot-not-found",r}let A=this.#l[e]??null;if(A===a)return a;a?this.#l[e]=a:delete this.#l[e];try{await this.#y()}catch(r){throw A?this.#l[e]=A:delete this.#l[e],r}return a})}async setContextEnhancement(t,o,{incarnation:i}={}){let e=jt(t),a=i===void 0?this.incarnationFor(e):i,A=GI(o);return this.#S(e,async()=>{if(!this.has(e)||a!==this.incarnationFor(e)){let s=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw s.code="workspace-bot-not-found",s}let r={...this.#o,[e]:A};return await this.#y(r),this.#o=r,A})}async setAccessPolicy(t,o,{incarnation:i}={}){let e=jt(t),a=i===void 0?this.incarnationFor(e):i,A=dc(o);return this.#S(e,async()=>{if(!this.has(e)||a!==this.incarnationFor(e)){let s=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw s.code="workspace-bot-not-found",s}let r={...this.#a,[e]:A};return await this.#y(this.#o,this.#i,2,r),this.#a=r,this.#t=2,A})}async bindWorkspaceSession(t,o,{conversationKey:i,sessionId:e,clearSessions:a,setSession:A,incarnation:r,expectedGeneration:s}={}){let g=jt(t);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)||r!==void 0&&r!==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 AT(await $1(o));return this.#S(g,async()=>{if(!this.has(g)||r!==void 0&&r!==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 LI("The bot workspace changed before the session binding could be committed.");if(!await TCe(c,this.workspaceFor(g))){let I=this.#r[g];this.#n.set(g,this.#C()),await a(),this.#r[g]=c;try{await this.#y()}catch(p){throw this.#r[g]=I,p}}return await A(i,e),{workspace:c,sessionId:e,generation:this.#n.get(g)}})}async invalidateSessions(t,{clearSessions:o}={}){let i=jt(t);return this.#S(i,async()=>{this.#n.set(i,this.#C()),await o?.()})}async beginRemoval(t,{clearSessions:o}={}){let i=jt(t);return this.#S(i,async()=>{let e=this.#g.get(i);if(e)return e;let a=Object.freeze({});this.#g.set(i,a),this.#I.set(a,{botId:i,incarnation:this.incarnationFor(i)}),this.#n.set(i,this.#C());try{await o?.()}catch(A){throw this.#g.get(i)===a&&this.#g.delete(i),A}return a})}async abortRemoval(t){let{botId:o}=this.#f(t);return this.#S(o,async()=>this.#g.get(o)!==t?!1:(this.#g.delete(o),Object.hasOwn(this.#r,o)&&(this.#n.set(o,this.#C()),this.#p.has(o)||this.#p.set(o,this.#B())),!0))}async finishRemoval(t){let{botId:o,incarnation:i}=this.#f(t);return this.#S(o,async()=>this.#g.get(o)!==t?{removed:!1,persisted:!0,error:null,stale:!0}:this.incarnationFor(o)!==i?(this.#g.delete(o),{removed:!1,persisted:!0,error:null,stale:!0}):(this.#g.delete(o),this.#Q(o)))}async retireAfterConfigCommit(t){let o=jt(t);return this.#S(o,async()=>(this.#g.delete(o),this.#Q(o)))}flushPendingRemoval(t){if(this.#d.has(t))return this.#S(t,async()=>{this.#d.has(t)&&await this.#m()})}async remove(t){let o=await this.retireAfterConfigCommit(t);if(o.error)throw o.error;return o.removed}async reconcile(t){let o=new Set([...t].map(jt)),i=new Set([...Object.keys(this.#r),...Object.keys(this.#l),...Object.keys(this.#o),...Object.keys(this.#i),...Object.keys(this.#a),...this.#d]);for(let e of i)o.has(e)||await this.remove(e)}decorateStatus(t){return!t||typeof t!="object"||!Array.isArray(t.bots)?t:{...t,bots:t.bots.map(o=>o?.botId?{...o,workspace:this.workspaceFor(o.botId),agentPreset:this.agentPresetFor(o.botId),contextEnhancement:this.contextEnhancementFor(o.botId),accessPolicy:this.accessPolicyFor(o.botId)}:o)}}#C(){let t=this.#s;return this.#s+=1,t}#B(){let t=this.#c;return this.#c+=1,t}#f(t){if(!t||typeof t!="object")throw new TypeError("Invalid workspace removal transaction");let o=this.#I.get(t);if(!o)throw new TypeError("Invalid workspace removal transaction");return o}async#Q(t){let o=Object.hasOwn(this.#r,t),i=Object.hasOwn(this.#l,t),e=Object.hasOwn(this.#o,t),a=Object.hasOwn(this.#i,t),A=Object.hasOwn(this.#a,t),r=o||i||e||a||A||this.#d.has(t);if(delete this.#r[t],delete this.#l[t],delete this.#o[t],delete this.#i[t],delete this.#a[t],this.#n.delete(t),this.#p.delete(t),!r)return{removed:!1,persisted:!0,error:null,stale:!1};try{return await this.#m(),{removed:o,persisted:!0,error:null,stale:!1}}catch(s){return this.#d.add(t),{removed:o,persisted:!1,error:s,stale:!1}}}async#S(t,o){let i=this.#h.then(o,o),e=i.then(()=>{},()=>{});return this.#h=e,this.#u.set(t,e),e.finally(()=>{this.#u.get(t)===e&&this.#u.delete(t)}),i}async#y(t=this.#o,o=this.#i,i=this.#t,e=this.#a){await GCe(this.#e,PCe({version:i,workspaces:this.#r,agentPresets:this.#l,contextEnhancement:t,deliveryTargets:o,accessPolicies:e})),this.#d.clear()}async#m(){if(Object.keys(this.#r).length>0||Object.keys(this.#l).length>0||Object.keys(this.#o).length>0||Object.keys(this.#i).length>0||Object.keys(this.#a).length>0){await this.#y();return}try{await bCe(this.#e),this.#d.clear()}catch(t){if(t?.code!=="ENOENT")throw t;this.#d.clear()}}};function tu(n){if(!n)return null;let t=typeof n=="function"?n():n;return t&&typeof t.then=="function"?t.then(rs):rs(t)}function t3(){let n=new Error("Agent Preset \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u7528\u3002");return n.code="agent-preset-unavailable",n}function Yp(n){if(n())return;let t=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw t.code="workspace-bot-not-found",t}function e3(n,t,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,r=tu(o);return r&&typeof r.then=="function"?r.then(A):A(r)};return t&&typeof t.then=="function"?t.then(i):i(t)}function FCe(n){return Promise.resolve(n.status())}function ja(n,{workspaces:t,method:o="remove",botIdFromRemoved:i=A=>A?.botId,saveMethod:e="save",botIdFromSave:a=A=>A?.botId}){if(!n||!t||typeof n[o]!="function")throw new TypeError("configStore removal observer dependencies are required");return new Proxy(n,{get(A,r){let s=Reflect.get(A,r,A);return r===o?async(...g)=>{let c=await s.apply(A,g),I=c?i(c,g):null;return I&&await t.retireAfterConfigCommit(I),c}:r===e&&typeof s=="function"?(...g)=>{let c=t.flushPendingRemoval(a(g[0],g));return c?c.then(()=>s.apply(A,g)):s.apply(A,g)}:typeof s=="function"?s.bind(A):s}})}function Ja(n,{botId:t,workspaces:o,state:i,agentPresetCatalog:e}={}){if(!n||!o||!i)throw new TypeError("harness, workspaces, and state are required");let a=o.incarnationFor(t),A=()=>o.has(t)&&o.incarnationFor(t)===a,r=async(I=e)=>{let p;try{p=await tu(I)??rs(null)}catch(B){throw Yp(A),B}return Yp(A),{agentPreset:o.agentPresetFor(t),agentPresetCatalog:p}},s=new Map,g=new Proxy(n,{get(I,p){if(p==="agentPresetSettings")return async(f={})=>{f?.signal?.throwIfAborted(),Yp(A);let Q=await r();return f?.signal?.throwIfAborted(),Q};if(p==="updateAgentPreset")return async(f,Q={})=>{Q?.signal?.throwIfAborted(),Yp(A);let y=f==="--default"?null:vp(f),m=null;if(y&&({agentPresetCatalog:m}=await r(),Q?.signal?.throwIfAborted(),!m.items.some(T=>T.id===y)))throw t3();if(await o.setAgentPreset(t,y,{incarnation:a}),Yp(A),m)return{agentPreset:o.agentPresetFor(t),agentPresetCatalog:m};try{return await r()}catch(T){if(T?.code==="workspace-bot-not-found")throw T;return Yp(A),{agentPreset:o.agentPresetFor(t),agentPresetCatalog:rs(null)}}};if(p==="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(t)};if(p==="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((p==="listWorkspaces"||p==="listWorkspaceSessions"||p==="listModels")&&typeof I[p]=="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[p](...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(p==="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(t,f,{clearSessions:()=>i.clearSessions(),incarnation:a})};if(p==="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 _=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw _.code="workspace-bot-not-found",_}if(typeof I.adoptWorkspaceSession!="function")throw new TypeError("Harness does not support adopting workspace sessions");let y=o.generationFor(t),m=await I.adoptWorkspaceSession(Q);if(!A()){let _=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw _.code="workspace-bot-not-found",_}if(y!==o.generationFor(t))throw LI("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 T=await o.bindWorkspaceSession(t,m.workspace,{conversationKey:f,sessionId:Q,clearSessions:()=>i.clearSessions(),setSession:(_,R)=>i.setSession(_,R),incarnation:a,expectedGeneration:y});if(!A()){let _=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw _.code="workspace-bot-not-found",_}if(T.generation!==o.generationFor(t))throw LI("The bot workspace changed before the session binding completed.");return s.set(Q,T.generation),{...m,workspace:T.workspace,sessionId:T.sessionId}};if(p==="createSession")return async(f={})=>{if(await o.whenBotIdle(t),!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 Q=o.generationFor(t),y=o.agentPresetFor(t),m=await I.createSession({...f,workspace:o.workspaceFor(t),...y==null?{}:{agentPreset:y}});return s.set(m,Q),m};if(p==="workspaceSession")return f=>{if(typeof f!="string"||!f)throw new TypeError("sessionId is required");let Q=s.get(f)??o.generationFor(t);s.delete(f);let y=()=>A()&&Q===o.generationFor(t),m=async(_,R,x)=>{if(!y())throw LI(`The bot workspace changed before this ${x} started.`);let F=await I[_](f,...R);if(!y())throw LI(`The bot workspace changed while this ${x} was running.`);return F},T=async(_,R,x)=>{if(!y())throw LI(`The bot workspace changed before this ${x} started.`);return I[_](f,...R)};return Object.freeze({sessionId:f,async sessionExists(..._){if(!y())return!1;let R=await I.sessionExists(f,..._);return y()&&R},models(..._){return m("getSessionModels",_,"model listing")},readHistory(..._){return m("readSessionHistory",_,"history read")},...typeof I.renameSession=="function"?{renameTitle(..._){return T("renameSession",_,"title rename")}}:{},selectModel(..._){return m("selectSessionModel",_,"model selection")},isRunning(..._){return m("isSessionRunning",_,"run-state check")},hasActiveTurn(..._){return m("hasActiveTurn",_,"turn ownership check")},stopActiveTurn(..._){return T("stopActiveTurn",_,"turn stop")},steerActiveTurn(..._){return T("steerActiveTurn",_,"turn steering")},ask(..._){if(!y())throw LI("The bot workspace changed before this prompt started.");return I.ask(f,..._)}})};if(p==="sessionExists")return(f,...Q)=>{if(!A())return!1;let y=s.get(f);return y!==void 0&&y!==o.generationFor(t)?(s.delete(f),!1):I.sessionExists(f,...Q)};if(p==="ask")return(f,...Q)=>{let y=s.get(f);if(s.delete(f),!A()||y!==void 0&&y!==o.generationFor(t)){let m=new Error("The bot workspace changed before this prompt started.");throw m.code=Sa,m}return I.ask(f,...Q)};if(p==="executeCommand"&&typeof I.executeCommand=="function")return(f,...Q)=>{let y=s.get(f);if(s.delete(f),!A()||y!==void 0&&y!==o.generationFor(t)){let m=new Error("The bot workspace changed before this command started.");throw m.code=Sa,m}return I.executeCommand(f,...Q)};let B=Reflect.get(I,p,I);return typeof B=="function"?B.bind(I):B}}),c=new Proxy(i,{get(I,p){if(p===a_)return I;if(p==="sessionFor")return(f,...Q)=>{if(!A())return null;let y=I.sessionFor(f,...Q);return y&&!s.has(y)&&s.set(y,o.generationFor(t)),y};if(p==="setSession")return(f,Q,...y)=>{let m=s.get(Q);return!A()||m!==void 0&&m!==o.generationFor(t)?(s.delete(Q),!1):I.setSession(f,Q,...y)};let B=Reflect.get(I,p,I);return typeof B=="function"?B.bind(I):B}});return Object.freeze({harness:g,state:c})}function qa(n,{workspaces:t,stateFor:o,agentPresetCatalog:i}={}){if(!n||!t||typeof o!="function")throw new TypeError("controller, workspaces, and stateFor are required");let e=new Map,a=(p,B)=>{let Q=(e.get(p)??Promise.resolve()).catch(()=>{}).then(B);return e.set(p,Q),Q.finally(()=>{e.get(p)===Q&&e.delete(p)})},A=p=>e3(t,p,i),r=(p,B)=>{let f=t.incarnationFor(p);return a(p,async()=>{if(!(await n.status())?.bots?.some(m=>m?.botId===p)){let m=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw m.code="workspace-bot-not-found",m}let y=await o(p);return await t.setWorkspace(p,B,{clearSessions:()=>y.clearSessions(),incarnation:f}),A(await n.status())})},s=(p,B)=>{let f=t.incarnationFor(p),Q=vp(B);return a(p,async()=>{if(!(await n.status())?.bots?.some(T=>T?.botId===p)){let T=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw T.code="workspace-bot-not-found",T}let m=Q&&i?await tu(i):null;if(Q&&i&&!m?.items.some(T=>T.id===Q))throw t3();return await t.setAgentPreset(p,Q,{incarnation:f}),e3(t,await n.status(),m??i)})},g=(p,B,f)=>{let Q=t.incarnationFor(p),y=GI(B);return a(p,async()=>{let m=await n.status();if(!m?.bots?.some(F=>F?.botId===p)){let F=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw F.code="workspace-bot-not-found",F}let T=await tu(i),_=t.decorateStatus(m),R={..._,bots:_.bots.map(F=>F?.botId===p?{...F,contextEnhancement:y}:F),...T?{agentPresetCatalog:T}:{}},x=f?await f(R):R;return await t.setContextEnhancement(p,y,{incarnation:Q}),x})},c=(p,B,f)=>{let Q=t.incarnationFor(p),y=dc(B);return a(p,async()=>{let m=await n.status();if(!m?.bots?.some(F=>F?.botId===p)){let F=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw F.code="workspace-bot-not-found",F}let T=await tu(i),_=t.decorateStatus(m),R={..._,bots:_.bots.map(F=>F?.botId===p?{...F,accessPolicy:y}:F),...T?{agentPresetCatalog:T}:{}},x=f?await f(R):R;return await t.setAccessPolicy(p,y,{incarnation:Q}),x})},I=(p,B)=>a(p,async()=>{let f=await t.beginRemoval(p,{clearSessions:async()=>{try{let Q=await o(p);if(!Q||typeof Q.clearSessions!="function")throw new TypeError("bot state does not support session cleanup");await Q.clearSessions()}catch(Q){console.warn(`[dsh-im] ignored session cleanup failure while deleting bot ${p}:`,Q?.message??Q)}}});try{let Q=await B();return await t.finishRemoval(f),A(Q)}catch(Q){let y=await FCe(n).catch(()=>null);throw Array.isArray(y?.bots)&&!y.bots.some(T=>T?.botId===p)?await t.finishRemoval(f):await t.abortRemoval(f),Q}});return new Proxy(n,{get(p,B){if(B==="updateWorkspace")return r;if(B==="updateAgentPreset")return s;if(B==="updateContextEnhancement")return g;if(B==="updateAccessPolicy")return c;let f=Reflect.get(p,B,p);return typeof f!="function"?f:B==="deleteBot"?(Q,...y)=>I(Q,()=>f.call(p,Q,...y)):B==="disconnect"?async(...Q)=>{let m=(await p.status())?.bots?.[0]?.botId;return m?I(m,()=>f.apply(p,Q)):A(f.apply(p,Q))}:(...Q)=>A(f.apply(p,Q))}})}var r3=new Set(["weixin","feishu","dingtalk","wecom","qq","slack","telegram","discord","whatsapp"]);function LCe(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}function UCe(n){return typeof n=="string"&&n.length>0&&n.length<=512&&n.trim()===n&&!/[\s:\u0000-\u001f\u007f]/u.test(n)}function tT(n,t){let o=`${t}:`;if(!n.startsWith(o))return null;let i=n.slice(o.length);return UCe(i)?i:null}function au(n,t){for(let[o,i,e]of t){let a=tT(n,o);if(a)return{kind:i,route:{[e]:a}}}return null}function HCe(n){let t=tT(n,"p2p");return t?.startsWith("ou_")?{kind:"user",route:{openId:t}}:au(n,[["group","group","chatId"]])}function KCe(n){let t=tT(n,"direct");if(t&&/^[A-Za-z0-9_-]{1,128}$/.test(t))return{kind:"conversation",route:{channelId:t}};let o=/^group:([^:]+):(\d{1,20}(?:\.\d{1,20})?)$/.exec(n);return!o||!/^[A-Za-z0-9_-]{1,128}$/.test(o[1])?null:{kind:"thread",route:{channelId:o[1],threadTs:o[2]}}}function xCe(n){let t=/^(direct|group):(-?\d+)(?::([1-9]\d*))?$/.exec(n);if(!t)return null;let o=Number(t[2]);if(!Number.isSafeInteger(o)||t[1]==="direct"&&t[3]!==void 0)return null;if(t[3]===void 0)return{kind:"chat",route:{chatId:t[2]}};let i=Number(t[3]);return!Number.isSafeInteger(i)||i<=0?null:{kind:"topic",route:{chatId:t[2],messageThreadId:i}}}function YCe(n){let t=/^(?:direct|group):(\d{1,32})$/.exec(n);return t?{kind:"channel",route:{channelId:t[1]}}:null}function jCe(n){let t=/^direct:(\d{5,32}@(s\.whatsapp\.net|lid))$/.exec(n);if(t)return{kind:"user",route:{jid:t[1]}};let o=/^group:(\d{5,32}(?:-\d{1,32})?@g\.us)$/.exec(n);return o?{kind:"group",route:{jid:o[1]}}:null}function JCe(n,t){if(!r3.has(n)||typeof t!="string")return null;switch(n){case"weixin":return au(t,[["p2p","user","toUserId"]]);case"feishu":return HCe(t);case"dingtalk":return au(t,[["p2p","user","userId"],["group","group","openConversationId"]]);case"wecom":return au(t,[["direct","user","chatId"],["group","group","chatId"]]);case"qq":return au(t,[["c2c","user","userOpenId"],["group","group","groupOpenId"]]);case"slack":return KCe(t);case"telegram":return xCe(t);case"discord":return YCe(t);case"whatsapp":return jCe(t);default:return null}}function a3(n,t){if(!r3.has(n)||!LCe(t))return[];let o=[],i=new Set;for(let e of Object.keys(t)){let a=JCe(n,e);if(!a)continue;let A=JSON.stringify(a);i.has(A)||(i.add(A),o.push(a))}return o}var qCe=new Set(["weixin","feishu","dingtalk","wecom","qq","slack","telegram","discord","whatsapp"]);function VQ(n){return qCe.has(n)}function Ec(n){let t=new Error(n);return t.code="invalid-target",t}function zCe(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}function rT(n,t,o){if(!zCe(n))throw Ec(`${o} must be an object`);if(Object.keys(n).find(e=>!t.includes(e))!==void 0)throw Ec(`${o} contains an unknown field`)}function zQ(n,t){if(typeof n!="string"||!n||n.trim()!==n)throw Ec(`${t} must be a non-empty string without surrounding whitespace`);return n}function VCe(n){if(typeof n!="string"||!n.trim()||n.trim().length>80)throw Ec("target.name must contain 1 to 80 characters");return n.trim()}function hc(n,t){rT(n,t,"route");let o={};for(let i of t)o[i]=zQ(n[i],`route.${i}`);return o}function hg(n,t){if(!t.includes(n))throw Ec("target kind is not supported by this channel");return n}function WCe(n,t,o){switch(n){case"weixin":return hg(t,["user"]),hc(o,["toUserId"]);case"feishu":return hg(t,["user","group"]),hc(o,t==="user"?["openId"]:["chatId"]);case"dingtalk":return hg(t,["user","group"]),hc(o,t==="user"?["userId"]:["openConversationId"]);case"wecom":return hg(t,["user","group"]),hc(o,["chatId"]);case"qq":return hg(t,["user","group"]),hc(o,t==="user"?["userOpenId"]:["groupOpenId"]);case"slack":return hg(t,["conversation","thread"]),hc(o,t==="conversation"?["channelId"]:["channelId","threadTs"]);case"telegram":{hg(t,["chat","topic"]),rT(o,t==="chat"?["chatId"]:["chatId","messageThreadId"],"route");let i=zQ(o.chatId,"route.chatId");if(!/^-?\d+$/.test(i))throw Ec("route.chatId must be a decimal string");if(t==="chat")return{chatId:i};if(!Number.isSafeInteger(o.messageThreadId)||o.messageThreadId<=0)throw Ec("route.messageThreadId must be a positive integer");return{chatId:i,messageThreadId:o.messageThreadId}}case"discord":return hg(t,["channel"]),hc(o,["channelId"]);case"whatsapp":{hg(t,["user","group"]);let i=hc(o,["jid"]);if(!(t==="user"?/^\d{5,32}@(s\.whatsapp\.net|lid)$/.test(i.jid):/^\d{5,32}(?:-\d{1,32})?@g\.us$/.test(i.jid)))throw Ec(`route.jid must be a ${t} WhatsApp JID`);return i}default:throw new TypeError(`Unsupported delivery channel: ${n}`)}}function jp(n,t,{targetIdRequired:o=!0}={}){if(!VQ(n))throw new TypeError(`Unsupported delivery channel: ${n}`);rT(t,o?["targetId","name","kind","route"]:["name","kind","route"],"target");let i={};return o&&(i.targetId=zQ(t.targetId,"target.targetId")),t.name!==void 0&&(i.name=VCe(t.name)),i.kind=zQ(t.kind,"target.kind"),i.route=WCe(n,i.kind,t.route),i}function za({channel:n,workspaces:t,coreController:o,stateFor:i}){if(!VQ(n))throw new TypeError(`Unsupported delivery channel: ${n}`);if(!t||typeof t!="object")throw new TypeError("delivery adapter requires a workspace store");if(!o||typeof o!="object")throw new TypeError("delivery adapter requires a proactive text controller");if(typeof i!="function")throw new TypeError("delivery adapter requires a bot state getter");return Object.freeze({channel:n,ownsBot:e=>t.has(e),listTargets:e=>t.listDeliveryTargets(e),async listSuggestions(e){let a=await i(e);if(!a||typeof a.snapshot!="function")throw new TypeError("delivery suggestion state cannot be inspected");return a3(n,a.snapshot()?.sessions).map(r=>jp(n,r,{targetIdRequired:!1}))},createTarget:(e,a)=>t.createDeliveryTarget(e,jp(n,a)),updateTarget:(e,a,A)=>t.updateDeliveryTarget(e,a,jp(n,A,{targetIdRequired:!1})),deleteTarget:(e,a)=>t.deleteDeliveryTarget(e,a),async sendText(e,a,A,r={}){let s=jp(n,a);if(typeof o.sendProactiveText!="function")throw new TypeError("delivery controller cannot send proactive text");return await o.sendProactiveText(e,s,A,r),{sent:!0}}})}var i3=Object.freeze([250,1e3,3e3,5e3,1e4,3e4]);function ZCe(n){if(!Array.isArray(n)||n.length===0)return[...i3];let t=n.filter(o=>Number.isFinite(o)&&o>=0);return t.length>0?t:[...i3]}var WQ=class{#e;#A;#t;#r;#l;#o;#i;#a=null;#n=null;#s=0;#p=!1;#c=!1;#g;#I;constructor({controller:t,harness:o,logger:i=console,retryDelaysMs:e,healthyIntervalMs:a=15e3,setTimeoutImpl:A=setTimeout,clearTimeoutImpl:r=clearTimeout}){if(!t||typeof t.initialize!="function"||typeof t.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=t,this.#A=o,this.#t=i,this.#r=ZCe(e),this.#l=Number.isFinite(a)&&a>=0?a:15e3,this.#o=A,this.#i=r,this.#g=new Promise(s=>{this.#I=s})}get ready(){return this.#g}start(){return this.#c||this.#p?this:(this.#c=!0,this.#d(0),this)}async close(){this.#p||(this.#p=!0,this.#a!==null&&this.#i(this.#a),this.#a=null,await this.#n?.catch(()=>{}),this.#I?.(null),this.#I=null)}#d(t){this.#p||(this.#a=this.#o(()=>{this.#a=null,this.#h()},t),this.#a?.unref?.())}async#h(){if(this.#p||this.#n)return;let t=this.#u();this.#n=t;try{await t}finally{this.#n===t&&(this.#n=null)}}async#u(){try{if(await this.#A.ensureRunning(),this.#p)return;let t=await this.#e.initialize();if(this.#p)return;this.#I?.(t),this.#I=null;let{configured:o,connected:i}=t.totals;if(i{let A={namespace:"commands",method:"execute",args:{agentId:i,line:e,images:[]},signal:a.signal};try{return await o.invoke(A)}catch(r){if(r?.name!=="TypertGatewayError"||r.code!=="arguments-invalid"||r.endpoint!=="commands/execute"||r.message!=='typert gateway: commands/execute: args fields do not match the descriptor: unexpected "images"')throw r;return a.signal?.throwIfAborted(),o.invoke({...A,args:{agentId:i,line:e}})}}}}import{randomUUID as iT}from"node:crypto";var ZQ=new WeakMap;function XCe(n){let t=n?.failure;return t&&typeof t=="object"&&typeof t.code=="string"&&typeof t.message=="string"?{code:t.code,message:t.message,details:t.details&&typeof t.details=="object"?t.details:{}}:n?.name==="AbortError"||n?.name==="RemoteInvocationCancelled"?{code:"cancelled",message:n instanceof Error?n.message:"Harness request was cancelled",details:{}}:{code:"internal",message:n instanceof Error?n.message:String(n),details:{}}}function Fo(n,t){return Promise.resolve().then(t).then(o=>({rpcId:n.rpcId,result:{ok:!0,value:o}}),o=>({rpcId:n.rpcId,result:{ok:!1,error:XCe(o)}}))}function o3(n,t,o,i){return{namespace:n,method:t,args:o,...i===void 0?{}:{signal:i}}}function XQ(n,t){let o=new Error(n);return o.name="UserQuestionError",o.code=t,o}function $Ce(n,t){return!n||typeof n!="object"||n.sessionId!==t.sessionId||!n.answer||typeof n.answer!="object"||!Array.isArray(n.answer.answers)||n.answer.answers.length!==t.questions.length?!1:n.answer.answers.every((o,i)=>{let e=t.questions[i];if(!o||typeof o!="object"||o.id!==e.id||!Array.isArray(o.selected)||o.selected.some(r=>typeof r!="string")||new Set(o.selected).size!==o.selected.length||o.custom!==void 0&&typeof o.custom!="string")return!1;let a=o.custom?.trim();if(a!==void 0&&!a||e.multiSelect!==!0&&(a!==void 0&&o.selected.length>0||o.selected.length>1))return!1;let A=new Set(e.options?.map(r=>r.label)??[]);return o.selected.every(r=>A.has(r))})}function eBe(n){if(n?.type==="event"&&n.event&&typeof n.event=="object")return[{event:n.event}];let t=n?.type==="chunks"?n.event:null;if(!t||typeof t!="object")throw new Error("Harness returned an invalid session history record");let{data:o}=t,i=t.type,e=i==="chunkrow/tool-call-chunks"?o?.args:o?.texts;if(!o||!Array.isArray(e)||e.length===0||e.some(A=>typeof A!="string")||!Array.isArray(o.dt)||o.dt.length!==e.length-1)throw new Error("Harness returned an invalid packed session chunk");let a=t.time;return e.map((A,r)=>{r>0&&(a+=o.dt[r-1]);let s;if(i==="chunkrow/text-chunks")s={type:"text-delta",index:o.index,text:A};else if(i==="chunkrow/reasoning-chunks")s={type:"reasoning-delta",index:o.index,text:A};else if(i==="chunkrow/tool-call-chunks")s={type:"tool-call-delta",index:o.index,id:o.id,...Object.hasOwn(o,"name")?{name:o.name}:{},argumentsDelta:A};else throw new Error(`Harness returned an unsupported packed session chunk: ${String(i)}`);return{event:{type:"assistant/chunk",seq:t.seq+r,time:a,data:{turn:o.turn,step:o.step,chunk:s}}}})}function n3(n){if(!Array.isArray(n))throw new Error("Harness returned invalid session history records");return n.flatMap(eBe)}var oT=class{#e=[];#A=null;#t=!1;#r;#l;#o;constructor(t,o){this.#r=t,this.#o=o,this.#l=()=>this.close(),t?.addEventListener("abort",this.#l,{once:!0}),t?.aborted&&this.close()}push(t){if(!this.#t)if(this.#A){let o=this.#A;this.#A=null,o({value:t,done:!1})}else this.#e.push(t)}next(){return this.#e.length>0?Promise.resolve({value:this.#e.shift(),done:!1}):this.#t?Promise.resolve({value:void 0,done:!0}):new Promise(t=>{this.#A=t})}return(){return this.close(),Promise.resolve({value:void 0,done:!0})}close(){if(!this.#t&&(this.#t=!0,this.#r?.removeEventListener("abort",this.#l),this.#o?.(),this.#o=null,this.#A)){let t=this.#A;this.#A=null,t({value:void 0,done:!0})}}[Symbol.asyncIterator](){return this}},nT=class{#e;#A;#t=new Set;#r=new Map;#l=new Map;#o=new Map;#i=[];#a=!1;constructor(t,o,i){this.#e=o,this.#A=i,this.host=Object.freeze({describe:e=>Fo(e,()=>({ready:!0,transport:"typert"}))}),this.workspace=Object.freeze({list:(e,a)=>Fo(e,()=>this.#p(a)),create:(e,a)=>Fo(e,()=>this.#n("workspace","create",{request:e.payload},a))}),this.sessions=Object.freeze({list:(e,a)=>Fo(e,()=>this.#n("session","list",{_request:e.payload},a)),create:(e,a)=>Fo(e,()=>this.#n("session","create",{request:e.payload},a)),history:(e,a)=>Fo(e,()=>this.#g(e.payload,a)),prompt:(e,a)=>Fo(e,()=>this.#n("session","prompt",{request:{requestId:e.rpcId,...e.payload}},a)),rename:(e,a)=>Fo(e,()=>this.#n("session","rename",{request:e.payload},a)),cancel:(e,a)=>Fo(e,()=>this.#n("session","cancel",{request:{sessionId:e.payload.sessionId}},a)),models:(e,a)=>Fo(e,()=>this.#h(e.payload.sessionId,a)),selectModel:(e,a)=>Fo(e,()=>this.#n("session","selectModel",{request:e.payload},a))}),this.llm=Object.freeze({models:(e,a)=>Fo(e,async()=>{let A=await this.#d(a);return{groups:A.groups,failures:A.failures}})}),this.events=Object.freeze({mux:(e,a)=>this.#u(a)}),this.respond=e=>Promise.resolve(this.#m(e)),typeof t?.on=="function"&&(this.#i.push(t.on("session/event",(e,a)=>{let A=e?.id;typeof A!="string"||!a||typeof a!="object"||(Number.isSafeInteger(a.seq)&&this.#I(A,a.seq),this.#C({type:"session/event",sessionId:A,event:a}))},{global:!0})),this.#i.push(t.on("approval/request",(e,a)=>this.#y(e,a),{global:!0,prepend:!0})),this.#i.push(t.on("user-questions/request",(e,a)=>this.#Q(e,a),{global:!0,prepend:!0})))}async#n(t,o,i,e){return this.#e.invoke(o3(t,o,i,e))}async#s(t,o,i,e){let a=new AbortController,A=e?AbortSignal.any([e,a.signal]):a.signal,r;try{r=(await this.#e.stream(o3(t,o,i,A)))[Symbol.asyncIterator]();let g=await r.next();if(g.done)throw new Error(`Harness ${t}.${o} stream ended before its baseline`);return g.value}finally{a.abort(new DOMException("Baseline received","AbortError")),await Promise.resolve(r?.return?.()).catch(()=>{})}}async#p(t){let o=await this.#s("workspace","follow",{},t);if(o?.type!=="baseline"||!o.value||typeof o.value!="object")throw new Error("Harness workspace.follow returned no baseline");return o.value}async#c(t,o,i){let e=await this.#s("session","follow",{request:{address:{kind:"session",sessionId:t},maxMessages:o}},i);if(e?.type!=="snapshot"||!Number.isSafeInteger(e.cursor))throw new Error("Harness session.follow returned no snapshot");return this.#I(t,e.cursor),e}async#g(t,o){let{sessionId:i,maxMessages:e=50,beforeSeq:a}=t,A=this.#o.get(i);if(A===void 0){let s=await this.#c(i,e,o);if(A=this.#o.get(i)??s.cursor,a===void 0&&A===s.cursor)return{events:n3(s.records),hasMore:s.hasMore===!0,...s.projections===void 0?{}:{projections:s.projections}}}let r=await this.#n("session","page",{request:{address:{kind:"session",sessionId:i},throughSeq:A,maxMessages:e,...a===void 0?{}:{beforeSeq:a}}},o);return{events:n3(r.records),hasMore:r.hasMore===!0}}#I(t,o){let i=this.#o.get(t);(i===void 0||o>i)&&this.#o.set(t,o)}#d(t){return this.#n("session","modelCatalog",{},t)}async#h(t,o){let[i,e]=await Promise.all([this.#d(o),this.#n("session","list",{_request:{}},o)]),a=e?.items?.find(s=>s?.sessionId===t);if(!a){let s=new Error(`session "${t}" not found`);throw s.failure={code:"session-not-found",message:s.message,details:{sessionId:t}},s}let A=a.projections?.values?.modelSelection,r=A?.next??A?.lastUsed??i.default;return{current:r,routable:i.routableProviders.includes(r.provider),groups:i.groups,failures:i.failures}}#u(t){let o;o=new oT(t,()=>this.#t.delete(o)),this.#t.add(o);for(let i of this.#r.values())o.push(this.#f(i));for(let i of this.#l.values())o.push(this.#S(i));return o}#C(t,o=iT()){let i={rpcId:o,payload:t};for(let e of this.#t)e.push(i)}#B(t){let o=t?.session?.id??t?.id;return typeof o!="string"||!t?.session||!Array.isArray(t.session.events)?null:X1(this.#A,o,t.session.events)?{sessionId:o,session:t.session}:null}#f(t){return{rpcId:t.rpcId,payload:{type:"question/requested",sessionId:t.sessionId,questions:t.questions}}}#Q(t,o){let i=this.#B(t?.agent);return i?t.signal?.aborted?Promise.reject(XQ("ask_user_question was aborted before the user answered","ASK_ABORTED")):new Promise((e,a)=>{let A={rpcId:iT(),sessionId:i.sessionId,questions:t.questions,signal:t.signal,settle:(s,g)=>{this.#r.delete(A.rpcId)&&(t.signal?.removeEventListener("abort",r),this.#C({type:"question/resolved",sessionId:A.sessionId,questionRpcId:A.rpcId,outcome:s}),s==="answered"?e(g):a(g))}},r=()=>A.settle("cancelled",XQ("ask_user_question was aborted before the user answered","ASK_ABORTED"));this.#r.set(A.rpcId,A),t.signal?.addEventListener("abort",r,{once:!0}),this.#C(this.#f(A).payload,A.rpcId)}):o()}#S(t){return{rpcId:t.rpcId,payload:{type:"approval/requested",sessionId:t.sessionId,approvalId:t.approvalId,toolName:t.toolName,...t.callId===void 0?{}:{callId:t.callId},...t.reason===void 0?{}:{reason:t.reason}}}}#y(t,o){let i=this.#B(t?.agent);if(!i)return o();if(t.signal?.aborted)return Promise.resolve("cancelled");let e=new Set([...this.#l.values()].map(r=>r.approvalId)),a=new Set,A;for(let r=i.session.events.length-1;r>=0;r-=1){let s=i.session.events[r];if(s.type==="approval/decided")a.add(s.data?.id);else if(s.type==="approval/asked"){let g=s.data?.id;if(!g||a.has(g)||e.has(g)||(t.callId??null)!==(s.data?.callId??null))continue;A=g;break}}return A===void 0?o():new Promise(r=>{let s={rpcId:iT(),sessionId:i.sessionId,approvalId:A,toolName:t.toolName,callId:t.callId,reason:t.reason,settle:c=>{this.#l.delete(s.rpcId)&&(t.signal?.removeEventListener("abort",g),this.#C({type:"approval/resolved",sessionId:s.sessionId,approvalId:s.approvalId,outcome:c}),r(c))}},g=()=>s.settle("cancelled");this.#l.set(s.rpcId,s),t.signal?.addEventListener("abort",g,{once:!0}),this.#C(this.#S(s).payload,s.rpcId)})}#m(t){let o=this.#l.get(t?.rpcId);if(o){let a=t?.result?.value;return t?.result?.ok!==!0||!a||typeof a!="object"||a.sessionId!==o.sessionId||a.approvalId!==o.approvalId||a.outcome!=="allowed-once"&&a.outcome!=="rejected"?{accepted:!1,reason:"bad-response"}:(o.settle(a.outcome),{accepted:!0})}let i=this.#r.get(t?.rpcId);if(!i)return{accepted:!1,reason:"not-pending"};if(t?.result?.ok!==!0)return t?.result?.error?.code!=="cancelled"?{accepted:!1,reason:"bad-response"}:(i.settle("cancelled",XQ("the user cancelled ask_user_question","ASK_CANCELLED")),{accepted:!0});if(!$Ce(t.result.value,i))return{accepted:!1,reason:"bad-response"};let e={answers:t.result.value.answer.answers.map(a=>({id:a.id,selected:[...a.selected],...a.custom===void 0?{}:{custom:a.custom}}))};return i.settle("answered",e),{accepted:!0}}dispose(){if(!this.#a){this.#a=!0;for(let t of[...this.#t])t.close();for(let t of[...this.#l.values()])t.settle("cancelled");for(let t of[...this.#r.values()])t.settle("cancelled",XQ("dsh-im interaction adapter was disposed","ASK_ABORTED"));for(let t of this.#i.splice(0).reverse())t?.()}}};function s3(n){let t=n?.root??n;if(!t||!["object","function"].includes(typeof t))throw new TypeError("dsh-im requires a Cordis Host context");let o=ZQ.get(t);if(o)return o;let i=n?.typertGateway;if(!i||typeof i.invoke!="function"||typeof i.stream!="function")throw new TypeError("dsh-im requires the modern Host Typert gateway");let e=new nT(n,i,t);return ZQ.set(t,e),typeof n.effect=="function"&&n.effect(()=>()=>{ZQ.get(t)===e&&ZQ.delete(t),e.dispose()},"dsh-im: modern Harness compatibility API"),e}function ABe(n,t){return n&&Object.hasOwn(n,t)?n[t]:typeof n?.get=="function"?n.get(t):void 0}function na(n,t={}){return t.harnessBaseUrl!==void 0?{baseUrl:new URL(t.harnessBaseUrl)}:{apiProxy:ABe(n,"apiProxy")??s3(n),interactionScope:n.root??n}}import{randomUUID as tBe}from"node:crypto";function rBe(n){if(typeof n?.get!="function")return;let t;try{t=n.get("agents")}catch{return}if(t!=null){if(typeof t.get!="function")throw new TypeError('dsh-im requires a callable AgentRegistry when ctx.get("agents") is present');return t}}function aBe(n,t,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===t&&A?.type==="user/message"&&A.data?.source?.rpcId===o&&(a=!0)}return e===t&&a}function g3(n){if(!n||typeof n!="object"||Object.isFrozen(n))return n;for(let t of Object.values(n))g3(t);return Object.freeze(n)}function iBe(n){return g3({id:tBe(),role:"user",content:[{type:"text",text:n}],source:{kind:"user"}})}function oBe(n){let t=new Error("Session is busy with an active turn or maintenance task",{cause:n});return t.code="agent-busy",t}function nBe(n){return({sessionId:t,expectedTurn:o,promptRpcId:i,action:e,text:a})=>{let A=n.get(t);if(A){if(!aBe(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(iBe(a)),!0);throw new TypeError(`Unsupported Harness control action: ${String(e)}`)}}}function sBe(n){return({sessionId:t,operation:o})=>{if(typeof o!="function")throw new TypeError("maintenance operation is required");let i=n.get(t);if(!i)return o();try{return i.runMaintenance(e=>{if(n.get(t)!==i){let a=new Error("Session agent changed before maintenance started");throw a.code="agent-unavailable",a}return o(e)})}catch(e){throw oBe(e)}}}function gBe(n){return({sessionId:t,workspace:o,files:i,signal:e})=>{let A=n.get(t)?.session?.header?.cwd,r=typeof A=="string"&&A?A:o;if(typeof r!="string"||!r)throw new ko("inbound-file-workspace-unavailable","The Harness Session workspace is unavailable for inbound files.");return n1({files:i},{workspace:r,signal:e})}}function Wa(n,t={}){let{controlExecutor:o,sessionMaintenanceExecutor:i,fileIngressExecutor:e}=t;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:rBe(n);return{controlExecutor:o??(a?nBe(a):void 0),sessionMaintenanceExecutor:i??(a?sBe(a):void 0),fileIngressExecutor:e??gBe(a??{get:()=>{}})}}function c3(n){return n.map(t=>({id:t,canExecuteCommands:!0}))}function Eg(n=[]){return UQ({mode:"open",open:{defaultCanExecuteCommands:!0,commandPermissionOverrides:[]},allowlist:{users:c3(n)}})}function uc(n=[]){return UQ({mode:"allowlist",open:{defaultCanExecuteCommands:!1,commandPermissionOverrides:[]},allowlist:{users:c3(n)}})}function ug(n){return[...new Set((Array.isArray(n)?n:[n]).filter(t=>typeof t=="string"||typeof t=="number"||typeof t=="bigint").map(t=>String(t).trim()).filter(Boolean))]}function cBe(n){return ug(n).map(t=>`${t.replace(/^\+/,"")}@s.whatsapp.net`)}function cr(n,t={}){let o=String(n??"").trim().toLowerCase();if(o==="weixin")return dg({direct:uc(),group:uc()});if(o==="feishu"){let e=ug(t.ownerOpenIds??t.ownerOpenId).includes("*")?Eg():uc();return dg({direct:e,group:e})}if(o==="qq"){let i=ug(t.ownerUserOpenid);return dg({direct:i.includes("*")?Eg():uc(),group:Eg()})}if(o==="telegram"){let i=ug(t.allowedUsers);return(t.accessMode??"compatible")==="private-allowlist"?dg({direct:uc(i),group:uc()}):dg({direct:Eg(i),group:Eg()})}if(o==="whatsapp"){let i=t.accessMode??"self-only",e=cBe(t.allowedNumbers);return i==="open"?dg({direct:Eg(e),group:Eg()}):dg({direct:uc(i==="private-allowlist"?e:[]),group:uc()})}if(["dingtalk","wecom","slack","discord"].includes(o))return dg({direct:Eg(),group:Eg()});throw new TypeError(`Unsupported access-policy channel: ${n}`)}function IBe(n,t={}){let o=String(n??"").trim().toLowerCase();if(o==="weixin")return ug(t.ownerUserId);if(o==="feishu")return ug(t.ownerOpenIds??t.ownerOpenId).filter(i=>i!=="*");if(o==="dingtalk"){let i=Array.isArray(t.approvedSenders)?t.approvedSenders:[];return ug(i.map(e=>e?.staffId))}return o==="qq"?ug(t.ownerUserOpenid).filter(i=>i!=="*"):o==="whatsapp"?ug(t.accountJid):[]}function Za(n,t,{channel:o,config:i,equals:e}={}){if(!n||typeof n.accessPolicyFor!="function")throw new TypeError("A workspace store with access policies is required");let a=new Set(IBe(o,i)),A=typeof e=="function"?e:(r,s)=>r===s;return Object.freeze({botId:t,getSettings:()=>n.accessPolicyFor(t),isPrivileged(r,s){if(!["direct","group"].includes(s))return!1;let g=Array.isArray(r)?r:[r];try{return g.some(c=>typeof c=="string"&&[...a].some(I=>A(c.trim(),I)===!0))}catch{return!1}}})}function dBe(n){let t=HI(n.dshHome??process.env.DSH_HOME??iu(pBe(),".dsh")),o=HI(n.dataDir??iu(t,"integrations","dsh-dingtalk"));return{root:o,config:HI(n.configPath??iu(o,"config.json")),bots:HI(n.botsDir??iu(o,"bots")),workspaces:HI(n.workspacesPath??iu(o,"workspaces.json"))}}async function sT(n,t={},o={}){if(!n?.credentials)throw new TypeError("dsh-dingtalk requires ctx.credentials");let i=na(n,t),e=o.ConfigStore??yQ,a=o.DeviceAuth??SQ,A=o.StateStore??LQ,r=o.HarnessClient??JQ,s=o.Controller??KE,g=o.Runtime??XE,c=o.createConnectionSupervisor??aT,I=typeof n.logger=="function"?n.logger("dsh-dingtalk"):n.logger??console,p=()=>Ya(n),B=dBe(t),f=await new e(B.config).load(),Q=HI(t.workspace??process.cwd()),y=o.WorkspaceStore??oa,m=o.workspaces??await new y(B.workspaces,{defaultWorkspace:Q}).load(),T=typeof f.list=="function",_=T?f.list():[];T&&await m.reconcile(_.map(X=>X.botId)),await Promise.all(_.map(X=>m.ensure(X.botId,{defaultAgentPreset:t.agentPreset,initialAccessPolicy:cr("dingtalk",X)})));let R=typeof f.remove=="function"?ja(f,{workspaces:m}):f,x=o.deviceAuth??new a({baseUrl:t.registrationBaseUrl}),F=new Map,L=X=>HI(B.bots,X,"state.json"),q=async X=>{let ie=F.get(X);return ie||(ie=await new A(L(X)).load(),F.set(X,ie)),ie},z=Va(n,o.commandExecutor),{controlExecutor:H,sessionMaintenanceExecutor:G,fileIngressExecutor:V}=Wa(n,{controlExecutor:o.controlExecutor,sessionMaintenanceExecutor:o.sessionMaintenanceExecutor,fileIngressExecutor:o.fileIngressExecutor}),U=new r({...i,workspace:Q,autostart:!1,dshBin:t.dshBin??"dsh",...z?{commandExecutor:z}:{},...H?{controlExecutor:H}:{},...G?{sessionMaintenanceExecutor:G}:{},...V?{fileIngressExecutor:V}:{}}),N=new s({deviceAuth:x,credentials:n.credentials,configStore:R,logger:I,createRuntime:async({botId:X,config:ie,clientSecret:we})=>{let oe=await q(X);await m.ensure(X,{defaultAgentPreset:t.agentPreset,initialAccessPolicy:cr("dingtalk",ie)});let Se=Ja(U,{botId:X,workspaces:m,state:oe,agentPresetCatalog:p});return new g({config:ie,clientSecret:we,harness:Se.harness,state:Se.state,contextEnhancement:{botId:X,getSettings:()=>m.contextEnhancementFor(X)},accessPolicy:Za(m,X,{channel:"dingtalk",config:ie}),replyTimeoutMs:t.replyTimeoutMs??6e5,maxMessageChars:t.maxMessageChars??4e3,connectTimeoutMs:t.connectTimeoutMs??15e3,logger:{error:(...Be)=>I.error?.(`[${X}]`,...Be),warn:(...Be)=>I.warn?.(`[${X}]`,...Be),info:(...Be)=>I.info?.(`[${X}]`,...Be),debug:(...Be)=>I.debug?.(`[${X}]`,...Be)}})},deleteState:async({botId:X})=>{let ie=F.get(X);if(F.delete(X),ie&&typeof ie.remove=="function")await ie.remove();else try{await lBe(L(X))}catch(we){if(we?.code!=="ENOENT")throw we}}}),k=qa(N,{workspaces:m,stateFor:q,agentPresetCatalog:p}),J=c({controller:k,harness:U,logger:I,retryDelaysMs:t.retryDelaysMs,healthyIntervalMs:t.healthyIntervalMs}).start();return{controller:k,deliveryAdapter:za({channel:"dingtalk",workspaces:m,coreController:N,stateFor:q}),ready:J.ready,async close(){await J.close(),await k.close(),U.stopManagedProcess()}}}import CBe from"qrcode";var Gi="bot.context-enhancement.set";function Xa(n){try{return!n||typeof n!="object"||Array.isArray(n)||Reflect.ownKeys(n).length!==2||!Object.hasOwn(n,"botId")||!Object.hasOwn(n,"config")||typeof n.botId!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(n.botId)?!1:(GI(n.config),!0)}catch{return!1}}var $a="bot.access-policy.set";function ei(n){try{return!n||typeof n!="object"||Array.isArray(n)||Reflect.ownKeys(n).length!==2||!Object.hasOwn(n,"botId")||!Object.hasOwn(n,"policy")||typeof n.botId!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(n.botId)?!1:(dc(n.policy),!0)}catch{return!1}}var hBe=new Set(["loopback","trusted-host"]);function yr(n){if(n===void 0)return"loopback";if(hBe.has(n))return n;throw new TypeError('dsh-im rpcAuthority must be "loopback" or "trusted-host"')}import{isAbsolute as EBe}from"node:path";var Fi="bot.workspace.set";function Ai(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)&&Object.keys(n).every(t=>["botId","workspace"].includes(t))&&typeof n.botId=="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(n.botId)&&typeof n.workspace=="string"&&n.workspace.length<=4096&&EBe(n.workspace.trim())}function ti(n){return["workspace-not-absolute","workspace-not-found","workspace-not-directory","workspace-bot-not-found","agent-preset-invalid","agent-preset-unavailable","context-enhancement-invalid"].includes(n?.code)?{code:n.code,message:n.message}:null}var Li="bot.preset.set";function uBe(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}function ri(n){return uBe(n)&&Object.keys(n).every(t=>["botId","agentPreset"].includes(t))&&typeof n.botId=="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(n.botId)&&(n.agentPreset===null||typeof n.agentPreset=="string"&&__.test(n.agentPreset))}var l3="/dingtalk",Lt=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:Fi,setAgentPreset:Li,setContextEnhancement:Gi,setAccessPolicy:$a,approveSender:"bot.sender.approve",revokeSender:"bot.sender.revoke"}),p3=Object.freeze(Object.values(Lt)),BBe=new Set(["clientSecret","client_secret","deviceCode","device_code","secretRef","staffId","senderStaffId","verificationUrl","verificationUri","userCode"]);function cT(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}function Cc(n,t){return cT(n)&&Object.keys(n).every(o=>t.includes(o))}function KI(n){return typeof n=="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(n)}function I3(n,t){return typeof n=="string"&&n.trim().length>0&&n.length<=t}function fBe(n,t){return cT(t)?n===Lt.status?Cc(t,[])?null:"connection.status does not accept fields.":n===Lt.beginProvisioning?Cc(t,["locale"])&&(t.locale===void 0||t.locale==="zh-CN")?null:"provision.begin received unsupported fields.":[Lt.pollProvisioning,Lt.cancelProvisioning].includes(n)?Cc(t,["attemptId"])&&KI(t.attemptId)?null:`${n} requires an attemptId.`:n===Lt.bindCredentials?Cc(t,["clientId","clientSecret"])&&I3(t.clientId,256)&&I3(t.clientSecret,1024)?null:"bot.bind-credentials requires Client ID and Client Secret.":n===Lt.reconnectBot?Cc(t,["botId","sendTest"])&&KI(t.botId)&&(t.sendTest===void 0||t.sendTest===!0)?null:"bot.reconnect requires a botId and optional sendTest=true.":n===Lt.deleteBot?Cc(t,["botId","confirm"])&&KI(t.botId)&&t.confirm===!0?null:"bot.delete requires a botId and confirm=true.":n===Lt.setWorkspace?Ai(t)?null:"\u8BF7\u8F93\u5165\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84\u3002":n===Lt.setAgentPreset?ri(t)?null:"\u8BF7\u9009\u62E9 Agent Preset\u3002":n===Lt.setContextEnhancement?Xa(t)?null:"\u8BF7\u63D0\u4EA4\u6709\u6548\u7684\u4E0A\u4E0B\u6587\u589E\u5F3A\u8BBE\u7F6E\u3002":n===Lt.setAccessPolicy?ei(t)?null:"\u8BF7\u63D0\u4EA4\u6709\u6548\u7684\u8BBF\u95EE\u8BBE\u7F6E\u3002":n===Lt.approveSender?Cc(t,["botId","requestId","confirm"])&&KI(t.botId)&&KI(t.requestId)&&t.confirm===!0?null:"bot.sender.approve requires botId, requestId, and confirm=true.":n===Lt.revokeSender?Cc(t,["botId","senderKey","confirm"])&&KI(t.botId)&&KI(t.senderKey)&&t.confirm===!0?null:"bot.sender.revoke requires botId, senderKey, and confirm=true.":"Unknown DingTalk endpoint.":"Payload must be an object."}function $Q(n){return{ok:!1,error:{code:"bad-request",message:n}}}function ou(){return{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}}function QBe(){return{ok:!1,error:{code:"dingtalk-operation-failed",message:"\u9489\u9489\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"}}}function Jp(n){if(Array.isArray(n))return n.map(Jp);if(!cT(n))return n;let t={};for(let[o,i]of Object.entries(n))BBe.has(o)||(t[o]=Jp(i));return t}async function yBe(n){return CBe.toDataURL(n,{type:"image/png",errorCorrectionLevel:"M",margin:2,width:320})}async function gT(n,t){return!n||typeof n.verificationUrl!="string"?Jp(n):Jp({...n,qrCodeDataUrl:await t(n.verificationUrl)})}async function gs(n,t){let o=structuredClone(n);return o?.provisioning&&(o.provisioning=await gT(o.provisioning,t)),Jp(o)}function mBe(n){for(let t of["status","startProvisioning","registrationStatus","cancelProvisioning","bindCredentials","reconnectBot","deleteBot","approveSender","revokeSender"])if(typeof n?.[t]!="function")throw new TypeError(`A complete DingTalk controller is required (${t})`)}function d3(n,{encodeQr:t=yBe}={}){mBe(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(()=>t(e)),o.set(e,a)),a};return async(e,a,A)=>{if(A?.aborted)return ou();if(!p3.includes(e))return $Q("Unknown DingTalk endpoint.");let r=fBe(e,a);if(r)return $Q(r);try{let s;if(e===Lt.status)s=await gs(await n.status(),i);else if(e===Lt.beginProvisioning){let g=await n.startProvisioning({signal:A});if(A?.aborted)return await n.cancelProvisioning(g.attemptId),ou();s=await gT(g,i)}else if(e===Lt.pollProvisioning){let g=await n.registrationStatus(a.attemptId);if(!g)return $Q("The provisioning attempt no longer exists.");s=await gT(g,i)}else if(e===Lt.cancelProvisioning){if(s=await n.cancelProvisioning(a.attemptId),!s)return $Q("The provisioning attempt no longer exists.");s=Jp(s)}else if(e===Lt.bindCredentials)s=await gs(await n.bindCredentials(a),i);else if(e===Lt.reconnectBot){let g=await n.reconnectBot(a.botId);if(A?.aborted)return ou();let c;if(a.sendTest===!0)if(!g?.bots?.some(p=>p?.botId===a.botId&&p?.connected===!0)||typeof n.sendConnectionTest!="function")c=gr(fr("\u9489\u9489\u673A\u5668\u4EBA"));else try{await n.sendConnectionTest(a.botId),c=gr()}catch(p){c=gr(p)}s=await gs({...g,...c?{testMessage:c}:{}},i)}else if(e===Lt.deleteBot)s=await gs(await n.deleteBot(a.botId),i);else if(e===Lt.setWorkspace){if(typeof n.updateWorkspace!="function")throw new Error("Workspace update is unavailable");s=await gs(await n.updateWorkspace(a.botId,a.workspace),i)}else if(e===Lt.setContextEnhancement){if(typeof n.updateContextEnhancement!="function")throw new Error("Context enhancement update is unavailable");s=await n.updateContextEnhancement(a.botId,a.config,g=>gs(g,i))}else if(e===Lt.setAccessPolicy){if(typeof n.updateAccessPolicy!="function")throw new Error("Access policy update is unavailable");s=await n.updateAccessPolicy(a.botId,a.policy,g=>gs(g,i))}else if(e===Lt.setAgentPreset){if(typeof n.updateAgentPreset!="function")throw new Error("Agent preset update is unavailable");s=await gs(await n.updateAgentPreset(a.botId,a.agentPreset),i)}else e===Lt.approveSender?s=await gs(await n.approveSender(a.botId,a.requestId),i):s=await gs(await n.revokeSender(a.botId,a.senderKey),i);return A?.aborted?ou():{ok:!0,value:s}}catch(s){let g=ti(s);return A?.aborted?ou():g?{ok:!1,error:g}:QBe()}}}function ey(n,t,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(l3,d3(t,o),{authority:yr(i)})}async function h3(n,t={}){if(t?.controller)return ey(n,t.controller,t.rpcOptions,t.rpcAuthority);let o=await sT(n,t,t.internals),i=t.deliveryService&&o.deliveryAdapter?t.deliveryService.registerAdapter(o.deliveryAdapter):void 0,e=ey(n,o.controller,t.rpcOptions,t.rpcAuthority);return n.effect(()=>async()=>{await i?.(),await o.close()},"dsh-dingtalk: close bot connections"),e}import{createHash as SBe}from"node:crypto";import{mkdir as wBe,readFile as DBe,rename as OBe,unlink as bBe,writeFile as MBe}from"node:fs/promises";import{dirname as _Be}from"node:path";var IT=Object.freeze({version:1,bots:Object.freeze([])});function Cg(n){return typeof n=="string"&&n.trim()?n.trim():null}function E3(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function nu(n,{botPrefix:t,tokenRefPrefix:o}){let i=Cg(n);if(!i)throw new TypeError("platformId is required");let e=SBe("sha256").update(i).digest("hex").slice(0,24);return{botId:`${t}_${e}`,tokenRef:`${o}_${e.toUpperCase()}`}}function Ay(n,t){let o=Cg(n)??"";return o.length<=10?o?`${o.slice(0,3)}\u2022\u2022\u2022`:t:`${o.slice(0,6)}\u2022\u2022\u2022\u2022${o.slice(-4)}`}var qp=class{#e;#A;#t;#r;#l;#o;#i;#a=IT;#n=Promise.resolve();constructor(t,{channel:o,botPrefix:i,tokenRefPrefix:e,normalizeBotExtension:a=()=>({})}){if(typeof a!="function")throw new TypeError("normalizeBotExtension must be a function");this.#e=t,this.#A=o,this.#t=i,this.#r=e,this.#l=a,this.#o=new RegExp(`^${E3(i)}_[a-f0-9]{24}$`),this.#i=new RegExp(`^${E3(e)}_[A-F0-9]{24}$`)}async load(){try{let t=this.#p(JSON.parse(await DBe(this.#e,"utf8")));if(!t)throw new Error(`dsh-im ${this.#A} config contains invalid bot data`);this.#a=t}catch(t){if(t?.code!=="ENOENT")throw t;this.#a=IT}return this}list(){return structuredClone(this.#a.bots)}get(t){let o=this.#a.bots.find(i=>i.botId===t);return o?structuredClone(o):null}getByPlatformId(t){let o=this.#a.bots.find(i=>i.platformId===t);return o?structuredClone(o):null}async save(t){let o=this.#s(t);if(!o)throw new Error(`Refusing to persist incomplete ${this.#A} bot data`);return this.#c(i=>{let e=i.find(r=>r.platformId===o.platformId&&r.botId!==o.botId),a=i.find(r=>r.tokenRef===o.tokenRef&&r.botId!==o.botId);if(e||a)throw new Error(`Duplicate ${this.#A} bot identity`);let A=i.findIndex(r=>r.botId===o.botId);return A===-1?i.push(o):i[A]=o,structuredClone(o)})}async remove(t){if(!this.#o.test(t))throw new TypeError(`Invalid ${this.#A} bot id`);return this.#c(o=>{let i=o.findIndex(a=>a.botId===t);if(i===-1)return null;let[e]=o.splice(i,1);return structuredClone(e)})}async clear(){let t=this.#n.then(async()=>{try{await bBe(this.#e)}catch(o){if(o?.code!=="ENOENT")throw o}this.#a=IT});this.#n=t.then(()=>{},()=>{}),await t}#s(t){if(!t||typeof t!="object")return null;let o=Cg(t.platformId),i=Cg(t.botId),e=Cg(t.tokenRef),a=Cg(t.name);if(!o||!i||!e||!a||!this.#o.test(i)||!this.#i.test(e))return null;let A=nu(o,{botPrefix:this.#t,tokenRefPrefix:this.#r});if(A.botId!==i||A.tokenRef!==e)return null;let r=this.#l(t);return!r||typeof r!="object"||Array.isArray(r)?null:Object.freeze({botId:i,platformId:o,tokenRef:e,name:a,username:Cg(t.username),createdAt:Cg(t.createdAt)??new Date().toISOString(),connectedAt:Cg(t.connectedAt),...r})}#p(t){if(!t||t.version!==1||!Array.isArray(t.bots))return null;let o=t.bots.map(A=>this.#s(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#c(t){let o,i=this.#n.then(async()=>{let e=[...this.#a.bots];o=t(e);let a=Object.freeze({version:1,bots:Object.freeze(e)});await this.#g(a),this.#a=a});return this.#n=i.then(()=>{},()=>{}),await i,o}async#g(t){await wBe(_Be(this.#e),{recursive:!0,mode:448});let o=`${this.#e}.tmp`;await MBe(o,`${JSON.stringify(t,null,2)} `,{encoding:"utf8",mode:384}),await OBe(o,this.#e)}};var u3=Object.freeze({botPrefix:"discord",tokenRefPrefix:"DSH_DISCORD_BOT_TOKEN"});function C3(n){return nu(n,u3)}function B3(n){return Ay(n,w("Discord\u673A\u5668\u4EBA"))}var ty=class extends qp{constructor(t){super(t,{channel:"Discord",...u3})}};function su(n){return typeof n=="string"&&n.trim()?n.trim():null}function gu(n,t){return Object.freeze({code:n,message:t})}var zp=class{#e;#A;#t;#r;#l;#o;#i;#a;#n;#s=new Map;#p=new Map;#c=new Map;#g=0;#I=!1;constructor({descriptor:t,credentials:o,configStore:i,inspectToken:e,deriveIdentity:a,maskPlatformId:A,createRuntime:r,deleteState:s=async()=>{},logger:g=console}){if(!t?.key||!t?.label||!t?.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(`${t.label} requires the DSH credential provider`);if(!i||typeof i.list!="function"||typeof i.save!="function"||typeof i.remove!="function")throw new TypeError(`${t.label} requires a config store`);if(typeof e!="function"||typeof a!="function"||typeof A!="function"||typeof r!="function")throw new TypeError(`${t.label} controller dependencies are incomplete`);this.#e=t,this.#A=o,this.#t=i,this.#r=e,this.#l=a,this.#o=A,this.#i=r,this.#a=s,this.#n=g}async initialize(){if(this.#I)return this.status();for(let t of this.#t.list())await this.#B(t.botId,async()=>{if(this.#I||this.#s.get(t.botId)?.status?.ready)return;let o=await this.#u(t.tokenRef);if(!o){this.#p.set(t.botId,gu("missing-token",w("{label}\u673A\u5668\u4EBA\u51ED\u636E\u7F3A\u5931\uFF0C\u8BF7\u79FB\u9664\u540E\u91CD\u65B0\u63A5\u5165\u3002",{label:this.#e.label})));return}try{await this.#d(t,o),this.#p.delete(t.botId)}catch(i){this.#p.set(t.botId,gu("connection-failed",w("{label}\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5\u3002",{label:this.#e.label}))),this.#n.warn?.(`[dsh-im:${this.#e.key}] bot ${t.botId} failed to initialize:`,i)}finally{this.#f()}});return this.status()}async bindCredentials({token:t}={}){if(this.#I)throw new Error(`${this.#e.label} controller is closed`);let o=su(t);if(!o)throw new TypeError(`${this.#e.label} Bot Token is required`);let i=await this.#r(o),e=su(i?.platformId),a=su(i?.name);if(!e||!a)throw new Error(`${this.#e.label} returned an invalid bot identity`);let A=this.#l(e);return await this.#B(A.botId,async()=>{if(this.#I)throw new Error(`${this.#e.label} controller is closed`);let r=this.#t.getByPlatformId(e),s=await this.#A.resolve(A.tokenRef).catch(()=>{}),g={botId:A.botId,platformId:e,tokenRef:A.tokenRef,name:a,username:su(i.username),createdAt:r?.createdAt??new Date().toISOString(),connectedAt:new Date().toISOString()};await this.#A.set(A.tokenRef,o);try{await this.#t.save(g)}catch(c){throw await this.#C(A.tokenRef,s),c}try{await this.#d(g,o),this.#p.delete(A.botId)}catch(c){this.#p.set(A.botId,gu("connection-failed",w("{label}\u673A\u5668\u4EBA\u5DF2\u63A5\u5165\uFF0C\u6D88\u606F\u8FDE\u63A5\u6682\u672A\u5C31\u7EEA\u3002",{label:this.#e.label}))),this.#n.warn?.(`[dsh-im:${this.#e.key}] bot ${A.botId} credential connection failed:`,c)}this.#f()}),this.status()}async reconnectBot(t){let o=this.#t.get(t);if(!o)throw new Error(`Unknown ${this.#e.label} bot`);return await this.#B(t,async()=>{let i=await this.#u(o.tokenRef);if(!i)throw new Error(`${this.#e.label} bot token is missing`);try{await this.#d(o,i),this.#p.delete(t)}catch(e){throw this.#p.set(t,gu("connection-failed",w("{label}\u8FDE\u63A5\u4ECD\u672A\u5C31\u7EEA\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",{label:this.#e.label}))),e}finally{this.#f()}}),this.status()}async updateBotConfig(t,o){if(this.#I)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.#B(t,async()=>{if(this.#I)throw new Error(`${this.#e.label} controller is closed`);let i=this.#t.get(t);if(!i)throw new Error(`Unknown ${this.#e.label} bot`);let e=await this.#u(i.tokenRef);if(!e)throw new Error(`${this.#e.label} bot token is missing`);if(this.#I)throw new Error(`${this.#e.label} controller is closed`);let a=o(i),A=await this.#t.save(a);try{await this.#d(A,e),this.#p.delete(t)}catch(r){throw this.#p.set(t,gu("connection-failed",w("{label}\u8FDE\u63A5\u4ECD\u672A\u5C31\u7EEA\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",{label:this.#e.label}))),r}finally{this.#f()}}),this.status()}async sendConnectionTest(t){let o=this.#t.get(t);if(!o)throw new Error(`Unknown ${this.#e.label} bot`);return this.#B(t,async()=>{let i=this.#s.get(t);if(!i?.status?.ready||typeof i.sendConnectionTest!="function"){let a=new Error(w("{label}\u673A\u5668\u4EBA\u5C1A\u672A\u8FDE\u63A5",{label:this.#e.label}));throw a.code="test-target-unavailable",a}let e=w("{name}\uFF08{id}\uFF09",{name:o.name,id:this.#o(o.platformId)});return await i.sendConnectionTest(La(e,w("{label}\u673A\u5668\u4EBA",{label:this.#e.label}))),{sent:!0}})}async sendProactiveText(t,o,i,e={}){if(!this.#t.get(t))throw new Error(`Unknown ${this.#e.label} bot`);return this.#B(t,async()=>{let A=this.#s.get(t);if(!A?.status?.ready||typeof A.sendProactiveText!="function"){let r=new Error(w("{label}\u673A\u5668\u4EBA\u5C1A\u672A\u8FDE\u63A5",{label:this.#e.label}));throw r.code="bot-not-connected",r}return A.sendProactiveText(o,i,e)})}async deleteBot(t){let o=this.#t.get(t);if(!o)throw new Error(`Unknown ${this.#e.label} bot`);return await this.#B(t,async()=>{let i=await this.#A.resolve(o.tokenRef).catch(()=>{});await this.#h(t);try{await this.#A.unset(o.tokenRef),await this.#t.remove(t)}catch(e){throw i?.value&&(await this.#A.set(o.tokenRef,i.value).catch(()=>{}),await this.#d(o,i.value).catch(()=>{})),new Error(`Unable to remove the ${this.#e.label} bot safely.`,{cause:e})}await this.#a({botId:t,config:o}).catch(e=>{this.#n.warn?.(`[dsh-im:${this.#e.key}] bot ${t} state cleanup failed:`,e)}),this.#p.delete(t),this.#f()}),this.status()}status(){let t=this.#t.list().map(i=>{let e=this.#s.get(i.botId)?.status??null,a=e?.ready===!0&&e.connectionState==="connected"&&e.harnessReachable===!0,A=a?"connected":e?.connectionState==="connecting"?"connecting":this.#p.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.#o(i.platformId)},health:{status:a?"healthy":A==="error"?"error":"offline",summary:a?w("{label}{connectionLabel}\u8FD0\u884C\u6B63\u5E38",{label:this.#e.label,connectionLabel:w(this.#e.connectionLabel)}):A==="error"?w("{label}\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5",{label:this.#e.label}):w("{label}\u8FDE\u63A5\u5F53\u524D\u79BB\u7EBF",{label:this.#e.label}),lastCheckedAt:e?.lastCheckedAt??null,lastConnectedAt:e?.lastConnectedAt??null},stats:{messagesReceived:e?.messagesReceived??0,messagesReplied:e?.messagesReplied??0},lastMessageError:Aa(e?.lastMessageError),error:structuredClone(this.#p.get(i.botId)??null)}}),o=t.filter(i=>i.connected).length;return{schemaVersion:1,revision:this.#g,state:t.length===0?"disconnected":o===t.length?"connected":o>0?"degraded":"offline",bots:t,totals:{configured:t.length,connected:o}}}async close(){this.#I||(this.#I=!0,await Promise.allSettled([...this.#c.values()]),await Promise.allSettled([...this.#s.keys()].map(t=>this.#h(t))))}async#d(t,o){if(this.#I)throw new Error(`${this.#e.label} controller is closed`);if(await this.#h(t.botId),this.#I)throw new Error(`${this.#e.label} controller is closed`);let i=await this.#i({botId:t.botId,config:t,token:o});if(!i||typeof i.start!="function"||typeof i.stop!="function")throw new TypeError(`createRuntime returned an invalid ${this.#e.label} runtime`);this.#s.set(t.botId,i);try{await i.start()}catch(e){throw await i.stop().catch(()=>{}),this.#s.delete(t.botId),e}}async#h(t){let o=this.#s.get(t);this.#s.delete(t),await o?.stop().catch(i=>{this.#n.warn?.(`[dsh-im:${this.#e.key}] bot ${t} failed to stop cleanly:`,i)})}async#u(t){let o=await this.#A.resolve(t).catch(()=>{});return su(o?.value)}async#C(t,o){o?.value?await this.#A.set(t,o.value).catch(()=>{}):await this.#A.unset(t).catch(()=>{})}#B(t,o){let a=(this.#c.get(t)??Promise.resolve()).catch(()=>{}).then(o).finally(()=>{this.#c.get(t)===a&&this.#c.delete(t)});return this.#c.set(t,a),a}#f(){this.#g+=1}};import{createHash as TBe}from"node:crypto";var NBe="https://discord.com/api/v10/",RBe=12e4,kBe=new Set([50001,50013]),vBe=new Set([40005]);function cs(n){return typeof n=="string"&&n.trim()?n.trim():null}function f3(n,t){let o=AbortSignal.timeout(t);return n?AbortSignal.any([n,o]):o}function lT(n){return n?.reason instanceof Error?n.reason:new DOMException("The operation was aborted","AbortError")}function PBe(n,t){if(!Number.isInteger(n)||n<1)throw new TypeError(`${t} must be a positive integer`);return n}function y3(n,t){return t?.providerCode!==void 0&&(n.providerCode=t.providerCode),t?.retry_after!==void 0&&(n.retry_after=t.retry_after,n.retryAfter=t.retry_after),Number.isInteger(t?.status)&&(n.status=t.status),n}function GBe(n){let t=Number(n?.providerCode),o=Number(n?.status),i=cs(n?.message)??"",e="artifact-provider-rejected",a="Discord rejected the attachment.";o===401||o===403||kBe.has(t)?(e="artifact-permission-required",a="Discord denied permission to send the attachment."):o===413||vBe.has(t)||/(?: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,y3(A,n)}function FBe(n){let t=new Error("Discord attachment delivery result is uncertain",{cause:n});return t.code="artifact-delivery-uncertain",y3(t,n)}function LBe(n,t){return new Promise((o,i)=>{if(t?.aborted){i(t.reason??new DOMException("Aborted","AbortError"));return}let e=setTimeout(o,n);e?.unref?.(),t?.addEventListener("abort",()=>{clearTimeout(e),i(t.reason??new DOMException("Aborted","AbortError"))},{once:!0})})}function sa(n,t){let o=cs(n);if(!o||!/^\d{5,30}$/.test(o))throw new TypeError(`Invalid Discord ${t}`);return o}function Q3(n){let t=cs(n);if(!t)throw new TypeError("A Discord reaction emoji is required");return t}function UBe(n){return typeof n=="string"&&/^[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{20,}$/.test(n.trim())}var cu=class{#e;#A;#t;#r;constructor({token:t,fetchImpl:o=fetch,baseUrl:i=NBe,fileUploadTimeoutMs:e=RBe}){if(!UBe(t))throw new TypeError("Discord Bot Token is invalid");if(typeof o!="function")throw new TypeError("DiscordApi requires fetch");this.#e=t.trim(),this.#A=o,this.#t=new URL(i),this.#r=PBe(e,"fileUploadTimeoutMs")}getCurrentUser(t={}){return this.#l("users/@me",{...t,method:"GET"})}getGatewayBot(t={}){return this.#l("gateway/bot",{...t,method:"GET"})}getChannel({channelId:t,signal:o}={}){return this.#l(`channels/${sa(t,"channel id")}`,{method:"GET",signal:o})}getMessage({channelId:t,messageId:o,signal:i}={}){return this.#l(`channels/${sa(t,"channel id")}/messages/${sa(o,"message id")}`,{method:"GET",signal:i})}startThreadFromMessage({channelId:t,messageId:o,name:i,signal:e}={}){let a=cs(i);if(!a||[...a].length>100)throw new TypeError("Discord thread name must contain 1-100 characters");if(e?.aborted)throw lT(e);return this.#l(`channels/${sa(t,"channel id")}/messages/${sa(o,"message id")}/threads`,{method:"POST",signal:e,body:{name:a}})}createMessage({channelId:t,content:o,replyToMessageId:i,signal:e}){return this.#l(`channels/${sa(t,"channel id")}/messages`,{method:"POST",signal:e,body:{content:o,allowed_mentions:{parse:[],replied_user:!1},...i?{message_reference:{message_id:sa(i,"message id"),channel_id:sa(t,"channel id"),fail_if_not_exists:!1}}:{}}})}async createFileMessage({channelId:t,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=cs(o.deliveryKey)??cs(o.artifactId),A=a?TBe("sha256").update(a).digest("hex").slice(0,25):void 0,r=new FormData;r.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:sa(i,"message id"),channel_id:sa(t,"channel id"),fail_if_not_exists:!1}}:{}})),r.append("files[0]",new Blob([o.bytes],{type:o.mediaType??"application/octet-stream"}),o.fileName);let s=sa(t,"channel id");if(e?.aborted)throw lT(e);let g=f3(e,this.#r);try{return await this.#l(`channels/${s}/messages`,{method:"POST",signal:g,timeoutMs:this.#r,body:r,multipart:!0})}catch(c){throw e?.aborted?lT(e):c?.code?.startsWith?.("discord-")?GBe(c):FBe(c)}}editMessage({channelId:t,messageId:o,content:i,signal:e}){return this.#l(`channels/${sa(t,"channel id")}/messages/${sa(o,"message id")}`,{method:"PATCH",signal:e,body:{content:i,allowed_mentions:{parse:[],replied_user:!1}}})}sendTyping({channelId:t,signal:o}){return this.#l(`channels/${sa(t,"channel id")}/typing`,{method:"POST",signal:o,expectBody:!1})}async addOwnReaction({channelId:t,messageId:o,emoji:i,signal:e}={}){let a=Q3(i);return await this.#l(`channels/${sa(t,"channel id")}/messages/${sa(o,"message id")}/reactions/${encodeURIComponent(a)}/@me`,{method:"PUT",signal:e,expectBody:!1,retry:!1}),a}removeOwnReaction({channelId:t,messageId:o,emoji:i,signal:e}={}){let a=Q3(i);return this.#l(`channels/${sa(t,"channel id")}/messages/${sa(o,"message id")}/reactions/${encodeURIComponent(a)}/@me`,{method:"DELETE",signal:e,expectBody:!1,retry:!1})}async#l(t,{method:o,body:i,signal:e,timeoutMs:a=15e3,expectBody:A=!0,retry:r=!0,multipart:s=!1}){let g;try{g=await this.#A(new URL(t,this.#t),{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:f3(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&&r){let I=Math.min(1e4,Math.max(50,Number(c?.retry_after)*1e3||1e3));return await LBe(I,e),this.#l(t,{method:o,body:i,signal:e,timeoutMs:a,expectBody:A,retry:!1,multipart:s})}if(!g.ok){let I=new Error(cs(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 p=Number(c?.retry_after);throw Number.isFinite(p)&&p>=0&&(I.retry_after=p,I.retryAfter=p),I}return A?c:null}};async function m3(n,t={}){let i=await new cu({token:n,...t}).getCurrentUser();if(!i?.id||i?.bot!==!0)throw new Error("Discord token does not belong to a bot");return{platformId:String(i.id),name:cs(i.global_name)??cs(i.username)??w("Discord\u673A\u5668\u4EBA"),username:cs(i.username)}}var HBe=Object.freeze({success(){},error(){},clear(){},settled:()=>Promise.resolve()});function S3(n,t){!n||typeof n!="object"||(n[t]=(n[t]??0)+1)}async function KBe(n,t){let o=AbortSignal.timeout(t),i,e=new Promise((a,A)=>{i=()=>A(o.reason??new DOMException("Timed out","TimeoutError")),o.addEventListener("abort",i,{once:!0})});try{return await Promise.race([n(o),e])}finally{o.removeEventListener("abort",i)}}function ry({adapter:n,target:t,reactions:o,status:i,logger:e=console,label:a="channel",timeoutMs:A=2e3}={}){if(!t||typeof n?.addReaction!="function"||typeof n?.removeReaction!="function"||typeof o?.processing!="string"||!o.processing||!Number.isSafeInteger(A)||A<=0)return HBe;let r=null,s=!1,g=async(B,f)=>{try{let Q=await KBe(f,A);return S3(i,B==="add"?"reactionsAdded":"reactionsRemoved"),{ok:!0,value:Q}}catch(Q){return S3(i,"reactionErrors"),e.warn?.(`[dsh-im:${a}] status reaction ${B} failed:`,Q?.message??Q?.name??String(Q)),{ok:!1,value:null}}},c=async B=>{if(r!==null){let Q=r;r=null,await g("remove",y=>n.removeReaction(t,Q,{signal:y}))}if(typeof B!="string"||!B)return;let f=await g("add",Q=>n.addReaction(t,B,{signal:Q}));f.ok&&f.value!==void 0&&f.value!==null&&(r=f.value)},I=c(o.processing),p=B=>{s||(s=!0,I=I.then(()=>c(B),()=>c(B)),I.catch(()=>{}))};return Object.freeze({success:()=>p(o.success),error:()=>p(o.error),clear:()=>p(null),settled:()=>I})}var w3="\u8FD9\u4E2A\u95EE\u9898\u5DF2\u5728\u5176\u4ED6\u5BA2\u6237\u7AEF\u5904\u7406\uFF0C\u65E0\u9700\u518D\u6B21\u56DE\u7B54\u3002",xBe="\u4EFB\u52A1\u5DF2\u5B8C\u6210\u3002";function di(n){return typeof n=="string"?n.trim():""}function YBe(n,t,o){return t.actor===o&&(n.kind!=="group"||n.addressed===!0)&&!Tt(n)&&!Dt(n)&&!!di(n.content)}function jBe(n,t,o){let i=String(n??w("\u7ED3\u679C\u6587\u4EF6")).replace(/[\r\n]+/g," ").trim().slice(0,255)||w("\u7ED3\u679C\u6587\u4EF6");switch(t?.code){case"artifact-delivery-uncertain":return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\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",{name:i});case"artifact-permission-required":return o?.key==="slack"?w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\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",{name:i}):o?.key==="discord"?w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46\u673A\u5668\u4EBA\u7F3A\u5C11 Discord \u7684 Send Messages\u3001Attach Files \u6216 Read Message History \u6743\u9650\u3002",{name:i}):o?.key==="telegram"?w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\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",{name:i}):w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\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",{name:i});case"artifact-too-large":return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u8D85\u8FC7\u5F53\u524D\u6E20\u9053\u5927\u5C0F\u4E0A\u9650\uFF0C\u672A\u53D1\u9001\u3002",{name:i});case"artifact-empty":return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001\u3002",{name:i});case"artifact-invalid":case"artifact-changed":case"artifact-unavailable":return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\u6216\u51C6\u5907\u53D1\u9001\uFF0C\u8BF7\u786E\u8BA4\u6587\u4EF6\u4ECD\u53EF\u8BBF\u95EE\u540E\u91CD\u8BD5\u3002",{name:i});case"artifact-rate-limited":return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u6682\u65F6\u88AB\u5F53\u524D\u6E20\u9053\u9650\u6D41\uFF0C\u672A\u80FD\u53D1\u9001\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",{name:i});case"artifact-provider-rejected":return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46\u5F53\u524D\u6E20\u9053\u62D2\u7EDD\u4E86\u8BE5\u6587\u4EF6\u6216\u6587\u4EF6\u6D88\u606F\u3002",{name:i});default:return w("\u7ED3\u679C\u6587\u4EF6\u300C{name}\u300D\u5DF2\u751F\u6210\uFF0C\u4F46\u5F53\u524D\u6E20\u9053\u6682\u65F6\u672A\u80FD\u53D1\u9001\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",{name:i})}}function so(){return{messagesReceived:0,messagesReplied:0,messagesRejected:0,lastMessageAt:null,lastReplyAt:null,lastRejectedAt:null,lastError:null,lastMessageError:null,reactionsAdded:0,reactionsRemoved:0,reactionErrors:0}}var Is=class{#e;#A;#t;#r;#l;#o;#i;#a;#n;#s;#p=new Map;#c=new Map;#g=new Map;#I=new Map;#d=new Set;#h=new Set;#u;#C=new ki;constructor({descriptor:t,bot:o,harness:i,state:e,contextEnhancement:a,accessPolicy:A,status:r=so(),logger:s=console,replyTimeoutMs:g=6e5,signal:c}){if(!t?.key||!t?.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=t,this.#A=o,this.#t=i,this.#r=e,this.#l=a,this.#o=A,this.#i=r,this.#a=s,this.#n=g,this.#s=c,this.#u=new Mi({label:t.key,logger:s})}get status(){return structuredClone(this.#i)}accept(t,{contextSnapshot:o,accessDecision:i}={}){if(this.#s?.aborted)return Promise.resolve();let e=di(t?.conversationId),a=t?.kind==="group"?"group":"direct",A={...t,kind:a,conversationId:e},r=di(A.messageId),s=di(A.senderId);if(!r||!s||!e||A.senderIsBot===!0||this.#r.hasSeen(r)||this.#I.has(r))return Promise.resolve();if(a==="group"&&A.addressed!==!0)return this.#i.messagesRejected+=1,this.#i.lastRejectedAt=new Date().toISOString(),this.#I.set(r,null),this.#f(A,r,null);if(this.#o||i){let I=Tt(A),p=Dt(A),B=i??Da(this.#o,{conversationType:a,senderIds:[s,di(A.senderAlternateId)].filter(Boolean),text:A.content,hasImages:I,hasFiles:p});if(!B.allowed)return this.#i.messagesRejected+=1,this.#i.lastRejectedAt=new Date().toISOString(),this.#I.set(r,null),this.#f(A,r,B.reason==="command-not-allowed"?w(no):null,{recordReceived:B.reason==="command-not-allowed"})}this.#I.set(r,o===void 0?aa(this.#l,t?.kind):o);let g=ry({adapter:this.#A,target:A.kind==="direct"||A.addressed===!0?A.reactionTarget:null,reactions:this.#e.reactions,status:this.#i,logger:this.#a,label:this.#e.key});A.statusReaction=g;let c=this.#B(A,r,s);return c.then(()=>g.success(),()=>g.error()),c}#B(t,o,i){t.kind==="direct"&&Do(this.#r,t.connectionTestTarget??t.replyTarget);let e=`${t.kind}:${t.conversationId}`,a=this.#c.get(e),A=di(t.content),r=vi(A);if(r&&t.kind==="group"&&t.addressed===!0)return this.#f(t,o,as());if(r&&t.kind==="direct"){if(/^\/batch$/iu.test(A)&&(this.#p.has(e)||a||this.#u.hasPending(e)))return this.#f(t,o,Ro());let p=this.#C.handle(e,A,{plainText:!!A&&t.plainText!==!1&&!Tt(t)&&!Dt(t)&&!Hr(t)});if(p.handled)return p.kind==="submit"?this.#Q({...t,content:p.prompt,batchSubmission:{token:p.token}},o,i,e):this.#f(t,o,p.message)}else if(t.kind==="direct"&&this.#C.status(e).phase==="collecting"){let I=this.#C.handle(e,A,{plainText:!!A&&t.plainText!==!1&&!Tt(t)&&!Dt(t)&&!Hr(t)});if(I.handled)return this.#f(t,o,I.message)}let g=t.kind==="direct"&&this.#C.status(e).phase==="collecting"?null:xa(A)?ao:Dt(t)?null:ci(A)?Ni:Ii(A)?io:li(A)?Ri:null;if(g&&(t.kind!=="group"||t.addressed===!0)){let I;return I=this.#S(t,o,e,g).finally(()=>{this.#I.delete(o),this.#h.delete(I)}),this.#h.add(I),I}let c=this.#u.claimReply({key:e,actor:i,messageId:o,text:Tt(t)||Dt(t)?"":t.content,addressed:t.kind!=="group"||t.addressed===!0,hasPendingQuestion:!!a,questionCompletion:a?.submitting||a?.claimedReplyMessageId?a.queue:null,isQuestionPending:()=>this.#c.has(e),send:I=>this.#A.sendText(t.replyTarget,I)});if(c){let I;return I=c.process(async()=>this.#r.hasSeen(o)?!1:(await this.#r.markSeen(o),this.#i.messagesReceived+=1,this.#i.lastMessageAt=new Date().toISOString(),!0)).finally(()=>{this.#I.delete(o),this.#d.delete(I)}),this.#d.add(I),I}if(a&&a.actor!==i)return this.#Q(t,o,i,e);if(a?.submitting||a?.claimedReplyMessageId)return this.#Q(t,o,i,e);if(a){YBe(t,a,i)&&(a.claimedReplyMessageId=o);let p=(a.queue??Promise.resolve()).catch(()=>{}).then(()=>this.#D(t,o,i,e,a)).finally(()=>{this.#I.delete(o),a.claimedReplyMessageId===o&&(a.claimedReplyMessageId=null),a.queue===p&&(a.queue=null)});return a.queue=p,p}return this.#Q(t,o,i,e)}#f(t,o,i,{recordReceived:e=!0}={}){let a;return a=(async()=>{this.#r.hasSeen(o)||(await this.#r.markSeen(o),e&&(this.#i.messagesReceived+=1,this.#i.lastMessageAt=new Date().toISOString()),i&&await this.#A.sendText(t.replyTarget,i),this.#i.lastError=null)})().catch(async A=>{if(this.#s?.aborted){t.statusReaction?.clear();return}t.statusReaction?.error(),this.#i.lastError=A?.message??String(A);let r=tt(this.#i,A);this.#a.error?.(`[dsh-im:${this.#e.key}] failed to process a batch input message [${r.referenceId}]:`,A)}).finally(()=>{this.#I.delete(o),this.#h.delete(a)}),this.#h.add(a),a}#Q(t,o,i,e,{releaseMessageId:a=!0,alreadyRecorded:A=!1}={}){let s=(this.#p.get(e)??Promise.resolve()).catch(()=>{}).then(()=>this.#m(t,o,i,e,{alreadyRecorded:A})).finally(()=>{a&&this.#I.delete(o),this.#p.get(e)===s&&this.#p.delete(e)});return this.#p.set(e,s),s}async waitForIdle(){await Promise.allSettled([...this.#p.values(),...[...this.#c.values()].flatMap(t=>t.queue?[t.queue]:[]),...this.#d,...this.#h])}async#S(t,o,i,e){if(this.#r.hasSeen(o))return;await this.#r.markSeen(o),this.#i.messagesReceived+=1,this.#i.lastMessageAt=new Date().toISOString();let a=t.replyTarget;try{let A=await e(di(t.content),this.#t,this.#r,i,{signal:this.#s,isDirect:t.kind==="direct",hasImages:Tt(t),hasFiles:Dt(t),pendingInteraction:this.#c.has(i)||this.#u.hasPending(i),control:{owner:this,key:i}});A?.stopped&&await Promise.allSettled([this.#P(i),this.#u.closeRoute(i)]);for(let r of A?.messages??[A?.message])r&&await this.#A.sendText(a,r);this.#i.lastError=null}catch(A){if(A?.code==="turn-stopped"||this.#s?.aborted){t.statusReaction?.clear();return}t.statusReaction?.error(),this.#i.lastError=A?.message??String(A);let r=tt(this.#i,A);this.#a.error?.(`[dsh-im:${this.#e.key}] failed to process a command [${r.referenceId}]:`,A),await this.#A.sendText(a,Et(r)).catch(()=>{})}}sendConnectionTest(t){return cc({state:this.#r,text:t,channelLabel:w("{label}\u673A\u5668\u4EBA",{label:this.#e.label}),send:(o,i)=>this.#A.sendText(o,i)})}sendProactiveText(t,o,{signal:i}={}){return i?.throwIfAborted(),this.#A.sendText(t,o)}async#y(t,o,i=[],e){let a=await Po({artifacts:i,baseReceipt:e,deliveryId:o,channelKey:this.#e.key,signal:this.#s,sendImage:typeof this.#A.sendImage=="function"?A=>this.#A.sendImage(t,A):void 0,sendFile:typeof this.#A.sendFile=="function"?A=>this.#A.sendFile(t,A):void 0,onFailure:(A,r)=>tt(this.#i,r,{userMessage:jBe(A?.fileName,r,this.#e),reason:r?.code}),sendFailureNotice:(A,r,s)=>this.#A.sendText(t,Et(s)),logger:this.#a});return this.#i.artifactsSent=(this.#i.artifactsSent??0)+a.artifactsSent,this.#i.artifactSendErrors=(this.#i.artifactSendErrors??0)+a.artifactSendErrors,{receipt:a.receipt,userVisible:a.userVisible,artifactSendErrors:a.artifactSendErrors}}async#m(t,o,i,e,{alreadyRecorded:a=!1}={}){if(!a){if(this.#r.hasSeen(o))return;await this.#r.markSeen(o),this.#i.messagesReceived+=1,this.#i.lastMessageAt=new Date().toISOString()}let A=t.replyTarget,r=di(t.content),s=t.batchSubmission,g=null,c=!1;try{if(this.#s?.throwIfAborted(),t.kind==="group"&&t.addressed!==!0){this.#i.messagesRejected+=1,this.#i.lastRejectedAt=new Date().toISOString();return}let I=Tt(t),p=Dt(t),B=Hr(t);if(!r&&!I&&!p&&!B){await this.#A.sendText(A,w("\u76EE\u524D\u652F\u6301\u6587\u5B57\u3001\u56FE\u7247\u548C\u6587\u4EF6\u6D88\u606F\u3002"));return}let f=r.toLowerCase();if(!I&&!p&&f==="/help"){await this.#A.sendText(A,[w("{label}\u673A\u5668\u4EBA\u5DF2\u8FDE\u63A5 DeepSeek Harness\u3002",{label:this.#e.label}),"",w("\u76F4\u63A5\u53D1\u9001\u6587\u5B57\u3001\u56FE\u7247\u6216\u6587\u4EF6\u5373\u53EF\u7EE7\u7EED\u5F53\u524D\u4F1A\u8BDD\u3002"),w("/new \u5F00\u542F\u4E00\u4E2A\u5168\u65B0\u4F1A\u8BDD"),w("/compact \u538B\u7F29\u5F53\u524D\u4F1A\u8BDD\u7684\u8F83\u65E9\u4E0A\u4E0B\u6587"),w("/history [\u6570\u91CF] \u67E5\u770B\u6700\u8FD1\u5386\u53F2\u6D88\u606F\uFF08\u9ED8\u8BA4 3 \u6761\uFF0C\u6700\u591A 5 \u6761\uFF09"),w("/workspace \u5DE5\u4F5C\u533A\u5E8F\u53F7\u6216\u7EDD\u5BF9\u8DEF\u5F84 \u5207\u6362\u5DE5\u4F5C\u533A"),w("/workspacelist \u5217\u51FA\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84"),w("/sessionlist \u6216 /sessions [\u5DE5\u4F5C\u533A\u5E8F\u53F7\u6216\u7EDD\u5BF9\u8DEF\u5F84] \u5217\u51FA\u4F1A\u8BDD ID \u548C\u6807\u9898"),w("/session Session ID \u6216\u5F53\u524D\u5DE5\u4F5C\u533A\u5E8F\u53F7 \u5C06\u5F53\u524D\u804A\u5929\u7ED1\u5B9A\u5230\u6307\u5B9A\u4F1A\u8BDD"),w("/models \u6309\u5E8F\u53F7\u5217\u51FA\u6240\u6709\u53EF\u7528\u6A21\u578B"),w("/reasoninglist \u6216 /reasonings \u6309\u5E8F\u53F7\u5217\u51FA\u5F53\u524D\u6A21\u578B\u53EF\u7528\u63A8\u7406\u7B49\u7EA7"),w("/reasoning [\u5E8F\u53F7\u3001\u7B49\u7EA7ID\u6216 --default] \u67E5\u770B\u6216\u5207\u6362\u5F53\u524D\u63A8\u7406\u7B49\u7EA7"),w("/model [\u5E8F\u53F7\u6216\u5B8C\u6574\u6A21\u578BID] [\u63A8\u7406\u7B49\u7EA7ID] \u67E5\u770B\u6216\u5207\u6362\u5F53\u524D\u4F1A\u8BDD\u6A21\u578B"),w("\u793A\u4F8B\uFF1A\u5148\u53D1 /models\uFF0C\u518D\u53D1 /model 2 [\u63A8\u7406\u7B49\u7EA7ID]"),w("/presetlist \u6216 /presets \u6309\u5E8F\u53F7\u5217\u51FA\u53EF\u7528 Agent Preset"),w("/preset [\u5E8F\u53F7\u6216\u5B8C\u6574ID] \u67E5\u770B\u6216\u8BBE\u7F6E\u5F53\u524D\u673A\u5668\u4EBA Agent Preset"),w("\u7EAF\u6570\u5B57 ID\uFF1A/preset id:"),w("/preset --default \u8DDF\u968F Host \u9ED8\u8BA4"),w("/stop \u505C\u6B62\u5F53\u524D\u4EFB\u52A1"),w("/steer \u8865\u5145\u6307\u4EE4 \u7EA0\u504F\u5F53\u524D\u4EFB\u52A1"),w("/batch \u5F00\u59CB\u6279\u91CF\u8F93\u5165\uFF08\u4EC5\u79C1\u804A\uFF0C\u6700\u591A 10 \u6761\u6587\u5B57\uFF09"),w("/send \u63D0\u4EA4\u5F53\u524D\u6279\u6B21"),w("/cancel \u53D6\u6D88\u5F53\u524D\u6279\u6B21"),w("/status \u68C0\u67E5\u8FDE\u63A5\u72B6\u6001"),w("/version \u67E5\u770B\u63D2\u4EF6\u7248\u672C"),w("/help \u663E\u793A\u672C\u5E2E\u52A9")].join(` `));return}if(!I&&!p&&f==="/status"){await this.#t.ensureRunning({signal:this.#s}),await this.#A.sendText(A,w("{label}\u673A\u5668\u4EBA\u4E0E DeepSeek Harness \u8FDE\u63A5\u6B63\u5E38\u3002",{label:this.#e.label}));return}let Q=!I&&!p?await _o(r,this.#t,e):null;if(Q){for(let N of Q.messages??[Q.message])await this.#A.sendText(A,N);return}if(!I&&!p&&f==="/new"){await this.#r.clearSession(e),await this.#A.sendText(A,w("\u5DF2\u5F00\u542F\u65B0\u4F1A\u8BDD\u3002\u8BF7\u53D1\u9001\u4F60\u7684\u95EE\u9898\u3002"));return}let y=!I&&!p?await Ti(r,this.#t,this.#r,e,{signal:this.#s}):null;if(y){await this.#A.sendText(A,y.message);return}await this.#A.sendTyping?.(A).catch(N=>{this.#a.warn?.(`[dsh-im:${this.#e.key}] typing indicator failed:`,N)});let m=!1;if(typeof this.#A.openDeliveryStream=="function")try{g=await this.#A.openDeliveryStream(A),c=!0}catch(N){this.#a.warn?.(`[dsh-im:${this.#e.key}] unable to start a semantic reply stream; using final delivery:`,N)}else if(typeof this.#A.openStream=="function")try{g=await this.#A.openStream(A)}catch(N){this.#a.warn?.(`[dsh-im:${this.#e.key}] unable to start a streamed reply; using text:`,N)}let T=I||B?await Go(t,{signal:this.#s}):void 0,_=this.#I.get(o),R=!1;if(_){let N=T??r;T=No(N,_,()=>({channel:this.#e.key,senderId:i,senderName:t.contextSource?.()?.senderName,conversationTitle:t.contextSource?.()?.conversationTitle})),R=T!==N}let{answer:x,artifacts:F=[]}=await _i({harness:this.#t,state:this.#r,key:e,text:r,content:T,contextEnhanced:R,createOptions:this.#s?{signal:this.#s}:void 0,existsOptions:this.#s?{signal:this.#s}:void 0,askOptions:{timeoutMs:this.#n,signal:this.#s,control:{owner:this,key:e},onUpdate:g?async N=>{let k=N.type==="text"?N.text:N.type==="tool"?w("\u6B63\u5728\u4F7F\u7528{name}\u2026",{name:N.name}):N.text;if(k){let J=N.type==="text"?"markdown":"plain";await g.update(c?os(k,J):k)}}:void 0,onInteraction:N=>this.#E(N,{key:e,actor:i,target:A,requiresMention:t.kind==="group"&&t.requiresMention!==!1}),onInteractionResolved:N=>this.#R(N),files:t.files}});s&&this.#C.complete(e,s.token);let L=!di(x)&&F.length>0,q=L?w(xBe):x,z=L?"plain":"markdown",H=null,G=null;if(g)try{let N=await g.finish(c?os(q,z):q);m=!0,G=$t({deliveryId:o,presentation:N?.presentation??g.presentation??`${this.#e.key}-stream`,providerMessageIds:[...Ur(g),...Ur(N)],deliveryOutcome:N?.deliveryOutcome,reason:N?.reason})}catch(N){g.cancel?.(),this.#a.warn?.(`[dsh-im:${this.#e.key}] streamed reply finalization failed; using text:`,N)}if(!m)try{let N=typeof this.#A.sendDelivery=="function"?await this.#A.sendDelivery(A,os(q,z)):await this.#A.sendText(A,q);G=$t({deliveryId:o,presentation:N?.presentation??`${this.#e.key}-text`,providerMessageIds:Ur(N),deliveryOutcome:N?.deliveryOutcome,reason:N?.reason})}catch(N){H=Ua(N)}let V=G?.deliveryOutcome==="unknown";G?.deliveryOutcome==="failed"?H=Ua(new Error(`Final text delivery failed (${G.reason??"unknown"})`),{uncertain:!1}):V&&(H=Ua(new Error(`Final text delivery outcome is unknown (${G.reason??"unknown"})`)));let U=await this.#y(A,o,F,G);if(H&&(!U.userVisible||V))throw H.deliveryReceipt=U.receipt,H;return H&&U.artifactSendErrors===0&&tt(this.#i,H),U.userVisible&&(this.#i.messagesReplied+=1,this.#i.lastReplyAt=new Date().toISOString(),this.#i.lastError=null,!H&&U.artifactSendErrors===0&&Oo(this.#i)),U.receipt}catch(I){let p=I?.code==="turn-stopped";s&&p&&this.#C.complete(e,s.token);let B=s&&!p?this.#C.fail(e,s.token):null;if(p){if(t.statusReaction?.clear(),g)try{await g.finish(w("\u5DF2\u505C\u6B62\u3002"))}catch{g.cancel?.()}return}if(this.#s?.aborted){t.statusReaction?.clear(),g?.cancel?.();return}t.statusReaction?.error(),this.#i.lastError=I?.message??String(I);let f=async _=>{let R=typeof g?.fail=="function"?"fail":typeof g?.finish=="function"?"finish":null;if(!R)return!1;try{let x=await g[R](_);return R==="fail"?!!x&&x.deliveryOutcome!=="failed":x!==!1&&x?.deliveryOutcome!=="failed"}catch(x){return this.#a.warn?.(`[dsh-im:${this.#e.key}] unable to finalize the failed stream:`,x),!1}},Q=Mo(I),y=vo(I),m=tt(this.#i,I,{userMessage:y??Q,reason:ro(I)?.reason}),T=B?.retained?`${Et(m)} diff --git a/src/channels/shared/harness-client.mjs b/src/channels/shared/harness-client.mjs index bd66ba2..dd4f125 100644 --- a/src/channels/shared/harness-client.mjs +++ b/src/channels/shared/harness-client.mjs @@ -1415,7 +1415,7 @@ export class HarnessClient { try { stagedImages = await this.#stageWorkspaceFiles(sessionId, imageSources, signal); } catch (stagingError) { - if (signal?.aborted) throw error; + if (signal?.aborted) throw signal.reason ?? stagingError; console.warn( `[${this.#logPrefix}] unable to restage rejected images as workspace files:`, stagingError?.message ?? String(stagingError), diff --git a/test/image-fallback.test.mjs b/test/image-fallback.test.mjs index 81d8c63..b58e28f 100644 --- a/test/image-fallback.test.mjs +++ b/test/image-fallback.test.mjs @@ -235,6 +235,29 @@ test('a failed restage keeps the original model-rejection error for the user', a assert.equal(promptCalls.length, 1); }); +test('caller cancellation wins while rejected images are being restaged', async (t) => { + const root = await workspace(t); + const controller = new AbortController(); + const cancellation = new Error('caller cancelled image fallback'); + const { client, promptCalls } = scriptedClient({ + workspaceRoot: root, + fileIngressExecutor: async () => { + controller.abort(cancellation); + throw cancellation; + }, + onPrompt: (attempt) => (attempt === 1 ? Promise.reject(modelImageRejection()) : Promise.resolve({})), + }); + + await assert.rejects( + client.ask('session-fallback', imageContent(), { + timeoutMs: 3_000, + signal: controller.signal, + }), + (error) => error === cancellation, + ); + assert.equal(promptCalls.length, 1); +}); + test('the fallback retry itself is never retried again', async (t) => { const root = await workspace(t); const { client, promptCalls } = scriptedClient({