shennian 0.4.10 → 0.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/dist/publish-build-manifest.json +185 -55
  2. package/dist/src/agents/acp/adapter.d.ts +78 -0
  3. package/dist/src/agents/acp/adapter.js +3 -0
  4. package/dist/src/agents/acp/auth-state.d.ts +3 -0
  5. package/dist/src/agents/acp/auth-state.js +1 -0
  6. package/dist/src/agents/acp/auth.d.ts +7 -0
  7. package/dist/src/agents/acp/auth.js +1 -0
  8. package/dist/src/agents/acp/capability-cache.d.ts +14 -0
  9. package/dist/src/agents/acp/capability-cache.js +2 -0
  10. package/dist/src/agents/acp/catalog.d.ts +11 -0
  11. package/dist/src/agents/acp/catalog.js +2 -0
  12. package/dist/src/agents/acp/client-services.d.ts +32 -0
  13. package/dist/src/agents/acp/client-services.js +3 -0
  14. package/dist/src/agents/acp/discovery.d.ts +9 -0
  15. package/dist/src/agents/acp/discovery.js +1 -0
  16. package/dist/src/agents/acp/events.d.ts +4 -0
  17. package/dist/src/agents/acp/events.js +3 -0
  18. package/dist/src/agents/acp/manifests.d.ts +63 -0
  19. package/dist/src/agents/acp/manifests.js +2 -0
  20. package/dist/src/agents/acp/metrics.d.ts +24 -0
  21. package/dist/src/agents/acp/metrics.js +1 -0
  22. package/dist/src/agents/acp/package-commands.d.ts +4 -0
  23. package/dist/src/agents/acp/package-commands.js +1 -0
  24. package/dist/src/agents/acp/process-client.d.ts +39 -0
  25. package/dist/src/agents/acp/process-client.js +1 -0
  26. package/dist/src/agents/acp/process-tree.d.ts +2 -0
  27. package/dist/src/agents/acp/process-tree.js +1 -0
  28. package/dist/src/agents/acp/types.d.ts +31 -0
  29. package/dist/src/agents/acp/types.js +1 -0
  30. package/dist/src/agents/acp/watcher.d.ts +22 -0
  31. package/dist/src/agents/acp/watcher.js +1 -0
  32. package/dist/src/agents/adapter.d.ts +96 -2
  33. package/dist/src/agents/adapter.js +1 -1
  34. package/dist/src/agents/claude.d.ts +14 -0
  35. package/dist/src/agents/claude.js +5 -5
  36. package/dist/src/agents/codex.d.ts +6 -0
  37. package/dist/src/agents/codex.js +1 -1
  38. package/dist/src/agents/command-spec.d.ts +5 -0
  39. package/dist/src/agents/command-spec.js +3 -3
  40. package/dist/src/agents/cursor.d.ts +6 -0
  41. package/dist/src/agents/cursor.js +1 -1
  42. package/dist/src/agents/custom-recovery-conformance.d.ts +2 -0
  43. package/dist/src/agents/custom-recovery-conformance.js +2 -0
  44. package/dist/src/agents/custom.d.ts +9 -0
  45. package/dist/src/agents/custom.js +4 -4
  46. package/dist/src/agents/detect.d.ts +11 -2
  47. package/dist/src/agents/detect.js +1 -1
  48. package/dist/src/agents/gemini.d.ts +6 -0
  49. package/dist/src/agents/gemini.js +1 -1
  50. package/dist/src/agents/manager.d.ts +2 -0
  51. package/dist/src/agents/manager.js +5 -5
  52. package/dist/src/agents/model-registry/service.js +1 -1
  53. package/dist/src/agents/opencode.d.ts +6 -0
  54. package/dist/src/agents/opencode.js +1 -1
  55. package/dist/src/agents/pi-coding-agent.d.ts +6 -0
  56. package/dist/src/agents/pi-coding-agent.js +4 -4
  57. package/dist/src/agents/workbuddy.d.ts +6 -0
  58. package/dist/src/agents/workbuddy.js +2 -2
  59. package/dist/src/commands/agent.d.ts +2 -0
  60. package/dist/src/commands/agent.js +2 -0
  61. package/dist/src/commands/daemon.d.ts +6 -2
  62. package/dist/src/commands/daemon.js +19 -23
  63. package/dist/src/commands/pair.js +10 -10
  64. package/dist/src/commands/upgrade.d.ts +7 -1
  65. package/dist/src/commands/upgrade.js +1 -1
  66. package/dist/src/config/index.d.ts +1 -1
  67. package/dist/src/config/index.js +2 -2
  68. package/dist/src/daemon-ipc/control.d.ts +16 -0
  69. package/dist/src/daemon-ipc/control.js +1 -0
  70. package/dist/src/index.js +3 -3
  71. package/dist/src/log-reporter.d.ts +23 -0
  72. package/dist/src/log-reporter.js +1 -1
  73. package/dist/src/relay/client.d.ts +1 -0
  74. package/dist/src/relay/client.js +1 -1
  75. package/dist/src/room/activation-service.d.ts +2 -0
  76. package/dist/src/room/activation-service.js +5 -5
  77. package/dist/src/session/handlers/agents.d.ts +1 -0
  78. package/dist/src/session/handlers/agents.js +1 -1
  79. package/dist/src/session/handlers/chat.d.ts +3 -0
  80. package/dist/src/session/handlers/chat.js +2 -2
  81. package/dist/src/session/manager.d.ts +23 -2
  82. package/dist/src/session/manager.js +3 -1
  83. package/dist/src/session/queue.d.ts +7 -1
  84. package/dist/src/session/queue.js +8 -8
  85. package/dist/src/session/run-recovery/barrier.d.ts +12 -0
  86. package/dist/src/session/run-recovery/barrier.js +1 -0
  87. package/dist/src/session/run-recovery/coordinator.d.ts +32 -0
  88. package/dist/src/session/run-recovery/coordinator.js +1 -0
  89. package/dist/src/session/run-recovery/journal.d.ts +30 -0
  90. package/dist/src/session/run-recovery/journal.js +2 -0
  91. package/dist/src/session/run-recovery/schema.d.ts +43 -0
  92. package/dist/src/session/run-recovery/schema.js +1 -0
  93. package/dist/src/session/types.d.ts +10 -0
  94. package/dist/src/upgrade/engine.d.ts +6 -5
  95. package/dist/src/upgrade/engine.js +2 -2
  96. package/dist/src/upgrade/scheduler.d.ts +14 -0
  97. package/dist/src/upgrade/scheduler.js +1 -1
  98. package/dist/src/upgrade/updater-bootstrap.d.ts +12 -0
  99. package/dist/src/upgrade/updater-bootstrap.js +2 -0
  100. package/dist/src/upgrade/updater-launcher.d.ts +28 -0
  101. package/dist/src/upgrade/updater-launcher.js +3 -0
  102. package/dist/src/upgrade/updater-protocol.d.ts +37 -0
  103. package/dist/src/upgrade/updater-protocol.js +1 -0
  104. package/dist/src/upgrade/updater-worker.d.ts +11 -0
  105. package/dist/src/upgrade/updater-worker.js +2 -0
  106. package/node_modules/@shennian/wire/dist/events.d.ts +31 -0
  107. package/node_modules/@shennian/wire/dist/index.d.ts +1 -0
  108. package/node_modules/@shennian/wire/dist/index.js +1 -0
  109. package/node_modules/@shennian/wire/dist/machine.d.ts +49 -1
  110. package/node_modules/@shennian/wire/dist/message-payload.d.ts +1 -0
  111. package/node_modules/@shennian/wire/dist/message-payload.js +4 -1
  112. package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
  113. package/node_modules/@shennian/wire/dist/relay-connectivity.d.ts +30 -0
  114. package/node_modules/@shennian/wire/dist/relay-connectivity.js +29 -0
  115. package/node_modules/@shennian/wire/dist/session-sync.d.ts +2 -2
  116. package/node_modules/@shennian/wire/dist/session-sync.js +2 -0
  117. package/node_modules/@shennian/wire/dist/session.d.ts +86 -1
  118. package/node_modules/@shennian/wire/dist/session.js +5 -1
  119. package/package.json +5 -3
  120. package/dist/native/macos-background-client/Shennian.app/Contents/CodeResources +0 -0
  121. package/dist/native/macos-background-client/Shennian.app/Contents/Info.plist +0 -17
  122. package/dist/native/macos-background-client/Shennian.app/Contents/MacOS/ShennianClientLauncher +0 -0
  123. package/dist/native/macos-background-client/Shennian.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -2
  124. package/dist/native/macos-background-client/Shennian.app/Contents/Resources/zh-Hans.lproj/InfoPlist.strings +0 -2
  125. package/dist/native/macos-background-client/Shennian.app/Contents/_CodeSignature/CodeResources +0 -153
@@ -1 +1 @@
1
- import w from"ws";import{generateTraceId as h}from"../log-reporter.js";import{recordPersonalSyncLocalMetric as c}from"../personal-sync/metrics.js";class R extends Error{rejection;constructor(e){super(e.message||e.code),this.rejection=e,this.name="SessionSyncRejectedError"}}const y=9e4,S=3e4,P=1e3,I=.2,A=2e4,_=1e4,C=15e3,b=1e3,k=new Set([4001,4003,4004,4009,4402,4403,4406]),v=["upgrade_required","epoch_mismatch","token_revoked","ownership_transferred","invalid_identity"];function N(l,e){if(k.has(l))return!0;const t=e.trim().toLowerCase();return v.some(s=>t.startsWith(s))}class j{options;ws=null;state="disconnected";reconnectAttempts=0;heartbeatTimer=null;reconnectTimer=null;connectTimer=null;pingTimer=null;pongTimer=null;disposed=!1;closeTrigger;lastSocketError;sendBuffer=new Map;pendingAcks=new Map;pendingRequests=new Map;constructor(e){this.options=e}connect(){if(this.disposed)return;this.ws&&this.cleanup(!1),this.state="connecting";const e=this.options.serverUrl.includes("?")?"&":"?",t=this.options.cliVersion?`&v=${encodeURIComponent(this.options.cliVersion)}`:"",s=this.options.agentList?.length?`&agents=${encodeURIComponent(this.options.agentList.join(","))}`:"",i=this.options.autoUpgradePolicy?`&upgradePolicy=${encodeURIComponent(this.options.autoUpgradePolicy)}`:"",r=this.options.sessionSyncProtocolVersion?`&syncProtocol=${this.options.sessionSyncProtocolVersion}`:"",o=this.options.sessionSyncCapabilities?.length?`&syncCapabilities=${encodeURIComponent(this.options.sessionSyncCapabilities.join(","))}`:"",u=this.options.daemonInstallationId?`&daemonInstallationId=${encodeURIComponent(this.options.daemonInstallationId)}`:"",g=this.options.dataEpoch?`&dataEpoch=${encodeURIComponent(this.options.dataEpoch)}`:"",f=`${this.options.serverUrl}${e}token=${encodeURIComponent(this.options.machineToken)}${t}${s}${i}${r}${o}${u}${g}`,n=new w(f);this.ws=n,this.connectTimer=setTimeout(()=>{this.ws===n&&this.state==="connecting"&&(this.closeTrigger="connect-timeout",n.terminate())},C),n.on("open",()=>{this.ws===n&&(this.clearConnectTimer(),this.state="connected",this.reconnectAttempts=0,this.startHeartbeat(),this.startPing(n),this.flushSendBuffer(),this.options.onConnected?.())}),n.on("pong",()=>{this.ws===n&&(this.clearPongTimer(),this.resetHeartbeat())}),n.on("message",a=>{if(this.ws!==n)return;let d;try{d=JSON.parse(a.toString())}catch{return}this.handleFrame(d)}),n.on("close",(a,d)=>{if(this.ws!==n)return;const T=this.state,m=d.toString(),p=N(a,m),E={code:a,reason:m,error:this.lastSocketError,phase:T,trigger:this.closeTrigger??"socket-close",reconnectAttempt:this.reconnectAttempts+1};p&&(this.disposed=!0,c("permanentStops")),this.cleanup(p),this.options.onDisconnected?.(E),p||this.scheduleReconnect()}),n.on("error",a=>{this.lastSocketError=a.message,this.closeTrigger="socket-error",n.close()})}disconnect(){this.disposed=!0,this.cleanup(!0)}sendRes(e){this.state!=="connected"||!this.ws||(e.traceId||(e.traceId=h()),this.ws.send(JSON.stringify(e)))}sendResAndWait(e){if(this.state!=="connected"||!this.ws)return Promise.reject(new Error("Relay is not connected"));e.traceId||(e.traceId=h());const t=this.ws;return new Promise((s,i)=>{t.send(JSON.stringify(e),r=>{r?i(r):s()})})}sendEvent(e){this.state!=="connected"||!this.ws||(e.traceId||(e.traceId=h()),c("relayEventsSent"),this.ws.send(JSON.stringify(e)))}sendReq(e,t=6e4){return this.state!=="connected"||!this.ws?Promise.reject(new Error("Relay is not connected")):(e.traceId||(e.traceId=h()),c("relayRequestsSent"),new Promise((s,i)=>{const r=this.pendingRequests.get(e.id);r&&(clearTimeout(r.timer),r.reject(new Error("Superseded by a newer relay request")));const o=setTimeout(()=>{this.pendingRequests.delete(e.id),i(new Error("Relay request timed out"))},t);this.pendingRequests.set(e.id,{resolve:s,reject:i,timer:o}),this.ws?.send(JSON.stringify(e))}))}sendBufferedEvent(e,t=12e4){if(e.traceId||(e.traceId=h()),!e.id)return this.sendEvent(e),Promise.resolve();const s=e.id;return c("relayEventsSent"),new Promise((i,r)=>{const o=this.pendingAcks.get(s);o&&(clearTimeout(o.timer),o.reject(new Error("Superseded by a newer buffered event")));const u=setTimeout(()=>{this.pendingAcks.delete(s),this.sendBuffer.delete(s),r(new Error("Relay event acknowledgement timed out"))},t);this.pendingAcks.set(s,{resolve:i,reject:r,timer:u}),this.bufferEvent(e)})}sendAgentEvent(e){this.sendBufferedEvent(e).catch(()=>{})}getState(){return this.state}handleFrame(e){if(c("relayFramesReceived"),e.type==="res"&&e.rejection&&c("structuredRejections"),e.type==="event"&&e.event==="tick"){this.resetHeartbeat();return}if(e.type==="res"&&e.id){this.sendBuffer.delete(e.id);const t=this.pendingAcks.get(e.id);if(t)if(this.pendingAcks.delete(e.id),clearTimeout(t.timer),e.ok)t.resolve(e);else{const i=e.rejection;t.reject(i?new R(i):new Error(e.error??"Relay event failed"))}const s=this.pendingRequests.get(e.id);s&&(this.pendingRequests.delete(e.id),clearTimeout(s.timer),s.resolve(e));return}e.type==="req"&&this.options.onReq?.(e)}flushSendBuffer(){if(!(this.sendBuffer.size===0||!this.ws))for(const e of this.sendBuffer.values()){if(this.state!=="connected"||!this.ws)break;this.ws.send(JSON.stringify(e))}}bufferEvent(e){if(!e.id){this.sendEvent(e);return}if(this.sendBuffer.set(e.id,e),this.sendBuffer.size>b){const t=this.sendBuffer.keys().next().value;if(t){this.sendBuffer.delete(t);const s=this.pendingAcks.get(t);s&&(this.pendingAcks.delete(t),clearTimeout(s.timer),s.reject(new Error("Relay send buffer overflow")))}}this.state==="connected"&&this.ws&&this.ws.send(JSON.stringify(e))}clearConnectTimer(){this.connectTimer!==null&&(clearTimeout(this.connectTimer),this.connectTimer=null)}startPing(e){this.clearPing(),this.pingTimer=setInterval(()=>{if(!(this.ws!==e||this.state!=="connected")){this.pongTimer=setTimeout(()=>{this.closeTrigger="pong-timeout",e.close()},_);try{e.ping()}catch{this.clearPongTimer(),this.closeTrigger="socket-error",e.close()}}},A)}clearPing(){this.pingTimer!==null&&(clearInterval(this.pingTimer),this.pingTimer=null),this.clearPongTimer()}clearPongTimer(){this.pongTimer!==null&&(clearTimeout(this.pongTimer),this.pongTimer=null)}startHeartbeat(){this.clearHeartbeat(),this.heartbeatTimer=setTimeout(()=>{this.closeTrigger="heartbeat-timeout",this.ws?.close()},y)}resetHeartbeat(){this.startHeartbeat()}clearHeartbeat(){this.heartbeatTimer!==null&&(clearTimeout(this.heartbeatTimer),this.heartbeatTimer=null)}scheduleReconnect(){if(this.disposed)return;const e=Math.min(P*Math.pow(2,this.reconnectAttempts),S),t=e*I*(Math.random()*2-1),s=Math.round(e+t);this.reconnectAttempts++,c("relayReconnects"),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connect()},s)}cleanup(e){if(this.state="disconnected",this.closeTrigger=void 0,this.lastSocketError=void 0,this.clearConnectTimer(),this.clearHeartbeat(),this.clearPing(),this.reconnectTimer!==null&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws){const t=this.ws;this.ws=null,t.removeAllListeners();try{t.close()}catch{}}for(const[t,s]of this.pendingRequests)clearTimeout(s.timer),s.reject(new Error("Relay client disconnected")),this.pendingRequests.delete(t);if(e)for(const[t,s]of this.pendingAcks)clearTimeout(s.timer),s.reject(new Error("Relay client disconnected")),this.pendingAcks.delete(t)}}export{j as CliRelayClient,R as SessionSyncRejectedError,N as isPermanentRelayClose};
1
+ import R from"ws";import{generateTraceId as d}from"../log-reporter.js";import{recordPersonalSyncLocalMetric as c}from"../personal-sync/metrics.js";class y extends Error{rejection;constructor(e){super(e.message||e.code),this.rejection=e,this.name="SessionSyncRejectedError"}}const P=9e4,S=3e4,I=1e3,A=.2,C=2e4,_=1e4,b=15e3,k=1e3,v=new Set([4001,4003,4004,4009,4402,4403,4406]),N=["upgrade_required","epoch_mismatch","token_revoked","ownership_transferred","invalid_identity"];function O(l,e){if(v.has(l))return!0;const t=e.trim().toLowerCase();return N.some(s=>t.startsWith(s))}class j{options;ws=null;state="disconnected";reconnectAttempts=0;heartbeatTimer=null;reconnectTimer=null;connectTimer=null;pingTimer=null;pongTimer=null;disposed=!1;closeTrigger;lastSocketError;sendBuffer=new Map;pendingAcks=new Map;pendingRequests=new Map;constructor(e){this.options=e}connect(){if(this.disposed)return;this.ws&&this.cleanup(!1),this.state="connecting";const e=this.options.serverUrl.includes("?")?"&":"?",t=this.options.cliVersion?`&v=${encodeURIComponent(this.options.cliVersion)}`:"",s=this.options.agentList?.length?`&agents=${encodeURIComponent(this.options.agentList.join(","))}`:"",i=this.options.autoUpgradePolicy?`&upgradePolicy=${encodeURIComponent(this.options.autoUpgradePolicy)}`:"",o=this.options.sessionSyncProtocolVersion?`&syncProtocol=${this.options.sessionSyncProtocolVersion}`:"",r=this.options.sessionSyncCapabilities?.length?`&syncCapabilities=${encodeURIComponent(this.options.sessionSyncCapabilities.join(","))}`:"",u=this.options.daemonInstallationId?`&daemonInstallationId=${encodeURIComponent(this.options.daemonInstallationId)}`:"",g=this.options.dataEpoch?`&dataEpoch=${encodeURIComponent(this.options.dataEpoch)}`:"",f=this.options.updaterProtocolVersion?`&updaterProtocol=${encodeURIComponent(this.options.updaterProtocolVersion)}`:"",T=`${this.options.serverUrl}${e}token=${encodeURIComponent(this.options.machineToken)}${t}${s}${i}${o}${r}${u}${g}${f}`,n=new R(T);this.ws=n,this.connectTimer=setTimeout(()=>{this.ws===n&&this.state==="connecting"&&(this.closeTrigger="connect-timeout",n.terminate())},b),n.on("open",()=>{this.ws===n&&(this.clearConnectTimer(),this.state="connected",this.reconnectAttempts=0,this.startHeartbeat(),this.startPing(n),this.flushSendBuffer(),this.options.onConnected?.())}),n.on("pong",()=>{this.ws===n&&(this.clearPongTimer(),this.resetHeartbeat())}),n.on("message",a=>{if(this.ws!==n)return;let h;try{h=JSON.parse(a.toString())}catch{return}this.handleFrame(h)}),n.on("close",(a,h)=>{if(this.ws!==n)return;const E=this.state,m=h.toString(),p=O(a,m),w={code:a,reason:m,error:this.lastSocketError,phase:E,trigger:this.closeTrigger??"socket-close",reconnectAttempt:this.reconnectAttempts+1};p&&(this.disposed=!0,c("permanentStops")),this.cleanup(p),this.options.onDisconnected?.(w),p||this.scheduleReconnect()}),n.on("error",a=>{this.lastSocketError=a.message,this.closeTrigger="socket-error",n.close()})}disconnect(){this.disposed=!0,this.cleanup(!0)}sendRes(e){this.state!=="connected"||!this.ws||(e.traceId||(e.traceId=d()),this.ws.send(JSON.stringify(e)))}sendResAndWait(e){if(this.state!=="connected"||!this.ws)return Promise.reject(new Error("Relay is not connected"));e.traceId||(e.traceId=d());const t=this.ws;return new Promise((s,i)=>{t.send(JSON.stringify(e),o=>{o?i(o):s()})})}sendEvent(e){this.state!=="connected"||!this.ws||(e.traceId||(e.traceId=d()),c("relayEventsSent"),this.ws.send(JSON.stringify(e)))}sendReq(e,t=6e4){return this.state!=="connected"||!this.ws?Promise.reject(new Error("Relay is not connected")):(e.traceId||(e.traceId=d()),c("relayRequestsSent"),new Promise((s,i)=>{const o=this.pendingRequests.get(e.id);o&&(clearTimeout(o.timer),o.reject(new Error("Superseded by a newer relay request")));const r=setTimeout(()=>{this.pendingRequests.delete(e.id),i(new Error("Relay request timed out"))},t);this.pendingRequests.set(e.id,{resolve:s,reject:i,timer:r}),this.ws?.send(JSON.stringify(e))}))}sendBufferedEvent(e,t=12e4){if(e.traceId||(e.traceId=d()),!e.id)return this.sendEvent(e),Promise.resolve();const s=e.id;return c("relayEventsSent"),new Promise((i,o)=>{const r=this.pendingAcks.get(s);r&&(clearTimeout(r.timer),r.reject(new Error("Superseded by a newer buffered event")));const u=setTimeout(()=>{this.pendingAcks.delete(s),this.sendBuffer.delete(s),o(new Error("Relay event acknowledgement timed out"))},t);this.pendingAcks.set(s,{resolve:i,reject:o,timer:u}),this.bufferEvent(e)})}sendAgentEvent(e){this.sendBufferedEvent(e).catch(()=>{})}getState(){return this.state}handleFrame(e){if(c("relayFramesReceived"),e.type==="res"&&e.rejection&&c("structuredRejections"),e.type==="event"&&e.event==="tick"){this.resetHeartbeat();return}if(e.type==="res"&&e.id){this.sendBuffer.delete(e.id);const t=this.pendingAcks.get(e.id);if(t)if(this.pendingAcks.delete(e.id),clearTimeout(t.timer),e.ok)t.resolve(e);else{const i=e.rejection;t.reject(i?new y(i):new Error(e.error??"Relay event failed"))}const s=this.pendingRequests.get(e.id);s&&(this.pendingRequests.delete(e.id),clearTimeout(s.timer),s.resolve(e));return}e.type==="req"&&this.options.onReq?.(e)}flushSendBuffer(){if(!(this.sendBuffer.size===0||!this.ws))for(const e of this.sendBuffer.values()){if(this.state!=="connected"||!this.ws)break;this.ws.send(JSON.stringify(e))}}bufferEvent(e){if(!e.id){this.sendEvent(e);return}if(this.sendBuffer.set(e.id,e),this.sendBuffer.size>k){const t=this.sendBuffer.keys().next().value;if(t){this.sendBuffer.delete(t);const s=this.pendingAcks.get(t);s&&(this.pendingAcks.delete(t),clearTimeout(s.timer),s.reject(new Error("Relay send buffer overflow")))}}this.state==="connected"&&this.ws&&this.ws.send(JSON.stringify(e))}clearConnectTimer(){this.connectTimer!==null&&(clearTimeout(this.connectTimer),this.connectTimer=null)}startPing(e){this.clearPing(),this.pingTimer=setInterval(()=>{if(!(this.ws!==e||this.state!=="connected")){this.pongTimer=setTimeout(()=>{this.closeTrigger="pong-timeout",e.close()},_);try{e.ping()}catch{this.clearPongTimer(),this.closeTrigger="socket-error",e.close()}}},C)}clearPing(){this.pingTimer!==null&&(clearInterval(this.pingTimer),this.pingTimer=null),this.clearPongTimer()}clearPongTimer(){this.pongTimer!==null&&(clearTimeout(this.pongTimer),this.pongTimer=null)}startHeartbeat(){this.clearHeartbeat(),this.heartbeatTimer=setTimeout(()=>{this.closeTrigger="heartbeat-timeout",this.ws?.close()},P)}resetHeartbeat(){this.startHeartbeat()}clearHeartbeat(){this.heartbeatTimer!==null&&(clearTimeout(this.heartbeatTimer),this.heartbeatTimer=null)}scheduleReconnect(){if(this.disposed)return;const e=Math.min(I*Math.pow(2,this.reconnectAttempts),S),t=e*A*(Math.random()*2-1),s=Math.round(e+t);this.reconnectAttempts++,c("relayReconnects"),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connect()},s)}cleanup(e){if(this.state="disconnected",this.closeTrigger=void 0,this.lastSocketError=void 0,this.clearConnectTimer(),this.clearHeartbeat(),this.clearPing(),this.reconnectTimer!==null&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws){const t=this.ws;this.ws=null,t.removeAllListeners();try{t.close()}catch{}}for(const[t,s]of this.pendingRequests)clearTimeout(s.timer),s.reject(new Error("Relay client disconnected")),this.pendingRequests.delete(t);if(e)for(const[t,s]of this.pendingAcks)clearTimeout(s.timer),s.reject(new Error("Relay client disconnected")),this.pendingAcks.delete(t)}}export{j as CliRelayClient,y as SessionSyncRejectedError,O as isPermanentRelayClose};
@@ -49,6 +49,8 @@ export declare class RoomActivationService {
49
49
  });
50
50
  processMessages(roomId: string, messages: RoomMessageView[], registrations: RoomSessionRegistration[], context?: RoomActivationContext): Promise<void>;
51
51
  prepareConversationTurn(contexts: ManagedRoomQueueContext[]): Promise<PreparedManagedRoomTurn>;
52
+ prepareDirectConversationTurn(sessionId: string): Promise<PreparedManagedRoomTurn | null>;
53
+ private prepareManagedTurn;
52
54
  private audit;
53
55
  }
54
56
  export declare function resolveManagedAgentPolicy(input: {
@@ -1,8 +1,8 @@
1
- import{isAvailableBuiltinAgentType as A}from"@shennian/wire";import{getBuiltinAgentVersion as k,resolveBuiltinCommand as P}from"../agents/command-spec.js";import{registerCustomRuntimeSecurity as S,resolveCustomRuntimeExecutableFingerprint as K}from"../agents/custom-managed-runtime.js";import{loadConfig as B}from"../config/index.js";import{resolveManagedAgentExecutableIdentity as $}from"../security/managed-agent-executable.js";import{compileManagedAgentPolicy as x}from"../security/managed-agent-policy.js";import{resolveManagedAgentRuntimeProtection as q}from"../security/managed-agent-runtime.js";import{selectRoomActivationTargets as T}from"./activation-policy.js";import{composeManagedRoomPrompt as W}from"./managed-prompt-composer.js";const D=4,R=6e4,E=6,N=5*6e4;class oe{deps;latestContexts=new Map;constructor(e){this.deps=e}async processMessages(e,s,t,a={roomName:"\u5F53\u524D\u7FA4\u804A"}){const d=[],c=[],g=new Map,u=new Set,m=[...s].sort((n,r)=>G(n.sequence,r.sequence));for(const n of t){const r=this.deps.registry.getByRegistryKey(n.registryKey);!r?.binding||r.binding.roomId!==e||r.binding.status!=="active"&&r.binding.status!=="paused"&&r.binding.activationQueue.length}for(const n of t){const r=this.deps.registry.getByRegistryKey(n.registryKey);if(!r?.binding||r.binding.roomId!==e||r.binding.activationQueue.length===0&&!r.binding.activeActivation||(u.add(r.registryKey),r.binding.status!=="active"||r.binding.activationMode==="paused")||r.binding.activationQueue.length===0)continue;const l=this.deps.observations.getBySource(r);if(!l?.workspacePath||l.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");this.latestContexts.set(r.registryKey,a);for(const o of r.binding.activationQueue)d.push({registration:r,input:{params:{sessionId:r.nianSessionId,text:o.deliveryText??"\u7FA4\u804A\u4E2D\u6709\u4E00\u6761\u5F85\u5904\u7406\u6D88\u606F\uFF0C\u8BF7\u7ED3\u5408\u5F53\u524D\u7FA4\u804A\u4E0A\u4E0B\u6587\u5904\u7406\u3002",agentType:y(r.runtimeAdapter),sessionMode:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:l.workspacePath,agentSessionId:r.sourceSessionKey,queueMessageId:`room-activation-${r.binding.bindingId}-${o.causeMessageId}`,clientMessageId:`room-activation-${r.binding.bindingId}-${o.causeMessageId}`},context:{registryKey:r.registryKey,causeMessageId:o.causeMessageId,rootCauseMessageId:o.rootCauseMessageId,sequence:o.sequence,hopCount:o.hopCount}}})}for(const n of m){const r=new Set(T(n,t).map(l=>l.registration.registryKey));for(const l of t){const o=this.deps.registry.getByRegistryKey(l.registryKey);if(!o?.binding||o.binding.roomId!==e||o.binding.status!=="active"&&o.binding.status!=="paused"||BigInt(n.sequence)<=BigInt(o.binding.lastActivatedSequence))continue;const f=g.get(o.registryKey)??[...o.binding.mentionContext??[]];if(g.set(o.registryKey,f),r.has(o.registryKey)){const p=(n.activationCause?.hopCount??0)+(n.author.principalType==="agent"?1:0),I=n.activationCause?.rootCauseMessageId??n.id;if(p>D)this.audit(o,n.id,I,p,"suppressed","max_hops");else{const h=this.deps.registry.claimActivation({registryKey:o.registryKey,windowMs:R,maxActivations:E,pauseMs:N,now:this.deps.now?.()});if(!h.allowed){h.newlyPaused&&this.audit(o,n.id,I,p,"suppressed","rate_limited"),c.push({registryKey:o.registryKey,sequence:n.sequence});continue}const b=this.deps.observations.getBySource(o);if(!b?.workspacePath||b.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const v=C([n]),w=o.binding.activationMode==="mention_only"?L([...f.map(_=>_.deliveryText),v]):v;this.latestContexts.set(o.registryKey,a),d.push({registration:o,input:{params:{sessionId:o.nianSessionId,text:w,agentType:y(o.runtimeAdapter),sessionMode:a.agentsByIdentityId?.[o.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:a.agentsByIdentityId?.[o.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:a.agentsByIdentityId?.[o.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:a.agentsByIdentityId?.[o.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:b.workspacePath,agentSessionId:o.sourceSessionKey,queueMessageId:`room-activation-${o.binding.bindingId}-${n.id}`,clientMessageId:`room-activation-${o.binding.bindingId}-${n.id}`},context:{registryKey:o.registryKey,causeMessageId:n.id,rootCauseMessageId:I,sequence:n.sequence,hopCount:p}}}),o.binding.activationMode==="mention_only"&&g.set(o.registryKey,[])}}else o.binding.activationMode==="mention_only"&&n.kind!=="system"&&n.author.membershipId!==o.binding.agentMembershipId&&(f.some(p=>p.sequence===n.sequence)||f.push({sequence:n.sequence,deliveryText:C([n])}));c.push({registryKey:o.registryKey,sequence:n.sequence})}}await this.deps.enqueueConversationBatch(d.map(n=>n.input));for(const n of u)this.deps.registry.retireLegacyActivationState(n,this.deps.now?.()??new Date);for(const[n,r]of g)this.deps.registry.replaceMentionContext(n,r,this.deps.now?.()??new Date);for(const n of d)this.audit(n.registration,n.input.context.causeMessageId,n.input.context.rootCauseMessageId,n.input.context.hopCount,"queued","conversation_queue");for(const n of c)this.deps.registry.advanceCursor({registryKey:n.registryKey,cursor:"activated",sequence:n.sequence})}async prepareConversationTurn(e){const s=e.at(-1);if(!s)throw new Error("managed_room_queue_context_missing");const t=this.deps.registry.getByRegistryKey(s.registryKey);if(!t?.binding||t.binding.status!=="active")throw new Error("managed_room_binding_unavailable");const a=this.deps.loadContext?await this.deps.loadContext(t):this.latestContexts.get(s.registryKey);if(!a)throw new Error("managed_room_prompt_context_unavailable");const d=a.agentsByIdentityId?.[t.binding.agentIdentityId];if(!d?.permissionPolicy)throw new Error("managed_agent_policy_missing");const c=d.sessionMode==="manager"?"manager":"standard",g=c==="manager"?{...d.managerConfig,defaultWorkerAgentType:d.managerConfig?.defaultWorkerAgentType??y(t.runtimeAdapter),defaultWorkerModelId:d.managerConfig?.defaultWorkerModelId??null}:null,u=this.deps.observations.getBySource(t);if(!u?.workspacePath||u.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const m=this.deps.resolveManagedAgentPolicy?await this.deps.resolveManagedAgentPolicy({registration:t,permissionPolicy:d.permissionPolicy,permissionConfirmation:d.permissionConfirmation,workDir:u.workspacePath}):await j({registration:t,permissionPolicy:d.permissionPolicy,permissionConfirmation:d.permissionConfirmation,workDir:u.workspacePath}),n=Q(a,t,m),r=this.deps.issueManagedRoomContext(t.registryKey,s.causeMessageId);if(!/^mrc_[A-Za-z0-9_-]{43}$/.test(r))throw new Error("managed_room_context_unavailable");return this.audit(t,s.causeMessageId,s.rootCauseMessageId,s.hopCount,"activated","conversation_queue_dispatch"),{systemPrompt:n.systemPrompt,sessionMode:c,managerConfig:g,managerDefaultWorkerAgentType:g?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:g?.defaultWorkerModelId??null,roomContextRegistryKey:t.registryKey,managedRoomContextToken:r,managedAgentPolicy:m,managedRoomPromptSnapshot:n.snapshot}}audit(e,s,t,a,d,c){e.binding&&this.deps.audit?.record({roomId:e.binding.roomId,bindingId:e.binding.bindingId,causeMessageId:s,rootCauseMessageId:t,hopCount:a,outcome:d,reason:c,at:this.deps.now?.()})}}function C(i){const e=i.map(s=>{const t=s.text?.trim()||"\u53D1\u9001\u4E86\u4E00\u6761\u6D88\u606F",a=s.attachments.length>0?`
2
- ${s.attachments.map(F).join(`
3
- `)}`:"";return{speaker:s.author.displayName,body:`${t}${a}`}});return M(e)}function F(i){return`\u9644\u4EF6\uFF1A${`${O(i.name)} \xB7 ${V(i.byteSize)}`}\uFF5C${i.mimeType}\uFF5CattachmentId=${i.attachmentId}`}function O(i){return String(i||"\u9644\u4EF6").replace(/[[\]\\]/g,"\\$&").replace(/[\r\n]+/g," ")}function V(i){const e=Number(i);if(!Number.isFinite(e)||e<0)return`${i} B`;if(e<1024)return`${e} B`;const s=["KB","MB","GB","TB"];let t=e,a=-1;do t/=1024,a+=1;while(t>=1024&&a<s.length-1);const d=t>=100?0:t>=10?1:2;return`${t.toFixed(d).replace(/\.0+$|(?<=\.[0-9])0+$/,"")} ${s[a]}`}function L(i){return M(i.map(z))}function z(i){const e=i.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);if(e)return{speaker:e[1],body:e[2]};const s=i.match(/^\[群消息\]\n<([^>\n]+)>\n([\s\S]*)$/);if(s)return{speaker:s[1],body:s[2]};const t=i.match(/^群消息\n([^\n]+)\n([\s\S]*)$/);if(t)return{speaker:t[1],body:t[2]};const a=i.match(/^([^\n]+)\n([\s\S]*)$/);return a?{speaker:a[1],body:a[2]}:{speaker:"\u7FA4\u6210\u5458",body:i.trim()}}function M(i){if(i.length===1){const[e]=i;return`[\u7FA4\u6D88\u606F] <${e.speaker}>
1
+ import{isAvailableBuiltinAgentType as A}from"@shennian/wire";import{getBuiltinAgentVersion as k,resolveBuiltinCommand as S}from"../agents/command-spec.js";import{registerCustomRuntimeSecurity as P,resolveCustomRuntimeExecutableFingerprint as K}from"../agents/custom-managed-runtime.js";import{loadConfig as B}from"../config/index.js";import{resolveManagedAgentExecutableIdentity as $}from"../security/managed-agent-executable.js";import{compileManagedAgentPolicy as T}from"../security/managed-agent-policy.js";import{resolveManagedAgentRuntimeProtection as x}from"../security/managed-agent-runtime.js";import{selectRoomActivationTargets as q}from"./activation-policy.js";import{composeManagedRoomPrompt as D}from"./managed-prompt-composer.js";const W=4,E=6e4,R=6,N=5*6e4;class oe{deps;latestContexts=new Map;constructor(e){this.deps=e}async processMessages(e,i,o,r={roomName:"\u5F53\u524D\u7FA4\u804A"}){const d=[],c=[],u=new Map,m=new Set,g=[...i].sort((n,a)=>G(n.sequence,a.sequence));for(const n of o){const a=this.deps.registry.getByRegistryKey(n.registryKey);!a?.binding||a.binding.roomId!==e||a.binding.status!=="active"&&a.binding.status!=="paused"&&a.binding.activationQueue.length}for(const n of o){const a=this.deps.registry.getByRegistryKey(n.registryKey);if(!a?.binding||a.binding.roomId!==e||a.binding.activationQueue.length===0&&!a.binding.activeActivation||(m.add(a.registryKey),a.binding.status!=="active"||a.binding.activationMode==="paused")||a.binding.activationQueue.length===0)continue;const p=this.deps.observations.getBySource(a);if(!p?.workspacePath||p.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");this.latestContexts.set(a.registryKey,r);for(const s of a.binding.activationQueue)d.push({registration:a,input:{params:{sessionId:a.nianSessionId,text:s.deliveryText??"\u7FA4\u804A\u4E2D\u6709\u4E00\u6761\u5F85\u5904\u7406\u6D88\u606F\uFF0C\u8BF7\u7ED3\u5408\u5F53\u524D\u7FA4\u804A\u4E0A\u4E0B\u6587\u5904\u7406\u3002",agentType:y(a.runtimeAdapter),sessionMode:r.agentsByIdentityId?.[a.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:r.agentsByIdentityId?.[a.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:r.agentsByIdentityId?.[a.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:r.agentsByIdentityId?.[a.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:p.workspacePath,agentSessionId:a.sourceSessionKey,queueMessageId:`room-activation-${a.binding.bindingId}-${s.causeMessageId}`,clientMessageId:`room-activation-${a.binding.bindingId}-${s.causeMessageId}`},context:{registryKey:a.registryKey,causeMessageId:s.causeMessageId,rootCauseMessageId:s.rootCauseMessageId,sequence:s.sequence,hopCount:s.hopCount}}})}for(const n of g){const a=new Set(q(n,o).map(p=>p.registration.registryKey));for(const p of o){const s=this.deps.registry.getByRegistryKey(p.registryKey);if(!s?.binding||s.binding.roomId!==e||s.binding.status!=="active"&&s.binding.status!=="paused"||BigInt(n.sequence)<=BigInt(s.binding.lastActivatedSequence))continue;const f=u.get(s.registryKey)??[...s.binding.mentionContext??[]];if(u.set(s.registryKey,f),a.has(s.registryKey)){const l=(n.activationCause?.hopCount??0)+(n.author.principalType==="agent"?1:0),I=n.activationCause?.rootCauseMessageId??n.id;if(l>W)this.audit(s,n.id,I,l,"suppressed","max_hops");else{const h=this.deps.registry.claimActivation({registryKey:s.registryKey,windowMs:E,maxActivations:R,pauseMs:N,now:this.deps.now?.()});if(!h.allowed){h.newlyPaused&&this.audit(s,n.id,I,l,"suppressed","rate_limited"),c.push({registryKey:s.registryKey,sequence:n.sequence});continue}const b=this.deps.observations.getBySource(s);if(!b?.workspacePath||b.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const v=M([n]),C=s.binding.activationMode==="mention_only"?L([...f.map(_=>_.deliveryText),v]):v;this.latestContexts.set(s.registryKey,r),d.push({registration:s,input:{params:{sessionId:s.nianSessionId,text:C,agentType:y(s.runtimeAdapter),sessionMode:r.agentsByIdentityId?.[s.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:r.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:r.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:r.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:b.workspacePath,agentSessionId:s.sourceSessionKey,queueMessageId:`room-activation-${s.binding.bindingId}-${n.id}`,clientMessageId:`room-activation-${s.binding.bindingId}-${n.id}`},context:{registryKey:s.registryKey,causeMessageId:n.id,rootCauseMessageId:I,sequence:n.sequence,hopCount:l}}}),s.binding.activationMode==="mention_only"&&u.set(s.registryKey,[])}}else s.binding.activationMode==="mention_only"&&n.kind!=="system"&&n.author.membershipId!==s.binding.agentMembershipId&&(f.some(l=>l.sequence===n.sequence)||f.push({sequence:n.sequence,deliveryText:M([n])}));c.push({registryKey:s.registryKey,sequence:n.sequence})}}await this.deps.enqueueConversationBatch(d.map(n=>n.input));for(const n of m)this.deps.registry.retireLegacyActivationState(n,this.deps.now?.()??new Date);for(const[n,a]of u)this.deps.registry.replaceMentionContext(n,a,this.deps.now?.()??new Date);for(const n of d)this.audit(n.registration,n.input.context.causeMessageId,n.input.context.rootCauseMessageId,n.input.context.hopCount,"queued","conversation_queue");for(const n of c)this.deps.registry.advanceCursor({registryKey:n.registryKey,cursor:"activated",sequence:n.sequence})}async prepareConversationTurn(e){const i=e.at(-1);if(!i)throw new Error("managed_room_queue_context_missing");const o=this.deps.registry.getByRegistryKey(i.registryKey);if(!o)throw new Error("managed_room_binding_unavailable");const r=await this.prepareManagedTurn(o,i.causeMessageId);return this.audit(o,i.causeMessageId,i.rootCauseMessageId,i.hopCount,"activated","conversation_queue_dispatch"),r}async prepareDirectConversationTurn(e){const i=this.deps.registry.getByNianSessionId(e);return i?this.prepareManagedTurn(i):null}async prepareManagedTurn(e,i){if(!e.binding||e.binding.status!=="active")throw new Error("managed_room_binding_unavailable");const o=this.deps.loadContext?await this.deps.loadContext(e):this.latestContexts.get(e.registryKey);if(!o)throw new Error("managed_room_prompt_context_unavailable");const r=o.agentsByIdentityId?.[e.binding.agentIdentityId];if(!r?.permissionPolicy)throw new Error("managed_agent_policy_missing");const d=r.sessionMode==="manager"?"manager":"standard",c=d==="manager"?{...r.managerConfig,defaultWorkerAgentType:r.managerConfig?.defaultWorkerAgentType??y(e.runtimeAdapter),defaultWorkerModelId:r.managerConfig?.defaultWorkerModelId??null}:null,u=this.deps.observations.getBySource(e);if(!u?.workspacePath||u.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const m=this.deps.resolveManagedAgentPolicy?await this.deps.resolveManagedAgentPolicy({registration:e,permissionPolicy:r.permissionPolicy,permissionConfirmation:r.permissionConfirmation,workDir:u.workspacePath}):await j({registration:e,permissionPolicy:r.permissionPolicy,permissionConfirmation:r.permissionConfirmation,workDir:u.workspacePath}),g=Q(o,e,m),n=this.deps.issueManagedRoomContext(e.registryKey,i);if(!/^mrc_[A-Za-z0-9_-]{43}$/.test(n))throw new Error("managed_room_context_unavailable");return{agentType:y(e.runtimeAdapter),workDir:m.adapterPolicy.workDir,agentSessionId:e.sourceSessionKey,systemPrompt:g.systemPrompt,sessionMode:d,managerConfig:c,managerDefaultWorkerAgentType:c?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:c?.defaultWorkerModelId??null,roomContextRegistryKey:e.registryKey,managedRoomContextToken:n,managedAgentPolicy:m,managedRoomPromptSnapshot:g.snapshot}}audit(e,i,o,r,d,c){e.binding&&this.deps.audit?.record({roomId:e.binding.roomId,bindingId:e.binding.bindingId,causeMessageId:i,rootCauseMessageId:o,hopCount:r,outcome:d,reason:c,at:this.deps.now?.()})}}function M(t){const e=t.map(i=>{const o=i.text?.trim()||"\u53D1\u9001\u4E86\u4E00\u6761\u6D88\u606F",r=i.attachments.length>0?`
2
+ ${i.attachments.map(F).join(`
3
+ `)}`:"";return{speaker:i.author.displayName,body:`${o}${r}`}});return w(e)}function F(t){return`\u9644\u4EF6\uFF1A${`${O(t.name)} \xB7 ${V(t.byteSize)}`}\uFF5C${t.mimeType}\uFF5CattachmentId=${t.attachmentId}`}function O(t){return String(t||"\u9644\u4EF6").replace(/[[\]\\]/g,"\\$&").replace(/[\r\n]+/g," ")}function V(t){const e=Number(t);if(!Number.isFinite(e)||e<0)return`${t} B`;if(e<1024)return`${e} B`;const i=["KB","MB","GB","TB"];let o=e,r=-1;do o/=1024,r+=1;while(o>=1024&&r<i.length-1);const d=o>=100?0:o>=10?1:2;return`${o.toFixed(d).replace(/\.0+$|(?<=\.[0-9])0+$/,"")} ${i[r]}`}function L(t){return w(t.map(z))}function z(t){const e=t.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);if(e)return{speaker:e[1],body:e[2]};const i=t.match(/^\[群消息\]\n<([^>\n]+)>\n([\s\S]*)$/);if(i)return{speaker:i[1],body:i[2]};const o=t.match(/^群消息\n([^\n]+)\n([\s\S]*)$/);if(o)return{speaker:o[1],body:o[2]};const r=t.match(/^([^\n]+)\n([\s\S]*)$/);return r?{speaker:r[1],body:r[2]}:{speaker:"\u7FA4\u6210\u5458",body:t.trim()}}function w(t){if(t.length===1){const[e]=t;return`[\u7FA4\u6D88\u606F] <${e.speaker}>
4
4
  ${e.body}`}return`[\u7FA4\u6D88\u606F]
5
- ${i.map(e=>`<${e.speaker}>
5
+ ${t.map(e=>`<${e.speaker}>
6
6
  ${e.body}`).join(`
7
7
 
8
- `)}`}function Q(i,e,s){const t=e.binding?i.agentsByIdentityId?.[e.binding.agentIdentityId]:void 0;if(!e.binding||!t)throw new Error("managed_room_prompt_incomplete");return W({runtimeAdapter:y(e.runtimeAdapter),agent:{displayName:t.displayName,description:t.description,systemPrompt:i.agentSystemPromptsByIdentityId?.[e.binding.agentIdentityId]},room:{name:i.roomName,activationMode:e.binding.activationMode,members:i.members},runtimePermissions:{requestedPolicy:s.requestedPolicy,enforcement:s.enforcement,resolvedWorkDir:s.adapterPolicy.workDir}})}async function j(i){const e=y(i.registration.runtimeAdapter),s=A(e)?P(e):null,t=e.startsWith("custom:")?B().customAgents?.[e.slice(7)]:void 0;t&&S(e,t);const a=s?k(e,s):t?.caps.version,d=s?$(s):{platform:process.platform,isWsl:process.platform==="linux"&&!!(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP),executableFingerprint:t?K(t):void 0},c={agentType:e,runtimeVersion:a,platform:d.platform,isWsl:d.isWsl,executableFingerprint:d.executableFingerprint,requireExecutableFingerprint:!0},g=q(c),u=X({confirmation:i.permissionConfirmation,policy:i.permissionPolicy,runtimeAdapter:e,protection:g}),m=await x({...c,policy:i.permissionPolicy,workDir:i.workDir,hostFullAccessConfirmed:u&&i.permissionConfirmation?.hostFullAccess,limitedProtectionConfirmed:u&&i.permissionConfirmation?.limitedProtection});if(!m.ok){const n=m.unsupportedCapabilities?.length?`:${m.unsupportedCapabilities.join(",")}`:"";throw new Error(`${m.errorCode}${n}`)}return m.value}function X(i){const{confirmation:e,policy:s,runtimeAdapter:t,protection:a}=i;return!!(e&&e.policyVersion===s.version&&e.runtimeAdapter===t&&e.runtimeVersion===a.runtimeVersion&&e.platform===a.platform&&e.executableFingerprint===a.executableFingerprint&&e.hostFullAccess===(s.fileAccess==="host-full")&&e.limitedProtection===(a.level!=="hard"))}function y(i){return i==="claude_code"?"claude":i}function G(i,e){const s=BigInt(i),t=BigInt(e);return s<t?-1:s>t?1:0}export{oe as RoomActivationService,X as managedAgentPermissionConfirmationMatches,j as resolveManagedAgentPolicy};
8
+ `)}`}function Q(t,e,i){const o=e.binding?t.agentsByIdentityId?.[e.binding.agentIdentityId]:void 0;if(!e.binding||!o)throw new Error("managed_room_prompt_incomplete");return D({runtimeAdapter:y(e.runtimeAdapter),agent:{displayName:o.displayName,description:o.description,systemPrompt:t.agentSystemPromptsByIdentityId?.[e.binding.agentIdentityId]},room:{name:t.roomName,activationMode:e.binding.activationMode,members:t.members},runtimePermissions:{requestedPolicy:i.requestedPolicy,enforcement:i.enforcement,resolvedWorkDir:i.adapterPolicy.workDir}})}async function j(t){const e=y(t.registration.runtimeAdapter),i=A(e)?S(e):null,o=e.startsWith("custom:")?B().customAgents?.[e.slice(7)]:void 0;o&&P(e,o);const r=i?k(e,i):o?.caps.version,d=i?$(i):{platform:process.platform,isWsl:process.platform==="linux"&&!!(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP),executableFingerprint:o?K(o):void 0},c={agentType:e,runtimeVersion:r,platform:d.platform,isWsl:d.isWsl,executableFingerprint:d.executableFingerprint,requireExecutableFingerprint:!0},u=x(c),m=X({confirmation:t.permissionConfirmation,policy:t.permissionPolicy,runtimeAdapter:e,protection:u}),g=await T({...c,policy:t.permissionPolicy,workDir:t.workDir,hostFullAccessConfirmed:m&&t.permissionConfirmation?.hostFullAccess,limitedProtectionConfirmed:m&&t.permissionConfirmation?.limitedProtection});if(!g.ok){const n=g.unsupportedCapabilities?.length?`:${g.unsupportedCapabilities.join(",")}`:"";throw new Error(`${g.errorCode}${n}`)}return g.value}function X(t){const{confirmation:e,policy:i,runtimeAdapter:o,protection:r}=t;return!!(e&&e.policyVersion===i.version&&e.runtimeAdapter===o&&e.runtimeVersion===r.runtimeVersion&&e.platform===r.platform&&e.executableFingerprint===r.executableFingerprint&&e.hostFullAccess===(i.fileAccess==="host-full")&&e.limitedProtection===(r.level!=="hard"))}function y(t){return t==="claude_code"?"claude":t}function G(t,e){const i=BigInt(t),o=BigInt(e);return i<o?-1:i>o?1:0}export{oe as RoomActivationService,X as managedAgentPermissionConfirmationMatches,j as resolveManagedAgentPolicy};
@@ -1,4 +1,5 @@
1
1
  import type { ReqFrame } from '@shennian/wire';
2
2
  import type { SessionManagerRuntime } from '../types.js';
3
+ export declare function refreshMachineAgents(runtime: SessionManagerRuntime, req?: ReqFrame): Promise<void>;
3
4
  export declare function handleModelsRefresh(runtime: SessionManagerRuntime, req: ReqFrame): Promise<void>;
4
5
  export declare function handleAgentsRefresh(runtime: SessionManagerRuntime, req: ReqFrame): Promise<void>;
@@ -1 +1 @@
1
- import{SERVERS as i}from"../../region.js";import{loadConfig as d}from"../../config/index.js";import{detectAgents as f}from"../../agents/detect.js";import{refreshAgentInfos as l}from"../../agents/model-registry.js";import{resetCommandSpecCache as p}from"../../agents/command-spec.js";async function a(e,n){const s=d(),c=s.serverUrl??i.cn.url,o=f(),r=o.map(t=>t.type);if(o.length===0){e.client.sendRes({type:"res",id:n.id,ok:!0,payload:{agentList:[],agents:[]}});return}try{const t=await l(o,{serverUrl:c,authToken:s.machineToken??s.accessToken});e.client.sendRes({type:"res",id:n.id,ok:!0,payload:{agentList:r,agents:t}}),e.client.sendEvent({type:"event",event:"machine.agents",payload:{agentList:r,agents:t}})}catch(t){e.client.sendRes({type:"res",id:n.id,ok:!1,error:t instanceof Error?t.message:String(t)})}}async function k(e,n){await a(e,n)}async function A(e,n){p(),e.reloadCustomAgents(),await a(e,n)}export{A as handleAgentsRefresh,k as handleModelsRefresh};
1
+ import{SERVERS as l}from"../../region.js";import{loadConfig as h}from"../../config/index.js";import{detectAgents as d}from"../../agents/detect.js";import{refreshAgentInfos as g}from"../../agents/model-registry.js";import{resetCommandSpecCache as m}from"../../agents/command-spec.js";import{clearAcpCapabilityCache as y}from"../../agents/acp/capability-cache.js";const i=new WeakMap;async function f(e,t){const s=h(),c=s.serverUrl??l.cn.url,o=await d({serverUrl:c,authToken:s.machineToken??s.accessToken}),p=o.map(n=>n.type);try{const n=o.length===0?[]:await g(o,{serverUrl:c,authToken:s.machineToken??s.accessToken}),a={agentList:p,agents:n};t&&e.client.sendRes({type:"res",id:t.id,ok:!0,payload:a});const r=JSON.stringify(a);i.get(e.client)!==r&&(i.set(e.client,r),e.client.sendEvent({type:"event",event:"machine.agents",payload:a}))}catch(n){if(!t)return;e.client.sendRes({type:"res",id:t.id,ok:!1,error:n instanceof Error?n.message:String(n)})}}async function u(e,t){await f(e,t)}async function v(e,t){m(),y(),e.reloadCustomAgents(),await f(e,t)}export{v as handleAgentsRefresh,u as handleModelsRefresh,f as refreshMachineAgents};
@@ -3,6 +3,8 @@ import { type ReqFrame, type AgentType, type SessionMode } from '@shennian/wire'
3
3
  import type { SessionManagerRuntime } from '../types.js';
4
4
  import type { CompiledManagedAgentPolicy } from '../../security/managed-agent-policy.js';
5
5
  import { type ManagedRoomPromptSnapshot } from '../../room/managed-prompt-composer.js';
6
+ import type { AgentRecoveryResult } from '../../agents/adapter.js';
7
+ import type { RunJournalRecord } from '../run-recovery/schema.js';
6
8
  export type ChatSendExecutionContext = AgentSendContext & {
7
9
  /** Local-only snapshot produced by RoomActivationService; never accepted from Relay input. */
8
10
  managedAgentPolicy: CompiledManagedAgentPolicy;
@@ -19,5 +21,6 @@ export declare function sendSessionUpdateEvent(runtime: SessionManagerRuntime, i
19
21
  managerDefaultWorkerAgentType?: AgentType | null;
20
22
  managerDefaultWorkerModelId?: string | null;
21
23
  }): void;
24
+ export declare function recoverJournaledRun(runtime: SessionManagerRuntime, record: RunJournalRecord, beforeContinueDispatch?: () => void): Promise<AgentRecoveryResult>;
22
25
  export declare function handleChatSend(runtime: SessionManagerRuntime, req: ReqFrame, context?: ChatSendExecutionContext): Promise<void>;
23
26
  export declare function handleChatAbort(runtime: SessionManagerRuntime, req: ReqFrame): Promise<void>;
@@ -1,2 +1,2 @@
1
- import ie from"node:os";import{createHash as K}from"node:crypto";import{createAgent as Me}from"../../agents/adapter.js";import{buildApprovalPendingPayload as xe,buildUserMessagePayload as le}from"@shennian/wire";import{ManagerModeAdapter as Te}from"../../agents/manager.js";import{reportLog as b}from"../../log-reporter.js";import{lookupClaudeTranscriptCwd as _e}from"../../native-fusion/parsers.js";import{appendMessage as q,getDaemonInstallationId as ce,getMessageDeliveryStatus as De,recordSession as H,upsertMessage as z}from"../store.js";import{mergeProjectedSessions as be}from"../projection.js";import{buildManagedAgentEnv as qe}from"../../agents/config-status.js";import{parsePersonalAttachmentReferences as Pe}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as $e,markAttachmentReferences as ve}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as T}from"../index-publisher.js";import{writeToolDetail as Ce}from"../tool-detail-store.js";import{getPersonalSyncIdentity as ue}from"../../personal-sync/epoch.js";import{buildManagedAgentPermissionPrompt as We}from"../../room/managed-prompt-composer.js";import{getManagedRoomIpcRoot as Oe,MANAGED_ROOM_IPC_ROOT_ENV as Ne}from"../../daemon-ipc/paths.js";function Fe(e,t,a){if(!e.managedAgentPolicy)throw new Error("managed_agent_policy_missing");const s=e.managedRoomPromptSnapshot;if(!s)throw new Error("managed_room_prompt_snapshot_missing");if(!t?.trim())throw new Error("managed_room_prompt_incomplete");if(!a||!/^mrc_[A-Za-z0-9_-]{43}$/.test(a))throw new Error("managed_room_context_unavailable");const o=K("sha256").update(JSON.stringify({requestedPolicy:e.managedAgentPolicy.requestedPolicy,enforcement:e.managedAgentPolicy.enforcement})).digest("hex"),l=K("sha256").update(e.managedAgentPolicy.adapterPolicy.workDir).digest("hex");if(s.runtimePermissionPolicyHash!==o||s.resolvedWorkDirHash!==l||s.enforcementLevel!==e.managedAgentPolicy.enforcement.level)throw new Error("managed_room_prompt_snapshot_mismatch")}function He(e){const t=e.indexOf(`
2
- `),a=t>0?Math.min(t,80):Math.min(e.length,80);return e.slice(0,a)}function ge(e,t,a){return`${e}:${K("sha256").update(`${t}\0${a??""}`).digest("hex")}`}function ze(e,t,a={}){if(e.state==="tool-call"||e.state==="tool-result"){const s={runId:e.runId,sourceSeq:e.seq};return{state:e.state,runId:e.runId,seq:e.seq,sessionId:t,detailRef:s,status:e.state==="tool-call"?"running":"completed",detailAvailable:!0,...e.durationMs!==void 0?{durationMs:e.durationMs}:{},...e.name?{name:e.name}:{},...e.source?{source:e.source}:{},...e.agentSessionId?{agentSessionId:e.agentSessionId}:{},...a}}return{...e,sessionId:t,...a}}const Be=3e4;function Le(e){return e.state==="heartbeat"?e.runPhase??null:e.state==="tool-call"||e.state==="tool-result"?"tool_running":e.state==="approval-pending"?"waiting_approval":e.state==="delta"?e.thinking?"thinking":"streaming_text":e.state==="init"||e.state==="start"?"thinking":null}function fe(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function me(e,t){return e!=="manager"?e:t==="claude"?"claude":"codex"}function Ue(e,t){H({sessionId:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,workDir:t.workDir,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:t.managerDefaultWorkerModelId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,workDir:t.workDir,status:"active",externalChannel:null}}})}function pe(e){return qe(e)}function he(e,t,a){return{}}function X(e,t,a,s,o,l,c,R){const S=a==="codex";e.configure?.({sessionId:t,externalChannel:s??null,env:{...pe(a),...he(t,s,o),...l&&!S?{SHENNIAN_MANAGED_ROOM_CONTEXT:l,[Ne]:Oe()}:{}},...c?{managedAgentPolicy:c}:{},...S?{managedRoomContextToken:l??null,managedRoomExecutor:l?R??null:null}:{}})}function ye(e,t,a){const s=t.managedRoomContextToken;s&&s!==a&&e.revokeManagedRoomContextToken?.(s),t.managedRoomContextToken=a}function C(e,t){const a=t?.managedRoomContextToken;!t||!a||(t.managedRoomContextToken=null,e.revokeManagedRoomContextToken?.(a))}function Je(e,t,a){if(e!=="claude"||!a)return t;const s=t.trim();return!!s&&s.startsWith("-")&&!s.includes("/")?_e(a)??t:t}function Qe(e,t,a,s){let o=null;function l(n,r={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:ze(n,t,r),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function c(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function R(n){if(!n.currentRunId||!n.currentRunPhase)return;const r=n.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:t,runId:n.currentRunId,seq:r,runPhase:n.currentRunPhase},seq:r,id:`agent-heartbeat-${n.currentRunId}-${r}-${Date.now()}`})}function S(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{R(n)},Be),n.heartbeatTimer.unref?.())}function h(n){const r=n?.pendingTextEvent;!n||!r||!r.text||(q(t,{id:`agent-${r.runId}-${r.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:r.text}),T(e.client),l({state:"delta",runId:r.runId,seq:r.seq,text:r.text,thinking:r.thinking||void 0}),n.pendingTextEvent=null)}s.on("agentEvent",n=>{const r=e.sessions.get(t),w=Le(n),_=n.state==="final"||n.state==="error"||n.state==="aborted";if(r&&(r.nextEventSeq=n.seq+1,_||(r.currentRunId=n.runId),!_&&w&&(r.currentRunPhase=w,S(r)),n.agentSessionId&&(r.agentSessionId=n.agentSessionId)),e.managerRuntime?.noteAgentEvent(t,n),n.state==="delta"&&n.thinking)return;n.state!=="delta"&&b({level:"info",sessionId:t,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:a}});let k=!1;if(n.state==="tool-call"||n.state==="tool-result"){const m=`agent-${n.runId}-${n.seq}`;Ce({sessionId:t,toolCallId:m,detailRevision:n.seq,ts:Date.now(),name:n.name??"tool",...n.args!==void 0?{args:n.args}:{},...n.result!==void 0?{result:n.result}:{},status:n.state==="tool-call"?"running":"completed",...n.durationMs!==void 0?{durationMs:n.durationMs}:{},detailRef:{runId:n.runId,sourceSeq:n.seq}}),q(t,{id:m,sessionId:t,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:n.state==="tool-call"?"tool_use":"tool_result",name:n.name,toolCallId:m,status:n.state==="tool-call"?"running":"completed",...n.durationMs!==void 0?{durationMs:n.durationMs}:{},detailRef:{runId:n.runId,sourceSeq:n.seq},detailRevision:n.seq,detailAvailable:!0,resultOmitted:n.state==="tool-result"})}),k=!0}else n.state==="approval-pending"?(q(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:xe(n.approval)}),k=!0,r&&H({sessionId:t,agentType:a,workDir:r.workDir,agentSessionId:r.agentSessionId,attentionId:ge("approval",n.runId,n.seq)})):(n.state==="error"||n.state==="aborted")&&n.message&&(q(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.message}),k=!0);k&&T(e.client);const A=`${t}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&e.runTextAcc.set(A,(e.runTextAcc.get(A)??"")+n.text),n.agentSessionId&&n.agentSessionId!==o&&(o=n.agentSessionId,e.nativeFusion?.noteManagedSourceSession(t,me(a,n.source),n.agentSessionId),r&&H({sessionId:t,agentType:a,workDir:r.workDir,agentSessionId:n.agentSessionId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t,agentType:a,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const m=n.text??"";if(!m)return;r?.pendingTextEvent&&r.pendingTextEvent.runId!==n.runId&&h(r),r&&!r.pendingTextEvent&&(r.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:!1}),r?.pendingTextEvent&&(r.pendingTextEvent.text+=m,r.pendingTextEvent.seq=n.seq);return}let W={};if(n.state==="final"){h(r);const m=e.runTextAcc.get(A)??"";m&&(W={messageSummary:He(m)}),e.runTextAcc.delete(A)}else n.state==="error"||n.state==="aborted"?(h(r),e.runTextAcc.delete(A)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&h(r);_&&r?.currentRunId===n.runId&&(C(e,r),r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,c(r),e.chatQueue?.noteTerminal(t),H({sessionId:t,agentType:a,workDir:r.workDir,agentSessionId:r.agentSessionId,status:n.state==="final"?"completed":"failed",attentionId:ge("terminal",n.runId),lastActivityAt:new Date().toISOString()}),T(e.client)),l(n,W)}),s.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${t} agentType=${a}: ${n.message}`),C(e,e.sessions.get(t)),e.sessions.delete(t),e.chatQueue?.noteTerminal(t),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:t,message:n.message,runId:"",seq:0}})})}function Ve(e,t){if(e.processedReqIds.add(t),e.processedReqIds.size>1e3){const a=e.processedReqIds.values().next().value;e.processedReqIds.delete(a)}}async function B(e,t){C(e,t),t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})}function Ie(e,t,a,s,o,l,c){return c?e.send(t,a,s,o,l,c):l?.trim()?e.send(t,a,s,o,l):o?.length?e.send(t,a,s,o):s?e.send(t,a,s):e.send(t,a)}function je(e,t,a,s,o,l,c){return e.prepareSend?e.prepareSend(t,a,s,o,l,c):Promise.resolve()}async function Ge(e,t,a,s,o,l,c,R,S,h="user"){e.evictIdleSessions();const n=Me(a);if(!n)throw new Error(`Unsupported agent: ${a}`);const r=s==="manager"&&a!=="manager"?new Te(n,a):n;X(r,t,a,c,R,null,S,e.managedRoomExecutor),h==="internal"?await r.start(t,o,l,{nativeSessionVisibility:h}):await r.start(t,o,l);const w={adapter:r,workDir:o,agentType:a,sessionMode:s,nativeSessionVisibility:h,agentSessionId:l??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:c??null,externalReplyTarget:R??null,externalChannelEnv:{...pe(a),...he(t,c,R)},managedRoomContextToken:null};return e.sessions.set(t,w),Qe(e,t,a,r),w}function Ke(e,t){const a=e.sessions.get(t),s=a?.currentRunId;if(!a||!s)return;const o=a.nextEventSeq;e.runTextAcc.delete(`${t}:${s}`),C(e,a),a.pendingTextEvent=null,a.currentRunId=null,a.currentRunPhase=null,a.nextEventSeq=0,a.heartbeatTimer&&(clearInterval(a.heartbeatTimer),a.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:t,runId:s,seq:o},seq:o,id:`agent-evt-${s}-${o}`}),e.chatQueue?.noteTerminal(t)}async function ht(e,t,a){const s=a?t.params.managedRoomContextToken??null:null;let o=!1;try{await Xe(e,t,a,()=>{o=!0})}finally{s&&!o&&e.revokeManagedRoomContextToken?.(s)}}async function Xe(e,t,a,s){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}const{sessionId:o,text:l,agentType:c,sessionMode:R,managerConfig:S,workDir:h,agentSessionId:n,modelId:r,systemPrompt:w,managerDefaultWorkerAgentType:_,managerDefaultWorkerModelId:k,reasoningEffort:A,clientMessageId:W,sessionListProjection:m,waitForDispatch:Y,responseId:Re,managedRoomContextToken:P,suppressExternalOwnerEcho:Se,queuedBatchItems:L,managedWorkerParentSessionId:$}=t.params,y=Re||t.id;be(m);const O=null,U="";if(a?.source==="room_activation"&&(Fe(a,w,P),c==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(Ve(e,t.id),!o||!l){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:"sessionId and text are required"});return}const g=c,v=g==="manager"||R==="manager"?"manager":"standard";v==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(o,_??null,k??null);const N=(g==="claude"||g==="codex"||g==="workbuddy"||g==="pi-coding-agent")&&typeof A=="string"&&A.trim()?A.trim():void 0,E=e.resolvePath(Je(g,h||ie.homedir(),n)||ie.homedir()),D=a?.managedAgentPolicy??($?e.managerRuntime?.getManagedAgentPolicyForWorker($,E)??null:null);if($&&!D)throw new Error("managed_worker_permission_context_unavailable");const Z=g==="codex"&&(a?.source==="room_activation"||$)?"internal":"user",J=$&&D?We({requestedPolicy:D.requestedPolicy,enforcement:D.enforcement,resolvedWorkDir:D.adapterPolicy.workDir}):w,Ae=E,F=Pe(t.params.attachments);F?.length&&$e(E,F);const p={text:l,attachments:F,localized:!1};let i=e.sessions.get(o);if(i){if(i.lastActiveAt=Date.now(),i.agentType!==g||(i.sessionMode??(i.agentType==="manager"?"manager":"standard"))!==v||i.workDir!==E||(i.nativeSessionVisibility??"user")!==Z||JSON.stringify(i.externalChannel??null)!==JSON.stringify(O??null)){e.sessions.delete(o);try{await B(e,i)}catch{e.processedReqIds.delete(t.id)}i=void 0}else if(n&&i.agentSessionId!==n)try{await i.adapter.resume(n),i.agentSessionId=n}catch{e.sessions.delete(o);try{await B(e,i)}catch{e.processedReqIds.delete(t.id)}i=void 0}}if(!i)try{i=await Ge(e,o,g,v,E,n,O,U,D,Z)}catch(d){const f=d instanceof Error&&d.message.startsWith("Unsupported agent:")?d.message:`Failed to start ${c}: ${d instanceof Error?d.message:String(d)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${o} agentType=${c} workDir=${E} agentSessionId=${n??""}: ${f}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:f,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:f});return}const I={id:W??`user-${t.id}`,sessionId:o,role:"user",ts:Date.now(),payload:le(p.text,p.attachments)},ee=Array.isArray(L)&&L.length>0?L.map(d=>({id:d.clientMessageId??d.id,sessionId:o,role:"user",ts:Date.parse(d.createdAt),payload:le(d.text,d.attachments)})):null,Q=ee??[I],M=De(o,I.id),te=M.state==="queued";if(M.state!=="not_found"&&!te){e.client.sendRes({type:"res",id:y,ok:!0,payload:M});return}if(Y){a?.source==="room_activation"&&X(i.adapter,o,g,O,U,P,a.managedAgentPolicy,e.managedRoomExecutor);try{await je(i.adapter,p.text,r,N,p.attachments,J,a?{source:a.source}:void 0)}catch(d){e.processedReqIds.delete(t.id),e.sessions.delete(o),await B(e,i),e.client.sendRes({type:"res",id:y,ok:!1,error:d instanceof Error?d.message:String(d)});return}}b({level:"info",sessionId:o,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:g,sessionMode:v,managerConfig:S??null,modelId:r,managerDefaultWorkerAgentType:_??null,managerDefaultWorkerModelId:k??null,reasoningEffort:N,...a?.source==="room_activation"?{managedRoomPromptSnapshot:a.managedRoomPromptSnapshot}:{}}});const V=(d,f)=>({...ue(ce()),sessionId:o,clientMessageId:I.id,messageId:I.id,deliveryState:d,bodyRevision:f,committedAt:new Date(I.ts).toISOString()}),ne=d=>{e.nativeFusion?.registerManagedSend({sessionId:o,agentType:g,sourceAgentType:me(g,r),canonicalMessageId:I.id,sourceSessionKey:i.agentSessionId??n??null,text:p.text,managedEchoPolicy:d?.deliveryMode==="external_owner_queue"&&!Se?"import_following":"suppress_following",externalRunId:d?.deliveryMode==="external_owner_queue"?`codex-external-${d.queuedSubmissionId??I.id}`:null})},ae=()=>{i.currentRunId||(i.currentRunId=`dispatch:${t.id}`,i.currentRunPhase="thinking",i.nextEventSeq=0)},re=()=>{if(ne(),te)return{...ue(ce()),sessionId:o,clientMessageId:M.clientMessageId,messageId:M.messageId,deliveryState:M.deliveryState,bodyRevision:M.bodyRevision,committedAt:M.committedAt};try{Ue(e,{sessionId:o,agentType:g,sessionMode:v,managerConfig:S??null,workDir:Ae,agentSessionId:i.agentSessionId??n??null,modelId:r,managerDefaultWorkerAgentType:_??null,managerDefaultWorkerModelId:k??null});let d=0;for(const[f,u]of Q.entries()){const x=ee?z(o,u,"committed"):q(o,u,"committed");f===0&&(d=x)}return ve(E,F??[]),T(e.client),V("committed",d)}catch(d){throw e.nativeFusion?.cancelManagedSend?.(I.id),d}},oe=d=>{i.currentRunId||(i.nextEventSeq=0),ne(d);const f=d?.deliveryMode==="external_owner_queue"?"queued":"executing";let u=0;for(const[x,j]of Q.entries()){const G=z(o,j,f);x===0&&(u=G)}return T(e.client),V(f,u)},se=async(d,f)=>{e.nativeFusion?.cancelManagedSend?.(I.id);const u=fe(g,d);console.error(`[chat.send] send failed reqId=${t.id} sessionId=${o} agentType=${c} workDir=${E} agentSessionId=${i.agentSessionId??n??""}: ${u}`),e.sessions.delete(o);try{await B(e,i)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:u,runId:"",seq:0}}),!f){const x={id:`agent-error-${t.id}-${Date.now()}`,sessionId:o,role:"agent",ts:Date.now(),payload:u};q(o,x),T(e.client)}f&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:u}))};if(Y){try{re()}catch(u){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:`Durable message commit failed: ${u instanceof Error?u.message:String(u)}`});return}ae();let d;try{a?.source==="room_activation"&&(ye(e,i,P),s());const u=p.attachments;d=await Ie(i.adapter,p.text,r,N,u,J,a?{source:a.source}:void 0),b({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(u){let x=0;for(const[G,ke]of Q.entries()){const Ee=z(o,ke,"failed");G===0&&(x=Ee)}T(e.client);const j=fe(g,u);await se(u,!1),e.client.sendRes({type:"res",id:y,ok:!0,payload:{...V("failed",x),failureMessage:j}});return}const f=oe(d??void 0);e.client.sendRes({type:"res",id:y,ok:!0,payload:{...f,...p.localized?{localizedAttachments:!0}:{},...d?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:d.deliveryMode,queuedSubmissionId:d.queuedSubmissionId}:{}}}),b({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}let de;try{de=re()}catch(d){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:`Durable message commit failed: ${d instanceof Error?d.message:String(d)}`});return}ae(),a?.source==="room_activation"&&(X(i.adapter,o,g,O,U,P,a.managedAgentPolicy,e.managedRoomExecutor),ye(e,i,P),s()),oe(),e.client.sendRes({type:"res",id:y,ok:!0,payload:{...de,...p.localized?{localizedAttachments:!0}:{}}}),b({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const we=p.attachments;Ie(i.adapter,p.text,r,N,we,J,a?{source:a.source}:void 0).then(()=>{b({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(d=>{z(o,I,"failed"),T(e.client),se(d,!1)})}async function yt(e,t){const{sessionId:a}=t.params,s=e.sessions.get(a);if(s){C(e,s);try{await s.adapter.stop()}catch{e.client.sendRes({type:"res",id:t.id,ok:!1,error:"agent_stop_failed"});return}Ke(e,a),e.activityPublisher?.publish(a,null)}e.client.sendRes({type:"res",id:t.id,ok:!0})}export{yt as handleChatAbort,ht as handleChatSend,Ue as sendSessionUpdateEvent};
1
+ import pe from"node:os";import{createHash as q,randomUUID as De}from"node:crypto";import{createAgent as be}from"../../agents/adapter.js";import{buildApprovalPendingPayload as qe,buildUserMessagePayload as me}from"@shennian/wire";import{ManagerModeAdapter as Ce}from"../../agents/manager.js";import{reportLog as C}from"../../log-reporter.js";import{lookupClaudeTranscriptCwd as $e}from"../../native-fusion/parsers.js";import{appendMessage as $,getDaemonInstallationId as ae,getMessageDeliveryStatus as je,recordSession as U,upsertMessage as j}from"../store.js";import{mergeProjectedSessions as Oe}from"../projection.js";import{buildManagedAgentEnv as We}from"../../agents/config-status.js";import{parsePersonalAttachmentReferences as He}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as Je,markAttachmentReferences as Be}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as _}from"../index-publisher.js";import{writeToolDetail as Ne}from"../tool-detail-store.js";import{getPersonalSyncIdentity as fe}from"../../personal-sync/epoch.js";import{buildManagedAgentPermissionPrompt as ye}from"../../room/managed-prompt-composer.js";import{getManagedRoomIpcRoot as Fe,MANAGED_ROOM_IPC_ROOT_ENV as ze}from"../../daemon-ipc/paths.js";import{refreshMachineAgents as Ve}from"./agents.js";function he(e,n,a){if(!e.managedAgentPolicy)throw new Error("managed_agent_policy_missing");const s=e.managedRoomPromptSnapshot;if(!s)throw new Error("managed_room_prompt_snapshot_missing");if(!n?.trim())throw new Error("managed_room_prompt_incomplete");if(!a||!/^mrc_[A-Za-z0-9_-]{43}$/.test(a))throw new Error("managed_room_context_unavailable");const r=q("sha256").update(JSON.stringify({requestedPolicy:e.managedAgentPolicy.requestedPolicy,enforcement:e.managedAgentPolicy.enforcement})).digest("hex"),u=q("sha256").update(e.managedAgentPolicy.adapterPolicy.workDir).digest("hex");if(s.runtimePermissionPolicyHash!==r||s.resolvedWorkDirHash!==u||s.enforcementLevel!==e.managedAgentPolicy.enforcement.level)throw new Error("managed_room_prompt_snapshot_mismatch")}function Ue(e){const n=e.indexOf(`
2
+ `),a=n>0?Math.min(n,80):Math.min(e.length,80);return e.slice(0,a)}function Ie(e,n,a){return`${e}:${q("sha256").update(`${n}\0${a??""}`).digest("hex")}`}function Le(e,n,a={}){if(e.state==="tool-call"||e.state==="tool-result"){const s={runId:e.runId,sourceSeq:e.seq};return{state:e.state,runId:e.runId,seq:e.seq,sessionId:n,detailRef:s,status:e.state==="tool-call"?"running":"completed",detailAvailable:!0,...e.durationMs!==void 0?{durationMs:e.durationMs}:{},...e.name?{name:e.name}:{},...e.source?{source:e.source}:{},...e.agentSessionId?{agentSessionId:e.agentSessionId}:{},...a}}return{...e,sessionId:n,...a}}const Ke=3e4;function Qe(e){return e.state==="heartbeat"?e.runPhase??null:e.state==="tool-call"||e.state==="tool-result"?"tool_running":e.state==="approval-pending"?"waiting_approval":e.state==="delta"?e.thinking?"thinking":"streaming_text":e.state==="init"||e.state==="start"?"thinking":null}function Re(e,n){return`Agent send failed: ${n instanceof Error?n.message:String(n)}`}function Se(e,n){return e!=="manager"?e:n==="claude"?"claude":"codex"}function Ge(e,n){U({sessionId:n.sessionId,agentType:n.agentType,sessionMode:n.sessionMode,managerConfig:n.managerConfig,workDir:n.workDir,agentSessionId:n.agentSessionId??null,modelId:n.modelId??null,managerDefaultWorkerAgentType:n.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:n.managerDefaultWorkerModelId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:n.sessionId,agentType:n.agentType,sessionMode:n.sessionMode,managerConfig:n.managerConfig,agentSessionId:n.agentSessionId??null,modelId:n.modelId??null,workDir:n.workDir,status:"active",externalChannel:null}}})}function ke(e){return We(e)}function we(e,n,a){return{}}function L(e,n,a,s,r,u,c,m){const l=a==="codex";e.configure?.({sessionId:n,externalChannel:s??null,env:{...ke(a),...we(n,s,r),...u&&!l?{SHENNIAN_MANAGED_ROOM_CONTEXT:u,[ze]:Fe()}:{}},...c?{managedAgentPolicy:c}:{},...l?{managedRoomContextToken:u??null,managedRoomExecutor:u?m??null:null}:{}})}function oe(e,n,a){const s=n.managedRoomContextToken;s&&s!==a&&e.revokeManagedRoomContextToken?.(s),n.managedRoomContextToken=a}function B(e,n){const a=n?.managedRoomContextToken;!n||!a||(n.managedRoomContextToken=null,e.revokeManagedRoomContextToken?.(a))}function Xe(e,n,a){if(e!=="claude"||!a)return n;const s=n.trim();return!!s&&s.startsWith("-")&&!s.includes("/")?$e(a)??n:n}function Ye(e,n,a,s){let r=null;function u(t,o={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:Le(t,n,o),seq:t.seq,id:`agent-evt-${t.runId}-${t.seq}`})}function c(t){t?.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null)}function m(t){if(!t.currentRunId||!t.currentRunPhase)return;const o=t.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:n,runId:t.currentRunId,seq:o,runPhase:t.currentRunPhase},seq:o,id:`agent-heartbeat-${t.currentRunId}-${o}-${Date.now()}`})}function l(t){!t||t.heartbeatTimer||(t.heartbeatTimer=setInterval(()=>{m(t)},Ke),t.heartbeatTimer.unref?.())}function y(t){const o=t?.pendingTextEvent;!t||!o||!o.text||($(n,{id:`agent-${o.runId}-${o.seq}`,sessionId:n,role:"agent",ts:Date.now(),payload:o.text}),_(e.client),u({state:"delta",runId:o.runId,seq:o.seq,text:o.text,thinking:o.thinking||void 0}),t.pendingTextEvent=null)}s.on("agentEvent",t=>{t.errorCode==="acp_auth_required"&&Ve(e).catch(()=>{});const o=e.sessions.get(n),k=Qe(t),v=t.state==="final"||t.state==="error"||t.state==="aborted";if(o&&(o.nextEventSeq=t.seq+1,v||(o.currentRunId=t.runId),!v&&k&&(o.currentRunPhase=k,l(o)),t.agentSessionId&&(o.agentSessionId=t.agentSessionId),o.journalRunId)){const h={agentSessionId:t.agentSessionId??o.agentSessionId,providerRunId:t.runId||null,lastObservedEventSeq:t.seq,...v?{}:{phase:k==="waiting_approval"?"waiting_approval":"running"}};e.runJournal?.update(n,o.journalRunId,h)}if(e.managerRuntime?.noteAgentEvent(n,t),t.state==="delta"&&t.thinking)return;t.state!=="delta"&&C({level:"info",sessionId:n,wsEvent:`agent.${t.state}`,wsDirection:"out",metadata:{runId:t.runId,seq:t.seq,agentType:a}});let E=!1;if(t.state==="tool-call"||t.state==="tool-result"){const h=t.toolCallId?`agent-tool-${q("sha256").update(`${t.runId}\0${t.toolCallId}`).digest("hex")}`:`agent-${t.runId}-${t.seq}`;Ne({sessionId:n,toolCallId:h,detailRevision:t.seq,ts:Date.now(),name:t.name??"tool",...t.args!==void 0?{args:t.args}:{},...t.result!==void 0?{result:t.result}:{},status:t.toolStatus??(t.state==="tool-call"?"running":"completed"),...t.durationMs!==void 0?{durationMs:t.durationMs}:{},detailRef:{runId:t.runId,sourceSeq:t.seq}}),(t.toolCallId?j:$)(n,{id:h,sessionId:n,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:t.state==="tool-call"?"tool_use":"tool_result",name:t.name,toolCallId:h,status:t.toolStatus??(t.state==="tool-call"?"running":"completed"),...t.durationMs!==void 0?{durationMs:t.durationMs}:{},detailRef:{runId:t.runId,sourceSeq:t.seq},detailRevision:t.seq,detailAvailable:!0,resultOmitted:t.state==="tool-result"})}),E=!0}else t.state==="approval-pending"?($(n,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:n,role:"agent",ts:Date.now(),payload:qe({...t.approval,permission:t.permission})}),E=!0,o&&U({sessionId:n,agentType:a,workDir:o.workDir,agentSessionId:o.agentSessionId,attentionId:Ie("approval",t.runId,t.seq)})):(t.state==="error"||t.state==="aborted")&&t.message&&($(n,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:n,role:"agent",ts:Date.now(),payload:t.message}),E=!0);E&&_(e.client);const w=`${n}:${t.runId}`;if(t.state==="delta"&&!t.thinking&&t.text&&e.runTextAcc.set(w,(e.runTextAcc.get(w)??"")+t.text),t.agentSessionId&&t.agentSessionId!==r&&(r=t.agentSessionId,e.nativeFusion?.noteManagedSourceSession(n,Se(a,t.source),t.agentSessionId),o&&U({sessionId:n,agentType:a,workDir:o.workDir,agentSessionId:t.agentSessionId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:n,agentType:a,agentSessionId:t.agentSessionId}}})),t.state==="delta"){const h=t.text??"";if(!h)return;o?.pendingTextEvent&&o.pendingTextEvent.runId!==t.runId&&y(o),o&&!o.pendingTextEvent&&(o.pendingTextEvent={runId:t.runId,seq:t.seq,text:"",thinking:!1}),o?.pendingTextEvent&&(o.pendingTextEvent.text+=h,o.pendingTextEvent.seq=t.seq);return}let N={};if(t.state==="final"){y(o);const h=e.runTextAcc.get(w)??"";h&&(N={messageSummary:Ue(h)}),e.runTextAcc.delete(w)}else t.state==="error"||t.state==="aborted"?(y(o),e.runTextAcc.delete(w)):(t.state==="tool-call"||t.state==="tool-result"||t.state==="approval-pending")&&y(o);v&&o?.currentRunId===t.runId&&(B(e,o),o.currentRunId=null,o.currentRunPhase=null,o.nextEventSeq=0,c(o),e.chatQueue?.noteTerminal(n),U({sessionId:n,agentType:a,workDir:o.workDir,agentSessionId:o.agentSessionId,status:t.state==="final"?"completed":"failed",attentionId:Ie("terminal",t.runId),lastActivityAt:new Date().toISOString()}),_(e.client),o.journalRunId&&(e.runJournal?.remove(n,o.journalRunId),e.recoveryBarrier?.end(n),o.journalRunId=null)),u(t,N)}),s.on("error",t=>{console.error(`[chat.send] adapter error sessionId=${n} agentType=${a}: ${t.message}`);const o=e.sessions.get(n);B(e,o),o?.journalRunId&&(e.runJournal?.update(n,o.journalRunId,{phase:"terminal"}),e.runJournal?.remove(n,o.journalRunId),e.recoveryBarrier?.end(n),o.journalRunId=null),e.sessions.delete(n),e.chatQueue?.noteTerminal(n),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:n,message:t.message,runId:"",seq:0}})})}function Ze(e,n){if(e.processedReqIds.add(n),e.processedReqIds.size>1e3){const a=e.processedReqIds.values().next().value;e.processedReqIds.delete(a)}}async function K(e,n){B(e,n),n.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null),n.adapter.removeAllListeners(),await n.adapter.stop().catch(()=>{})}function Ae(e,n,a,s,r,u,c){return c?e.send(n,a,s,r,u,c):u?.trim()?e.send(n,a,s,r,u):r?.length?e.send(n,a,s,r):s?e.send(n,a,s):e.send(n,a)}function en(e,n,a,s,r,u,c){return e.prepareSend?e.prepareSend(n,a,s,r,u,c):Promise.resolve()}async function Pe(e,n,a,s,r,u,c,m,l,y="user"){e.evictIdleSessions();const t=be(a);if(!t)throw new Error(`Unsupported agent: ${a}`);const o=s==="manager"&&a!=="manager"?new Ce(t,a):t;L(o,n,a,c,m,null,l,e.managedRoomExecutor),y==="internal"?await o.start(n,r,u,{nativeSessionVisibility:y}):await o.start(n,r,u);const k={adapter:o,workDir:r,agentType:a,sessionMode:s,nativeSessionVisibility:y,agentSessionId:u??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:c??null,externalReplyTarget:m??null,externalChannelEnv:{...ke(a),...we(n,c,m)},managedRoomContextToken:null,journalRunId:null};return e.sessions.set(n,k),Ye(e,n,a,o),k}async function An(e,n,a=()=>{}){let s=null,r=null,u=null,c;if(n.source==="managed_room"||n.source==="managed_direct"){const l=await e.prepareManagedRecovery?.(n);if(!l)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"managed recovery requires a fresh permission context"};if(l.agentType!==n.agentType||l.sessionMode!==n.sessionMode||e.resolvePath(l.workDir)!==e.resolvePath(n.workDir)||l.managedRoomPromptSnapshot.managedRoomPolicyVersion!==n.managedPolicyVersion||l.managedRoomPromptSnapshot.runtimePermissionPolicyHash!==n.managedRuntimePolicyHash)return e.revokeManagedRoomContextToken?.(l.managedRoomContextToken),{result:"recovery_failed",reason:"permission_context_unavailable",message:"managed recovery authority no longer matches the interrupted run"};const y={source:"room_activation",managedAgentPolicy:l.managedAgentPolicy,managedRoomPromptSnapshot:l.managedRoomPromptSnapshot};he(y,l.systemPrompt,l.managedRoomContextToken),s=l.managedAgentPolicy,r=l.managedRoomContextToken,u=l.systemPrompt,c={...y,recovery:"upgrade"}}else if(n.source==="manager_worker"){const l=n.parentSessionId;if(s=l?e.managerRuntime?.getManagedAgentPolicyForWorker(l,n.workDir)??null:null,!s)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker recovery requires the current parent policy"};if(q("sha256").update(JSON.stringify({requestedPolicy:s.requestedPolicy,enforcement:s.enforcement})).digest("hex")!==n.managedRuntimePolicyHash)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker policy changed after interruption"};u=ye({requestedPolicy:s.requestedPolicy,enforcement:s.enforcement,resolvedWorkDir:s.adapterPolicy.workDir})}c??={source:"upgrade_recovery",recovery:"upgrade"};let m=e.sessions.get(n.sessionId);return m||(m=await Pe(e,n.sessionId,n.agentType,n.sessionMode,n.workDir,n.agentSessionId,null,null,s,n.nativeSessionVisibility)),r&&(oe(e,m,r),L(m.adapter,n.sessionId,n.agentType,null,null,r,s,e.managedRoomExecutor)),m.journalRunId=n.runId,m.currentRunId=n.providerRunId??`recovery:${n.runId}`,m.currentRunPhase="thinking",m.adapter.recoverInterruptedRun({sessionId:n.sessionId,shennianRunId:n.runId,workDir:n.workDir,agentSessionId:n.agentSessionId,modelId:n.modelId,reasoningEffort:n.reasoningEffort,providerRunId:n.providerRunId,lastObservedEventSeq:n.lastObservedEventSeq,continueDispatchState:n.continueDispatchState,systemPrompt:u,sendContext:c,beforeContinueDispatch:a})}function nn(e,n){const a=e.sessions.get(n),s=a?.currentRunId;if(!a||!s)return;const r=a.nextEventSeq;e.runTextAcc.delete(`${n}:${s}`),B(e,a),a.pendingTextEvent=null,a.currentRunId=null,a.currentRunPhase=null,a.nextEventSeq=0,a.heartbeatTimer&&(clearInterval(a.heartbeatTimer),a.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:n,runId:s,seq:r},seq:r,id:`agent-evt-${s}-${r}`}),a.journalRunId&&(e.runJournal?.remove(n,a.journalRunId),e.recoveryBarrier?.end(n),a.journalRunId=null),e.chatQueue?.noteTerminal(n)}async function Pn(e,n,a){const s=a?n.params.managedRoomContextToken??null:null;let r=!1;try{await tn(e,n,a,()=>{r=!0})}finally{s&&!r&&e.revokeManagedRoomContextToken?.(s)}}async function tn(e,n,a,s){if(e.processedReqIds.has(n.id)){e.client.sendRes({type:"res",id:n.id,ok:!0});return}const{sessionId:r,text:u,agentType:c,sessionMode:m,managerConfig:l,workDir:y,agentSessionId:t,modelId:o,systemPrompt:k,managerDefaultWorkerAgentType:v,managerDefaultWorkerModelId:E,reasoningEffort:w,clientMessageId:N,sessionListProjection:h,waitForDispatch:Q,responseId:_e,managedRoomContextToken:O,suppressExternalOwnerEcho:ve,queuedBatchItems:G,managedWorkerParentSessionId:M,managedRecoverySource:Ee}=n.params,I=_e||n.id;Oe(h);const F=null,X="";if(a?.source==="room_activation"&&(he(a,k,O),c==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(Ze(e,n.id),!r||!u){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:I,ok:!1,error:"sessionId and text are required"});return}if(e.recoveryBarrier?.isDispatchFrozen()){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:I,ok:!1,error:"upgrade_in_progress"});return}e.recoveryBarrier?.isRecovering(r)&&await e.recoveryBarrier.wait(r);const p=c,D=p==="manager"||m==="manager"?"manager":"standard";D==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(r,v??null,E??null);const W=(p==="claude"||p==="codex"||p==="workbuddy"||p==="pi-coding-agent"||p.startsWith("acp:"))&&typeof w=="string"&&w.trim()?w.trim():void 0,A=e.resolvePath(Xe(p,y||pe.homedir(),t)||pe.homedir()),P=a?.managedAgentPolicy??(M?e.managerRuntime?.getManagedAgentPolicyForWorker(M,A)??null:null);if(M&&!P)throw new Error("managed_worker_permission_context_unavailable");const Y=p==="codex"&&(a?.source==="room_activation"||M)?"internal":"user",Z=M&&P?ye({requestedPolicy:P.requestedPolicy,enforcement:P.enforcement,resolvedWorkDir:P.adapterPolicy.workDir}):k,xe=A,z=He(n.params.attachments);z?.length&&Je(A,z);const R={text:u,attachments:z,localized:!1};let d=e.sessions.get(r);if(d){if(d.lastActiveAt=Date.now(),d.agentType!==p||(d.sessionMode??(d.agentType==="manager"?"manager":"standard"))!==D||d.workDir!==A||(d.nativeSessionVisibility??"user")!==Y||JSON.stringify(d.externalChannel??null)!==JSON.stringify(F??null)){e.sessions.delete(r);try{await K(e,d)}catch{e.processedReqIds.delete(n.id)}d=void 0}else if(t&&d.agentSessionId!==t)try{await d.adapter.resume(t),d.agentSessionId=t}catch{e.sessions.delete(r);try{await K(e,d)}catch{e.processedReqIds.delete(n.id)}d=void 0}}if(!d)try{d=await Pe(e,r,p,D,A,t,F,X,P,Y)}catch(i){const f=i instanceof Error&&i.message.startsWith("Unsupported agent:")?i.message:`Failed to start ${c}: ${i instanceof Error?i.message:String(i)}`;console.error(`[chat.send] start failed reqId=${n.id} sessionId=${r} agentType=${c} workDir=${A} agentSessionId=${t??""}: ${f}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:f,runId:"",seq:0}}),e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:I,ok:!1,error:f});return}const S={id:N??`user-${n.id}`,sessionId:r,role:"user",ts:Date.now(),payload:me(R.text,R.attachments)},re=Array.isArray(G)&&G.length>0?G.map(i=>({id:i.clientMessageId??i.id,sessionId:r,role:"user",ts:Date.parse(i.createdAt),payload:me(i.text,i.attachments)})):null,V=re??[S],x=je(r,S.id),se=x.state==="queued";if(x.state!=="not_found"&&!se){e.client.sendRes({type:"res",id:I,ok:!0,payload:x});return}if(Q){a?.source==="room_activation"&&L(d.adapter,r,p,F,X,O,a.managedAgentPolicy,e.managedRoomExecutor);try{await en(d.adapter,R.text,o,W,R.attachments,Z,a?{source:a.source}:void 0)}catch(i){e.processedReqIds.delete(n.id),e.sessions.delete(r),await K(e,d),e.client.sendRes({type:"res",id:I,ok:!1,error:i instanceof Error?i.message:String(i)});return}}C({level:"info",sessionId:r,wsEvent:"chat.send.start",metadata:{reqId:n.id,agentType:p,sessionMode:D,managerConfig:l??null,modelId:o,managerDefaultWorkerAgentType:v??null,managerDefaultWorkerModelId:E??null,reasoningEffort:W,...a?.source==="room_activation"?{managedRoomPromptSnapshot:a.managedRoomPromptSnapshot}:{}}});const H=(i,f)=>({...fe(ae()),sessionId:r,clientMessageId:S.id,messageId:S.id,deliveryState:i,bodyRevision:f,committedAt:new Date(S.ts).toISOString()}),ie=i=>{e.nativeFusion?.registerManagedSend({sessionId:r,agentType:p,sourceAgentType:Se(p,o),canonicalMessageId:S.id,sourceSessionKey:d.agentSessionId??t??null,text:R.text,managedEchoPolicy:i?.deliveryMode==="external_owner_queue"&&!ve?"import_following":"suppress_following",externalRunId:i?.deliveryMode==="external_owner_queue"?`codex-external-${i.queuedSubmissionId??S.id}`:null})},de=()=>{d.currentRunId||(d.currentRunId=`dispatch:${n.id}`,d.currentRunPhase="thinking",d.nextEventSeq=0)},le=()=>{if(!e.runJournal||d.journalRunId)return;const i=`run:${De()}`;e.runJournal.begin({daemonInstallationId:ae(),sessionId:r,runId:i,clientMessageId:S.id,agentType:p,sessionMode:D,workDir:A,agentSessionId:d.agentSessionId??t??null,modelId:o??null,reasoningEffort:W??null,nativeSessionVisibility:Y,providerRunId:null,source:a?.source==="room_activation"?Ee==="managed_direct"?"managed_direct":"managed_room":M?"manager_worker":"personal",parentSessionId:M??null,roomContextRegistryKey:typeof n.params.roomContextRegistryKey=="string"?n.params.roomContextRegistryKey:null,activationCauseMessageId:typeof n.params.activationCauseMessageId=="string"?n.params.activationCauseMessageId:null,rootCauseMessageId:typeof n.params.rootCauseMessageId=="string"?n.params.rootCauseMessageId:null,activationSequence:typeof n.params.activationSequence=="string"?n.params.activationSequence:null,activationHopCount:typeof n.params.activationHopCount=="number"?n.params.activationHopCount:null,managedPolicyVersion:a?.managedRoomPromptSnapshot.managedRoomPolicyVersion??null,managedRuntimePolicyHash:a?.managedRoomPromptSnapshot.runtimePermissionPolicyHash??(P?q("sha256").update(JSON.stringify({requestedPolicy:P.requestedPolicy,enforcement:P.enforcement})).digest("hex"):null)}),d.journalRunId=i},ue=()=>{if(ie(),se)return{...fe(ae()),sessionId:r,clientMessageId:x.clientMessageId,messageId:x.messageId,deliveryState:x.deliveryState,bodyRevision:x.bodyRevision,committedAt:x.committedAt};try{Ge(e,{sessionId:r,agentType:p,sessionMode:D,managerConfig:l??null,workDir:xe,agentSessionId:d.agentSessionId??t??null,modelId:o,managerDefaultWorkerAgentType:v??null,managerDefaultWorkerModelId:E??null});let i=0;for(const[f,g]of V.entries()){const T=re?j(r,g,"committed"):$(r,g,"committed");f===0&&(i=T)}return Be(A,z??[]),_(e.client),H("committed",i)}catch(i){throw e.nativeFusion?.cancelManagedSend?.(S.id),i}},ce=i=>{d.currentRunId||(d.nextEventSeq=0),ie(i);const f=i?.deliveryMode==="external_owner_queue"?"queued":"executing";let g=0;for(const[T,J]of V.entries()){const b=j(r,J,f);T===0&&(g=b)}return _(e.client),H(f,g)},ee=async(i,f)=>{e.nativeFusion?.cancelManagedSend?.(S.id);const g=Re(p,i);if(console.error(`[chat.send] send failed reqId=${n.id} sessionId=${r} agentType=${c} workDir=${A} agentSessionId=${d.agentSessionId??t??""}: ${g}`),d.adapter.hasPendingAuthenticationRetry?.()===!0){f&&e.client.sendRes({type:"res",id:I,ok:!0,payload:H("queued",0)});return}e.sessions.delete(r),d.journalRunId&&(e.runJournal?.update(r,d.journalRunId,{phase:"terminal"}),e.runJournal?.remove(r,d.journalRunId),e.recoveryBarrier?.end(r),d.journalRunId=null);try{await K(e,d)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:g,runId:"",seq:0}}),!f){const J={id:`agent-error-${n.id}-${Date.now()}`,sessionId:r,role:"agent",ts:Date.now(),payload:g};$(r,J),_(e.client)}f&&(e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:I,ok:!1,error:g}))};if(Q){try{ue()}catch(g){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:I,ok:!1,error:`Durable message commit failed: ${g instanceof Error?g.message:String(g)}`});return}de(),le();let i;try{a?.source==="room_activation"&&(oe(e,d,O),s());const g=R.attachments;i=await Ae(d.adapter,R.text,o,W,g,Z,a?{source:a.source}:void 0),C({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}catch(g){if(d.adapter.hasPendingAuthenticationRetry?.()){let b=0;for(const[ne,te]of V.entries()){const Te=j(r,te,"queued");ne===0&&(b=Te)}_(e.client),await ee(g,!1),e.client.sendRes({type:"res",id:I,ok:!0,payload:H("queued",b)});return}let T=0;for(const[b,ne]of V.entries()){const te=j(r,ne,"failed");b===0&&(T=te)}_(e.client);const J=Re(p,g);await ee(g,!1),e.client.sendRes({type:"res",id:I,ok:!0,payload:{...H("failed",T),failureMessage:J}});return}const f=ce(i??void 0);e.client.sendRes({type:"res",id:I,ok:!0,payload:{...f,...R.localized?{localizedAttachments:!0}:{},...i?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:i.deliveryMode,queuedSubmissionId:i.queuedSubmissionId}:{}}}),C({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});return}let ge;try{ge=ue()}catch(i){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:I,ok:!1,error:`Durable message commit failed: ${i instanceof Error?i.message:String(i)}`});return}de(),le(),a?.source==="room_activation"&&(L(d.adapter,r,p,F,X,O,a.managedAgentPolicy,e.managedRoomExecutor),oe(e,d,O),s()),ce(),e.client.sendRes({type:"res",id:I,ok:!0,payload:{...ge,...R.localized?{localizedAttachments:!0}:{}}}),C({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});const Me=R.attachments;Ae(d.adapter,R.text,o,W,Me,Z,a?{source:a.source}:void 0).then(()=>{C({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}).catch(i=>{j(r,S,d.adapter.hasPendingAuthenticationRetry?.()?"queued":"failed"),_(e.client),ee(i,!1)})}async function _n(e,n){const{sessionId:a}=n.params,s=e.sessions.get(a);if(s){s.journalRunId&&e.runJournal?.markAborted(a,s.journalRunId),B(e,s);try{await s.adapter.stop()}catch{e.client.sendRes({type:"res",id:n.id,ok:!1,error:"agent_stop_failed"});return}nn(e,a),e.activityPublisher?.publish(a,null)}e.client.sendRes({type:"res",id:n.id,ok:!0})}export{_n as handleChatAbort,Pn as handleChatSend,An as recoverJournaledRun,Ge as sendSessionUpdateEvent};
@@ -1,6 +1,6 @@
1
1
  import { type ReqFrame } from '@shennian/wire';
2
2
  import type { CliRelayClient } from '../relay/client.js';
3
- import type { RemoteAccessPauseController, UpgradePolicyController } from './types.js';
3
+ import type { SessionManagerRuntime, RemoteAccessPauseController, UpgradePolicyController } from './types.js';
4
4
  import type { UpgradeIdleState } from '../upgrade/scheduler.js';
5
5
  import type { NativeSessionFusionService } from '../native-fusion/service.js';
6
6
  import { type ManagedRoomEnqueueParams, type ManagedRoomQueueContext, type PreparedManagedRoomTurn } from './queue.js';
@@ -14,6 +14,7 @@ import '../agents/pi-coding-agent.js';
14
14
  import '../agents/manager.js';
15
15
  import type { ManagedRoomBinder } from '../room/managed-room-binding.js';
16
16
  import type { ManagedRoomExecutor } from '../room/managed-room-ipc.js';
17
+ import { ActiveRunJournal } from './run-recovery/journal.js';
17
18
  export { resolveSessionWorkDir } from '../fs/boundary.js';
18
19
  export declare class SessionManager {
19
20
  private client;
@@ -38,15 +39,33 @@ export declare class SessionManager {
38
39
  private activityProbeTimer;
39
40
  private attachmentCleanupTimer;
40
41
  private activeRequests;
41
- constructor(client: CliRelayClient, nativeFusion?: NativeSessionFusionService | null, cliVersion?: string | undefined, upgradePolicyController?: UpgradePolicyController | null, managedRoomBinder?: ManagedRoomBinder | null, remoteAccessPauseController?: RemoteAccessPauseController | null, managedRoomContextRevoker?: ((token: string) => void) | null, managedRoomExecutor?: ManagedRoomExecutor | null);
42
+ private acpDiscoveryWatcher;
43
+ private runJournal;
44
+ private recoveryBarrier;
45
+ private recoveryCoordinator;
46
+ private recoveryPass;
47
+ private prepareManagedRecovery;
48
+ constructor(client: CliRelayClient, nativeFusion?: NativeSessionFusionService | null, cliVersion?: string | undefined, upgradePolicyController?: UpgradePolicyController | null, managedRoomBinder?: ManagedRoomBinder | null, remoteAccessPauseController?: RemoteAccessPauseController | null, managedRoomContextRevoker?: ((token: string) => void) | null, managedRoomExecutor?: ManagedRoomExecutor | null, runJournalOverride?: ActiveRunJournal | null);
42
49
  private getRuntime;
50
+ resumePendingAcpAuthentication(agentType: string): Promise<boolean>;
43
51
  getUpgradeIdleState(): UpgradeIdleState;
52
+ prepareForUpgrade(upgradeJobId: string): {
53
+ activeRunCount: number;
54
+ };
55
+ cancelUpgradePreparation(): void;
56
+ recoverInterruptedRuns(): Promise<void>;
57
+ getPendingRecoveryCount(): number;
58
+ private commitRecoveryTerminal;
44
59
  setManagedRoomTurnPreparer(prepare: (contexts: ManagedRoomQueueContext[]) => Promise<PreparedManagedRoomTurn>): void;
60
+ setManagedAgentDirectTurnPreparer(prepare: (sessionId: string) => Promise<PreparedManagedRoomTurn | null>): void;
61
+ setManagedRecoveryPreparer(prepare: NonNullable<SessionManagerRuntime['prepareManagedRecovery']>): void;
45
62
  enqueueManagedRoomBatch(inputs: Array<{
46
63
  params: ManagedRoomEnqueueParams;
47
64
  context: ManagedRoomQueueContext;
48
65
  }>): Promise<void>;
49
66
  discardPendingManagedRoomMessages(sessionId: string): number;
67
+ private managedChatContext;
68
+ private dispatchManagedChat;
50
69
  private dispatchQueuedChat;
51
70
  synchronizeSessionDeletions(): Promise<void>;
52
71
  private synchronizeSessionDeletionsOnce;
@@ -55,6 +74,8 @@ export declare class SessionManager {
55
74
  private handleExternalTerminal;
56
75
  private publishManagedActivitySnapshots;
57
76
  private reloadCustomAgents;
77
+ private handleAgentPermissionResponse;
78
+ private handleAcpAgentRequest;
58
79
  handleReq(req: ReqFrame, options?: {
59
80
  propagateErrors?: boolean;
60
81
  }): Promise<void>;
@@ -1 +1,3 @@
1
- import{SESSION_SYNC_PROTOCOL_VERSION as p,SESSION_SYNC_DATA_EPOCH as f,isAvailableAgentType as k}from"@shennian/wire";import g from"node:crypto";import{getRegisteredAgents as S,unregisterAgent as A}from"../agents/adapter.js";import{loadConfig as T}from"../config/index.js";import{handleUpgradeStart as I,handleUpgradeStatus as C}from"../commands/upgrade.js";import{handleAgentsRefresh as P,handleModelsRefresh as E}from"./handlers/agents.js";import{handleAgentCapabilitiesList as D}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as M,handleAgentConfigGet as x,handleAgentConfigTest as O,handleAgentConfigUpsert as _}from"./handlers/agent-config.js";import{handleChatAbort as F,handleChatSend as v}from"./handlers/chat.js";import{handleSessionRefresh as N}from"./handlers/session-refresh.js";import{handleSessionToolDetail as Q}from"./handlers/tool-detail.js";import{handleSessionTitleSet as z}from"./handlers/title.js";import{handleSessionMessageStatus as U}from"./handlers/message-status.js";import{handleAgentWorkspaceValidation as W}from"./handlers/agent-workspace-validation.js";import{handleVoiceSessionBodyWindow as V}from"./handlers/voice-window.js";import{handleRemoteAccessPause as B}from"./handlers/remote-access.js";import{cleanupPendingTransfers as j,handleFsLs as $,handleFsRead as L,handleFsRename as H,handleFsWrite as Y,handleFsTransferAbort as G,handleFsTransferChunk as K,handleFsTransferFinish as X,handleFsTransferStart as Z,handleFsExportMarkdownPdf as J,handleFsExportMarkdownPdfSetup as q,handleFsArchiveZip as ee}from"./handlers/fs.js";import{handleRegionProbe as se,handleRegionSwitch as te,handleUpgradeSetPolicy as ae}from"./handlers/control.js";import{ManagerRuntimeService as ie,setManagerRuntimeService as b}from"../manager/runtime.js";import{ChatQueueManager as ne,TerminalQueuedChatDispatchError as oe}from"./queue.js";import{createAuthorizedFsRoot as re,createAuthorizedFsRootAsync as ce,resolveAuthorizedPath as le,resolveAuthorizedPathAsync as he,resolveSessionWorkDir as de}from"../fs/boundary.js";import"../agents/claude.js";import"../agents/codex.js";import"../agents/workbuddy.js";import"../agents/gemini.js";import"../agents/cursor.js";import"../agents/opencode.js";import"../agents/pi-coding-agent.js";import"../agents/manager.js";import{registerCustomAgent as ue}from"../agents/custom.js";import{handleManagedRoomBind as me,handleManagedRoomRestart as pe}from"./handlers/room-managed.js";import{getDaemonInstallationId as y,listSessionRecords as R,purgeCanonicalSession as fe,purgeSessionRecord as ge}from"./store.js";import{PersonalHistorySubscriptions as ve}from"./history-subscriptions.js";import{sweepAttachmentTransfers as be}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as ye}from"../native-fusion/native-source-deletions.js";import{purgeToolDetails as Re}from"./tool-detail-store.js";import{readDeletionSyncRevision as we,writeDeletionSyncRevision as w}from"./deletion-sync-state.js";import{assertDeletionSyncRunnable as ke,clearDeletionSyncFailure as Se,DeletionSyncRetryWaitError as Ae,DeletionSyncTerminalError as Te,recordDeletionSyncFailure as m}from"./deletion-sync-state.js";import{NativeSessionCleanupOutbox as Ie}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as l}from"../personal-sync/epoch.js";const Ce=50;function Pe(r,e){if(!r||!Number.isSafeInteger(r.baselineRevision)||r.baselineRevision<e||!Number.isSafeInteger(r.latestRevision)||r.latestRevision<r.baselineRevision||!Array.isArray(r.deletions))throw new Error("invalid deletion sync response");let t=r.baselineRevision;for(const s of r.deletions){if(!s?.sessionId||!Number.isSafeInteger(s.deletionRevision)||s.deletionRevision!==t+1||!Array.isArray(s.sources)||Number.isNaN(Date.parse(s.deletedAt))||s.nativeCleanupPolicy!==void 0&&s.nativeCleanupPolicy!=="none"&&s.nativeCleanupPolicy!=="auto")throw new Error("invalid session deletion command");for(const a of s.sources)if(!a?.sourceSessionKey||!k(a.agentType))throw new Error("invalid native source deletion command");t=s.deletionRevision}if(t!==r.latestRevision)throw new Error("invalid deletion sync revision range")}import{resolveSessionWorkDir as bs}from"../fs/boundary.js";class fs{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;remoteAccessPauseController;managedRoomContextRevoker;managedRoomExecutor;sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;historySubscriptions;nativeCleanupOutbox;activityProbeTimer=null;attachmentCleanupTimer=null;activeRequests=0;constructor(e,t=null,s,a=null,i=null,c=null,n=null,h=null){this.client=e,this.nativeFusion=t,this.cliVersion=s,this.upgradePolicyController=a,this.managedRoomBinder=i,this.remoteAccessPauseController=c,this.managedRoomContextRevoker=n,this.managedRoomExecutor=h,this.managerRuntime=new ie({getRuntime:()=>this.getRuntime(),dispatchReq:o=>this.handleReq(o,{propagateErrors:!0})}),this.chatQueue=new ne({getRuntime:()=>this.getRuntime(),dispatchReq:o=>this.handleReq(o),dispatchQueuedReq:o=>this.dispatchQueuedChat(o)}),this.historySubscriptions=new ve(this.client),this.nativeCleanupOutbox=new Ie({logger:console.info}),this.nativeFusion?.setExternalTerminalHandler?.(o=>{this.handleExternalTerminal(o)}),this.nativeCleanupOutbox.start(),b(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents(),this.activityProbeTimer=setInterval(()=>{this.publishManagedActivitySnapshots().catch(o=>{console.error("[session.activity] managed probe failed",o)})},15e3),this.activityProbeTimer.unref?.(),this.attachmentCleanupTimer=setInterval(()=>{const o=new Set([...R().map(d=>d.workDir),...Array.from(this.pendingTransfers.values()).map(d=>d.rootPath)]);for(const d of o)try{be(this.resolvePath(d))}catch(u){console.error("[attachment.cleanup] failed",u instanceof Error?u.message:String(u))}},6e4),this.attachmentCleanupTimer.unref?.()}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,t)=>this.resolveAuthorizedPath(e,t),resolveAuthorizedPathAsync:(e,t)=>this.resolveAuthorizedPathAsync(e,t),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,t)=>this.publishSessionActivity(e,t)},upgradePolicyController:this.upgradePolicyController,revokeManagedRoomContextToken:this.managedRoomContextRevoker,managedRoomExecutor:this.managedRoomExecutor}}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(t=>t.currentRunId!==null)&&e.push("active-agent-turn"),this.pendingTransfers.size>0&&e.push("file-transfer"),this.activeRequests>0&&e.push("active-daemon-request"),{idle:e.length===0,reasons:e}}setManagedRoomTurnPreparer(e){this.chatQueue.setManagedRoomTurnPreparer(e)}async enqueueManagedRoomBatch(e){const t=e.map(s=>{const a=R().find(i=>i.sessionId===s.params.sessionId);return{context:s.context,params:{...s.params,modelId:a?.modelId??s.params.modelId,sessionMode:a?.sessionMode??s.params.sessionMode,managerConfig:a?.managerConfig??s.params.managerConfig,managerDefaultWorkerAgentType:a?.managerDefaultWorkerAgentType??s.params.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:a?.managerDefaultWorkerModelId??s.params.managerDefaultWorkerModelId}}});await this.chatQueue.enqueueManagedRoomBatch(t)}discardPendingManagedRoomMessages(e){return this.chatQueue.discardPendingManagedRoomMessages(e)}async dispatchQueuedChat(e){const t={value:null},s=new Proxy(this.client,{get:(c,n)=>{if(n==="sendRes")return o=>{t.value=o};const h=Reflect.get(c,n,c);return typeof h=="function"?h.bind(c):h}}),a=e.params,i=a.roomActivation===!0?{source:"room_activation",managedAgentPolicy:a.managedAgentPolicy,managedRoomPromptSnapshot:a.managedRoomPromptSnapshot}:void 0;if(await v({...this.getRuntime(),client:s},e,i),t.value?.payload?.deliveryState==="failed")throw new oe(t.value.payload.failureMessage??t.value.error??"queued_chat_dispatch_failed");if(!t.value?.ok)throw new Error(t.value?.error??"queued_chat_dispatch_failed")}async synchronizeSessionDeletions(){ke();try{await this.synchronizeSessionDeletionsOnce(),Se()}catch(e){if(e instanceof Ae||e instanceof Te)throw e;const t=e instanceof Error?e.message:String(e),s=/offline|not connected|disconnect|timed out|timeout|temporar|unavailable|ECONN|ENOTFOUND|EIO|EBUSY/i.test(t);throw m({code:s?"transport_unavailable":"invalid_deletion_sync_response",retryable:s}),e}}async synchronizeSessionDeletionsOnce(){const e=y(),t=we(),s=await this.client.sendReq({type:"req",id:`session-deletions-sync-${g.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:p,dataEpoch:f,daemonInstallationId:e,afterRevision:t}});if(!s.ok){const n=s.rejection;throw n?(m({code:n.code,retryable:n.retryable&&n.requiredAction==="retry"}),new Error(n.code)):(m({code:"unstructured_rejection",retryable:!1}),new Error(s.error??"unstructured_rejection"))}const a=s.payload;l(a),Pe(a,t);let i=a.baselineRevision;i>t&&w(i);for(const n of a.deletions)n.deletionRevision<=i||(await this.applySessionDeletion(n),i=n.deletionRevision,w(i));if(i!==a.latestRevision)throw new Error(`deletion revision gap: applied ${i}, latest ${a.latestRevision}`);if(i===0)return;const c=await this.client.sendReq({type:"req",id:`session-deletions-ack-${g.randomUUID()}`,method:"session.deletions.ack",params:{protocolVersion:p,dataEpoch:f,daemonInstallationId:e,deletionRevision:i}});if(!c.ok)throw new Error(c.error??"session deletion acknowledgement failed")}async applySessionDeletion(e){const t=new ye({daemonInstallationId:y()});t.recordSession(e);for(const a of e.sources)t.record({...e,...a});const s=this.sessions.get(e.sessionId);if(s){this.revokeManagedRoomToken(s);try{await s.adapter.stop()}catch{}this.sessions.delete(e.sessionId)}this.historySubscriptions.closeSession(e.sessionId),this.nativeFusion?.handleSessionDeleted(e.sessionId),this.chatQueue.purgeSession(e.sessionId),Re(e.sessionId),fe(e.sessionId),ge(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null),this.nativeCleanupOutbox.enqueue(e),this.nativeCleanupOutbox.kick()}publishSessionActivity(e,t){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:t}})}async handleExternalTerminal(e){const t=this.sessions.get(e);t&&(this.sessions.delete(e),this.revokeManagedRoomToken(t),t.heartbeatTimer&&clearInterval(t.heartbeatTimer),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})),this.chatQueue.noteTerminal(e)}async publishManagedActivitySnapshots(){for(const[e,t]of this.sessions.entries()){if(t.heartbeatTimer||!t.currentRunId||!t.adapter.getStatus)continue;const s=await t.adapter.getStatus().catch(()=>null);if(!s?.active||!s.runPhase)continue;const a=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:s.runId||t.currentRunId,runPhase:s.runPhase,startedAt:new Date(t.lastActiveAt).toISOString(),updatedAt:a,canStop:s.canStop??!0});const i=t.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:s.runId||t.currentRunId,seq:i,runPhase:s.runPhase,canStop:s.canStop??!0},seq:i,id:`agent-status-${s.runId||t.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const t of S())t.startsWith("custom:")&&A(t);const e=T();for(const[t,s]of Object.entries(e.customAgents??{}))ue(t,s)}async handleReq(e,t={}){const s=this.getRuntime();this.activeRequests++;try{switch(e.method){case"chat.send":l(e.params),await v(s,e);break;case"chat.enqueue":l(e.params),await this.chatQueue.handleEnqueue(e);break;case"chat.queue.get":l(e.params),await this.chatQueue.handleGet(e);break;case"chat.queue.edit":l(e.params),await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":l(e.params),await this.chatQueue.handleDelete(e);break;case"chat.abort":l(e.params),await F(s,e);break;case"session.refresh":l(e.params),await N(s,e);break;case"session.history.open":this.historySubscriptions.handleOpen(e);break;case"session.history.sync":this.historySubscriptions.handleSync(e);break;case"session.history.page":this.historySubscriptions.handlePage(e);break;case"session.history.close":this.historySubscriptions.handleClose(e);break;case"session.body.replica.subscribe":this.historySubscriptions.handleReplicaSubscribe(e);break;case"session.body.replica.unsubscribe":this.historySubscriptions.handleReplicaUnsubscribe(e);break;case"session.history":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"upgrade_required"});break;case"session.message.status":U(s,e);break;case"session.tool.detail":await Q(s,e);break;case"voice.session.body.window":V(s,e);break;case"session.title.set":await z(s,e);break;case"room.agent.bind-managed":await me(s,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await pe(s,this.managedRoomBinder,e);break;case"fs.ls":await $(s,e);break;case"fs.read":await L(s,e);break;case"fs.write":await Y(s,e);break;case"fs.export.markdown-pdf":await J(s,e);break;case"fs.export.markdown-pdf.setup":await q(s,e);break;case"fs.archive.zip":await ee(s,e);break;case"fs.rename":await H(s,e);break;case"fs.transfer.start":await Z(s,e);break;case"fs.transfer.chunk":await K(s,e);break;case"fs.transfer.finish":await X(s,e);break;case"fs.transfer.abort":await G(s,e);break;case"region.probe":await se(s,e);break;case"region.switch":await te(s,e);break;case"upgrade.start":await I(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0});break;case"upgrade.status":await C(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await ae(s,e);break;case"machine.remote-access.pause":await B(this.client,this.remoteAccessPauseController,e);break;case"agents.refresh":await P(s,e);break;case"models.refresh":await E(s,e);break;case"agent.config.get":await x(s,e);break;case"agent.config.upsert":await _(s,e);break;case"agent.config.clear":await M(s,e);break;case"agent.config.test":await O(s,e);break;case"agent.workspace.validate":await W(s,e);break;case"agent.capabilities.list":await D(s,e);break;case"skill.list":case"skill.install":case"skill.doctor":case"skill.setup":case"skill.use":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;case"session.wechat-rpa.channel.get":case"session.wechat-rpa.channel.upsert":case"session.wechat-rpa.channel.sync":case"manager.wechat-rpa.channel.get":case"manager.wechat-rpa.channel.upsert":case"manager.wechat-rpa.channel.sync":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;default:this.client.sendRes({type:"res",id:e.id,ok:!1,error:`Unknown method: ${e.method}`})}}catch(a){if(t.propagateErrors)throw a;this.client.sendRes({type:"res",id:e.id,ok:!1,error:a instanceof Error?a.message:String(a)})}finally{this.activeRequests--}}evictIdleSessions(){if(this.sessions.size<Ce)return;let e=null;for(const[t,s]of this.sessions)(!e||s.lastActiveAt<e.session.lastActiveAt)&&(e={key:t,session:s});e&&(this.revokeManagedRoomToken(e.session),e.session.heartbeatTimer&&(clearInterval(e.session.heartbeatTimer),e.session.heartbeatTimer=null),e.session.adapter.removeAllListeners(),e.session.adapter.stop().catch(()=>{}),this.sessions.delete(e.key))}resolvePath(e){return de(e)}resolveAuthorizedPath(e,t){return le(e,re(t))}async resolveAuthorizedPathAsync(e,t){return he(e,await ce(t))}async cleanup(){this.nativeFusion?.setExternalTerminalHandler?.(null),this.nativeCleanupOutbox.stop(),this.historySubscriptions.closeAll(),this.activityProbeTimer&&(clearInterval(this.activityProbeTimer),this.activityProbeTimer=null),this.attachmentCleanupTimer&&(clearInterval(this.attachmentCleanupTimer),this.attachmentCleanupTimer=null);for(const[,e]of this.sessions)this.revokeManagedRoomToken(e),e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),j(this.getRuntime()),await this.managerRuntime.stop(),b(null)}revokeManagedRoomToken(e){const t=e.managedRoomContextToken;e.managedRoomContextToken=null,t&&this.managedRoomContextRevoker?.(t)}}export{fs as SessionManager,bs as resolveSessionWorkDir};
1
+ import{SESSION_SYNC_PROTOCOL_VERSION as R,SESSION_SYNC_DATA_EPOCH as b,isAvailableAgentType as S}from"@shennian/wire";import g from"node:crypto";import{createAgent as E,getRegisteredAgents as x,unregisterAgent as M}from"../agents/adapter.js";import{resetCommandSpecCache as D}from"../agents/command-spec.js";import{loadConfig as _}from"../config/index.js";import{handleUpgradeStart as O,handleUpgradeStatus as F}from"../commands/upgrade.js";import{handleAgentsRefresh as $,handleModelsRefresh as U,refreshMachineAgents as Q}from"./handlers/agents.js";import{AcpDiscoveryWatcher as B}from"../agents/acp/watcher.js";import{handleAgentCapabilitiesList as z}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as N,handleAgentConfigGet as W,handleAgentConfigTest as J,handleAgentConfigUpsert as V}from"./handlers/agent-config.js";import{handleChatAbort as j,handleChatSend as f,recoverJournaledRun as H}from"./handlers/chat.js";import{handleSessionRefresh as L}from"./handlers/session-refresh.js";import{handleSessionToolDetail as G}from"./handlers/tool-detail.js";import{handleSessionTitleSet as Y}from"./handlers/title.js";import{handleSessionMessageStatus as K}from"./handlers/message-status.js";import{handleAgentWorkspaceValidation as X}from"./handlers/agent-workspace-validation.js";import{handleVoiceSessionBodyWindow as Z}from"./handlers/voice-window.js";import{handleRemoteAccessPause as q}from"./handlers/remote-access.js";import{cleanupPendingTransfers as ee,handleFsLs as se,handleFsRead as te,handleFsRename as ae,handleFsWrite as ne,handleFsTransferAbort as ie,handleFsTransferChunk as re,handleFsTransferFinish as oe,handleFsTransferStart as ce,handleFsExportMarkdownPdf as de,handleFsExportMarkdownPdfSetup as le,handleFsArchiveZip as he}from"./handlers/fs.js";import{handleRegionProbe as ue,handleRegionSwitch as pe,handleUpgradeSetPolicy as me}from"./handlers/control.js";import{ManagerRuntimeService as ge,setManagerRuntimeService as k}from"../manager/runtime.js";import{ChatQueueManager as fe,TerminalQueuedChatDispatchError as ye}from"./queue.js";import{createAuthorizedFsRoot as ve,createAuthorizedFsRootAsync as we,resolveAuthorizedPath as Re,resolveAuthorizedPathAsync as be,resolveSessionWorkDir as Se}from"../fs/boundary.js";import"../agents/claude.js";import"../agents/codex.js";import"../agents/workbuddy.js";import"../agents/gemini.js";import"../agents/cursor.js";import"../agents/opencode.js";import"../agents/pi-coding-agent.js";import"../agents/manager.js";import{registerCustomAgent as ke}from"../agents/custom.js";import{handleManagedRoomBind as Ie,handleManagedRoomRestart as Ae}from"./handlers/room-managed.js";import{getDaemonInstallationId as y,listSessionRecords as v,purgeCanonicalSession as Te,purgeSessionRecord as Ce,recordSession as I,upsertMessage as A}from"./store.js";import{requestSessionIndexPublish as T}from"./index-publisher.js";import{reportUpgradeAggregateMetric as Pe}from"../log-reporter.js";import{PersonalHistorySubscriptions as Ee}from"./history-subscriptions.js";import{sweepAttachmentTransfers as xe}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as Me}from"../native-fusion/native-source-deletions.js";import{purgeToolDetails as De}from"./tool-detail-store.js";import{readDeletionSyncRevision as _e,writeDeletionSyncRevision as C}from"./deletion-sync-state.js";import{assertDeletionSyncRunnable as Oe,clearDeletionSyncFailure as Fe,DeletionSyncRetryWaitError as $e,DeletionSyncTerminalError as Ue,recordDeletionSyncFailure as w}from"./deletion-sync-state.js";import{NativeSessionCleanupOutbox as Qe}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as u}from"../personal-sync/epoch.js";import{ActiveRunJournal as Be}from"./run-recovery/journal.js";import{RunRecoveryBarrier as ze}from"./run-recovery/barrier.js";import{RunRecoveryCoordinator as Ne}from"./run-recovery/coordinator.js";const We=50;function Je(l,e){if(!l||!Number.isSafeInteger(l.baselineRevision)||l.baselineRevision<e||!Number.isSafeInteger(l.latestRevision)||l.latestRevision<l.baselineRevision||!Array.isArray(l.deletions))throw new Error("invalid deletion sync response");let t=l.baselineRevision;for(const s of l.deletions){if(!s?.sessionId||!Number.isSafeInteger(s.deletionRevision)||s.deletionRevision!==t+1||!Array.isArray(s.sources)||Number.isNaN(Date.parse(s.deletedAt))||s.nativeCleanupPolicy!==void 0&&s.nativeCleanupPolicy!=="none"&&s.nativeCleanupPolicy!=="auto")throw new Error("invalid session deletion command");for(const a of s.sources)if(!a?.sourceSessionKey||!S(a.agentType))throw new Error("invalid native source deletion command");t=s.deletionRevision}if(t!==l.latestRevision)throw new Error("invalid deletion sync revision range")}import{resolveSessionWorkDir as zs}from"../fs/boundary.js";class Us{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;remoteAccessPauseController;managedRoomContextRevoker;managedRoomExecutor;sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;historySubscriptions;nativeCleanupOutbox;activityProbeTimer=null;attachmentCleanupTimer=null;activeRequests=0;acpDiscoveryWatcher;runJournal;recoveryBarrier=new ze;recoveryCoordinator;recoveryPass=null;prepareManagedRecovery;constructor(e,t=null,s,a=null,i=null,d=null,r=null,o=null,h=null){this.client=e,this.nativeFusion=t,this.cliVersion=s,this.upgradePolicyController=a,this.managedRoomBinder=i,this.remoteAccessPauseController=d,this.managedRoomContextRevoker=r,this.managedRoomExecutor=o,this.runJournal=h??new Be(y());for(const n of this.runJournal.loadPendingReconciliation().records)this.recoveryBarrier.begin(n.sessionId);this.recoveryCoordinator=new Ne({journal:this.runJournal,barrier:this.recoveryBarrier,recover:(n,c)=>H(this.getRuntime(),n,c),commitTerminal:(n,c)=>this.commitRecoveryTerminal(n,c),isCanonicalTerminal:n=>{const c=v().find(m=>m.sessionId===n.sessionId),p=`terminal:${g.createHash("sha256").update(`${n.runId}\0`).digest("hex")}`;return c?.lastAttentionId===p},onOutcome:n=>Pe({kind:"recovery",adapterType:n.adapterType,result:n.result,stage:n.result==="continued"?"continue":n.result==="reattached"?"reattach":n.result==="resumed"?"resume":n.result==="terminal_reconciled"?"inspect":"terminal",durationMs:n.durationMs,retries:Math.max(0,n.attempts-1),...n.reason?{errorCode:n.reason}:{}})}),this.managerRuntime=new ge({getRuntime:()=>this.getRuntime(),dispatchReq:n=>this.handleReq(n,{propagateErrors:!0})}),this.chatQueue=new fe({getRuntime:()=>this.getRuntime(),dispatchReq:n=>this.handleReq(n),dispatchManagedReq:(n,c)=>this.dispatchManagedChat(n,c),dispatchQueuedReq:(n,c)=>this.dispatchQueuedChat(n,c)}),this.historySubscriptions=new Ee(this.client),this.nativeCleanupOutbox=new Qe({logger:console.info}),this.acpDiscoveryWatcher=new B(async()=>{D(),await Q(this.getRuntime())}),this.nativeFusion?.setExternalTerminalHandler?.(n=>{this.handleExternalTerminal(n)}),this.nativeCleanupOutbox.start(),k(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents();try{this.acpDiscoveryWatcher.start()}catch(n){console.warn(`[acp.discovery] watcher unavailable: ${n instanceof Error?n.message:String(n)}`)}this.activityProbeTimer=setInterval(()=>{this.publishManagedActivitySnapshots().catch(n=>{console.error("[session.activity] managed probe failed",n)})},15e3),this.activityProbeTimer.unref?.(),this.attachmentCleanupTimer=setInterval(()=>{const n=new Set([...v().map(c=>c.workDir),...Array.from(this.pendingTransfers.values()).map(c=>c.rootPath)]);for(const c of n)try{xe(this.resolvePath(c))}catch(p){console.error("[attachment.cleanup] failed",p instanceof Error?p.message:String(p))}},6e4),this.attachmentCleanupTimer.unref?.()}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,t)=>this.resolveAuthorizedPath(e,t),resolveAuthorizedPathAsync:(e,t)=>this.resolveAuthorizedPathAsync(e,t),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,t)=>this.publishSessionActivity(e,t)},upgradePolicyController:this.upgradePolicyController,revokeManagedRoomContextToken:this.managedRoomContextRevoker,managedRoomExecutor:this.managedRoomExecutor,runJournal:this.runJournal,recoveryBarrier:this.recoveryBarrier,prepareManagedRecovery:this.prepareManagedRecovery}}async resumePendingAcpAuthentication(e){const t=[...this.sessions.values()].find(s=>s.agentType===e&&s.adapter.hasPendingAuthenticationRetry?.());return t?.adapter.resumeAfterExternalAuthentication?(await t.adapter.resumeAfterExternalAuthentication(),!0):!1}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(t=>t.currentRunId!==null)&&e.push("active-agent-turn"),this.pendingTransfers.size>0&&e.push("file-transfer"),this.activeRequests>0&&e.push("active-daemon-request"),{idle:e.length===0,reasons:e}}prepareForUpgrade(e){this.recoveryBarrier.freezeDispatch();const t=this.runJournal.markPlannedUpgrade(e);return this.runJournal.sync(),{activeRunCount:t}}cancelUpgradePreparation(){this.recoveryBarrier.unfreezeDispatch()}async recoverInterruptedRuns(){this.recoveryPass??=this.recoveryCoordinator.recoverAll(),await this.recoveryPass}getPendingRecoveryCount(){return this.runJournal.loadPendingReconciliation().records.length}async commitRecoveryTerminal(e,t){const s=this.sessions.get(e.sessionId);s&&(s.currentRunId=null,s.currentRunPhase=null,s.journalRunId=null,s.heartbeatTimer&&(clearInterval(s.heartbeatTimer),s.heartbeatTimer=null)),this.runTextAcc.delete(`${e.sessionId}:${e.providerRunId??e.runId}`),this.publishSessionActivity(e.sessionId,null);const a=t.result==="interrupted_by_upgrade"?`\u4EFB\u52A1\u56E0\u795E\u5FF5\u5347\u7EA7\u800C\u4E2D\u65AD\uFF08${t.reason}\uFF09\u3002\u539F\u4F1A\u8BDD\u5DF2\u4FDD\u7559\uFF0C\u53EF\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002
2
+ Task interrupted by the Shennian upgrade (${t.reason}). The session is preserved and ready for a new message.`:`\u795E\u5FF5\u5347\u7EA7\u540E\u672A\u80FD\u6062\u590D\u4EFB\u52A1\uFF08${t.reason}\uFF09\u3002\u539F\u4F1A\u8BDD\u5DF2\u4FDD\u7559\uFF0C\u53EF\u91CD\u8BD5\u6216\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002
3
+ The task could not be recovered after the Shennian upgrade (${t.reason}). The session is preserved; retry or send a new message.`;A(e.sessionId,{id:`upgrade-recovery-terminal-${e.runId}`,sessionId:e.sessionId,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:"error",content:a,error:!0,recoveryState:t.result,recoveryReason:t.reason})}),I({sessionId:e.sessionId,agentType:e.agentType,sessionMode:e.sessionMode,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,status:"failed",attentionId:`upgrade-recovery:${e.runId}`,lastActivityAt:new Date().toISOString()}),T(this.client);const i=e.lastObservedEventSeq+1;this.client.sendAgentEvent({type:"event",event:"agent",payload:t.result==="interrupted_by_upgrade"?{state:"interrupted_by_upgrade",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:i,reason:t.reason}:{state:"recovery_failed",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:i,reason:t.reason,...t.message?{message:t.message}:{}},seq:i,id:`agent-recovery-terminal-${e.runId}`}),this.chatQueue.noteTerminal(e.sessionId)}setManagedRoomTurnPreparer(e){this.chatQueue.setManagedRoomTurnPreparer(e)}setManagedAgentDirectTurnPreparer(e){this.chatQueue.setManagedAgentDirectTurnPreparer(e)}setManagedRecoveryPreparer(e){this.prepareManagedRecovery=e}async enqueueManagedRoomBatch(e){const t=e.map(s=>{const a=v().find(i=>i.sessionId===s.params.sessionId);return{context:s.context,params:{...s.params,modelId:a?.modelId??s.params.modelId,sessionMode:a?.sessionMode??s.params.sessionMode,managerConfig:a?.managerConfig??s.params.managerConfig,managerDefaultWorkerAgentType:a?.managerDefaultWorkerAgentType??s.params.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:a?.managerDefaultWorkerModelId??s.params.managerDefaultWorkerModelId}}});await this.chatQueue.enqueueManagedRoomBatch(t)}discardPendingManagedRoomMessages(e){return this.chatQueue.discardPendingManagedRoomMessages(e)}managedChatContext(e){return{source:"room_activation",managedAgentPolicy:e.managedAgentPolicy,managedRoomPromptSnapshot:e.managedRoomPromptSnapshot}}async dispatchManagedChat(e,t){await f(this.getRuntime(),e,this.managedChatContext(t))}async dispatchQueuedChat(e,t){const s={value:null},a=new Proxy(this.client,{get:(i,d)=>{if(d==="sendRes")return o=>{s.value=o};const r=Reflect.get(i,d,i);return typeof r=="function"?r.bind(i):r}});if(await f({...this.getRuntime(),client:a},e,t?this.managedChatContext(t):void 0),s.value?.payload?.deliveryState==="failed")throw new ye(s.value.payload.failureMessage??s.value.error??"queued_chat_dispatch_failed");if(!s.value?.ok)throw new Error(s.value?.error??"queued_chat_dispatch_failed")}async synchronizeSessionDeletions(){Oe();try{await this.synchronizeSessionDeletionsOnce(),Fe()}catch(e){if(e instanceof $e||e instanceof Ue)throw e;const t=e instanceof Error?e.message:String(e),s=/offline|not connected|disconnect|timed out|timeout|temporar|unavailable|ECONN|ENOTFOUND|EIO|EBUSY/i.test(t);throw w({code:s?"transport_unavailable":"invalid_deletion_sync_response",retryable:s}),e}}async synchronizeSessionDeletionsOnce(){const e=y(),t=_e(),s=await this.client.sendReq({type:"req",id:`session-deletions-sync-${g.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:R,dataEpoch:b,daemonInstallationId:e,afterRevision:t}});if(!s.ok){const r=s.rejection;throw r?(w({code:r.code,retryable:r.retryable&&r.requiredAction==="retry"}),new Error(r.code)):(w({code:"unstructured_rejection",retryable:!1}),new Error(s.error??"unstructured_rejection"))}const a=s.payload;u(a),Je(a,t);let i=a.baselineRevision;i>t&&C(i);for(const r of a.deletions)r.deletionRevision<=i||(await this.applySessionDeletion(r),i=r.deletionRevision,C(i));if(i!==a.latestRevision)throw new Error(`deletion revision gap: applied ${i}, latest ${a.latestRevision}`);if(i===0)return;const d=await this.client.sendReq({type:"req",id:`session-deletions-ack-${g.randomUUID()}`,method:"session.deletions.ack",params:{protocolVersion:R,dataEpoch:b,daemonInstallationId:e,deletionRevision:i}});if(!d.ok)throw new Error(d.error??"session deletion acknowledgement failed")}async applySessionDeletion(e){const t=new Me({daemonInstallationId:y()});t.recordSession(e);for(const a of e.sources)t.record({...e,...a});this.runJournal.markSessionAborted(e.sessionId),this.recoveryBarrier.end(e.sessionId);const s=this.sessions.get(e.sessionId);if(s){this.revokeManagedRoomToken(s);try{await s.adapter.stop()}catch{}this.sessions.delete(e.sessionId)}this.historySubscriptions.closeSession(e.sessionId),this.nativeFusion?.handleSessionDeleted(e.sessionId),this.chatQueue.purgeSession(e.sessionId),De(e.sessionId),Te(e.sessionId),Ce(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null),this.nativeCleanupOutbox.enqueue(e),this.nativeCleanupOutbox.kick()}publishSessionActivity(e,t){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:t}})}async handleExternalTerminal(e){const t=this.sessions.get(e);t&&(this.sessions.delete(e),this.revokeManagedRoomToken(t),t.heartbeatTimer&&clearInterval(t.heartbeatTimer),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})),this.chatQueue.noteTerminal(e)}async publishManagedActivitySnapshots(){for(const[e,t]of this.sessions.entries()){if(t.heartbeatTimer||!t.currentRunId||!t.adapter.getStatus)continue;const s=await t.adapter.getStatus().catch(()=>null);if(!s?.active||!s.runPhase)continue;const a=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:s.runId||t.currentRunId,runPhase:s.runPhase,startedAt:new Date(t.lastActiveAt).toISOString(),updatedAt:a,canStop:s.canStop??!0});const i=t.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:s.runId||t.currentRunId,seq:i,runPhase:s.runPhase,canStop:s.canStop??!0},seq:i,id:`agent-status-${s.runId||t.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const t of x())t.startsWith("custom:")&&M(t);const e=_();for(const[t,s]of Object.entries(e.customAgents??{}))ke(t,s)}async handleAgentPermissionResponse(e){const t=typeof e.params.sessionId=="string"?e.params.sessionId:"",s=typeof e.params.permissionId=="string"?e.params.permissionId:"",a=typeof e.params.optionId=="string"?e.params.optionId:"",i=this.sessions.get(t);if(!t||!s||!a||!i?.adapter.respondToPermission){this.client.sendRes({type:"res",id:e.id,ok:!1,error:"permission_request_unavailable"});return}try{await i.adapter.respondToPermission(s,a),this.client.sendRes({type:"res",id:e.id,ok:!0,payload:{accepted:!0}})}catch(d){this.client.sendRes({type:"res",id:e.id,ok:!1,error:d instanceof Error?d.message:"permission_response_failed"})}}async handleAcpAgentRequest(e){const t=typeof e.params.agentType=="string"?e.params.agentType:"";if(!t.startsWith("acp:")||!S(t)){this.client.sendRes({type:"res",id:e.id,ok:!1,error:"invalid_acp_agent"});return}const s=e.method==="agent.auth.start"?[...this.sessions.values()].find(r=>r.agentType===t&&r.adapter.hasPendingAuthenticationRetry?.())?.adapter:void 0;if(String(e.method)==="agent.session-options.get"){const r=typeof e.params.sessionId=="string"?e.params.sessionId:"",o=this.sessions.get(r);if(!o||o.agentType!==t||!o.adapter.getSessionOptions){this.client.sendRes({type:"res",id:e.id,ok:!1,error:"acp_session_not_active"});return}this.client.sendRes({type:"res",id:e.id,ok:!0,payload:o.adapter.getSessionOptions()});return}const a=s??E(t);if(!a){this.client.sendRes({type:"res",id:e.id,ok:!1,error:"acp_agent_not_installed"});return}const i=typeof e.params.cwd=="string"&&e.params.cwd?e.params.cwd:process.cwd(),d=!s;try{d&&await a.start(`acp-control-${g.randomUUID()}`,i);let r={ok:!0};if(e.method==="agent.external-sessions.list")r={sessions:await a.listExternalSessions?.({cwd:typeof e.params.cwd=="string"?e.params.cwd:void 0})??[]};else if(e.method==="agent.external-sessions.import"){if(!a.loadExternalSession)throw new Error("ACP session history import is unsupported");const o=String(e.params.agentSessionId??""),h=String(e.params.sessionId??"");if(!o||!h)throw new Error("ACP and Shennian session ids are required");const n=await a.loadExternalSession(o),c=Date.now();for(const[p,m]of n.entries()){const P=`acp-import-${g.createHash("sha256").update(`${t}\0${o}\0${m.sourceId}`).digest("hex")}`;A(h,{id:P,sessionId:h,role:m.role,ts:c+p,payload:m.payload})}I({sessionId:h,agentType:t,workDir:i,agentSessionId:o,title:typeof e.params.title=="string"?e.params.title.slice(0,120):void 0,titleSource:typeof e.params.title=="string"?"agent_native":void 0,status:"active"}),T(this.client),this.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:h,agentType:t,agentSessionId:o,workDir:i,status:"active"}}}),r={sessionId:h,agentSessionId:o,importedMessageCount:n.length}}else if(e.method==="agent.auth.start"){if(!a.authenticate)throw new Error("ACP authentication is unsupported");await a.authenticate(String(e.params.methodId??""))}else if(e.method==="agent.auth.logout"){if(!a.logout)throw new Error("ACP logout is unsupported");await a.logout()}else{const o=String(e.params.agentSessionId??"");if(!o)throw new Error("ACP session id is required");if(e.method==="agent.external-sessions.close"){if(!a.closeExternalSession)throw new Error("ACP session close is unsupported");await a.closeExternalSession(o)}else{if(!a.deleteExternalSession)throw new Error("ACP session delete is unsupported");await a.deleteExternalSession(o)}}this.client.sendRes({type:"res",id:e.id,ok:!0,payload:r})}catch(r){this.client.sendRes({type:"res",id:e.id,ok:!1,error:r instanceof Error?r.message:"acp_request_failed"})}finally{d&&await a.stop().catch(()=>{})}}async handleReq(e,t={}){const s=this.getRuntime();this.activeRequests++;try{switch(e.method){case"chat.send":u(e.params),this.recoveryBarrier.isDispatchFrozen()||this.recoveryBarrier.isRecovering(String(e.params.sessionId??""))?await this.chatQueue.handleEnqueue({...e,method:"chat.enqueue"}):await f(s,e);break;case"chat.enqueue":u(e.params),await this.chatQueue.handleEnqueue(e);break;case"chat.queue.get":u(e.params),await this.chatQueue.handleGet(e);break;case"chat.queue.edit":u(e.params),await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":u(e.params),await this.chatQueue.handleDelete(e);break;case"chat.abort":u(e.params),await j(s,e);break;case"session.refresh":u(e.params),await L(s,e);break;case"session.history.open":this.historySubscriptions.handleOpen(e);break;case"session.history.sync":this.historySubscriptions.handleSync(e);break;case"session.history.page":this.historySubscriptions.handlePage(e);break;case"session.history.close":this.historySubscriptions.handleClose(e);break;case"session.body.replica.subscribe":this.historySubscriptions.handleReplicaSubscribe(e);break;case"session.body.replica.unsubscribe":this.historySubscriptions.handleReplicaUnsubscribe(e);break;case"session.history":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"upgrade_required"});break;case"session.message.status":K(s,e);break;case"session.tool.detail":await G(s,e);break;case"voice.session.body.window":Z(s,e);break;case"session.title.set":await Y(s,e);break;case"room.agent.bind-managed":await Ie(s,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await Ae(s,this.managedRoomBinder,e);break;case"fs.ls":await se(s,e);break;case"fs.read":await te(s,e);break;case"fs.write":await ne(s,e);break;case"fs.export.markdown-pdf":await de(s,e);break;case"fs.export.markdown-pdf.setup":await le(s,e);break;case"fs.archive.zip":await he(s,e);break;case"fs.rename":await ae(s,e);break;case"fs.transfer.start":await ce(s,e);break;case"fs.transfer.chunk":await re(s,e);break;case"fs.transfer.finish":await oe(s,e);break;case"fs.transfer.abort":await ie(s,e);break;case"region.probe":await ue(s,e);break;case"region.switch":await pe(s,e);break;case"upgrade.start":await O(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0,prepareForUpgrade:a=>this.prepareForUpgrade(a),cancelUpgradePreparation:()=>this.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()}});break;case"upgrade.status":await F(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await me(s,e);break;case"machine.remote-access.pause":await q(this.client,this.remoteAccessPauseController,e);break;case"agents.refresh":await $(s,e);break;case"agent.permission.respond":await this.handleAgentPermissionResponse(e);break;case"agent.auth.start":case"agent.auth.logout":case"agent.session-options.get":case"agent.external-sessions.list":case"agent.external-sessions.import":case"agent.external-sessions.close":case"agent.external-sessions.delete":await this.handleAcpAgentRequest(e);break;case"models.refresh":await U(s,e);break;case"agent.config.get":await W(s,e);break;case"agent.config.upsert":await V(s,e);break;case"agent.config.clear":await N(s,e);break;case"agent.config.test":await J(s,e);break;case"agent.workspace.validate":await X(s,e);break;case"agent.capabilities.list":await z(s,e);break;case"skill.list":case"skill.install":case"skill.doctor":case"skill.setup":case"skill.use":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;case"session.wechat-rpa.channel.get":case"session.wechat-rpa.channel.upsert":case"session.wechat-rpa.channel.sync":case"manager.wechat-rpa.channel.get":case"manager.wechat-rpa.channel.upsert":case"manager.wechat-rpa.channel.sync":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;default:this.client.sendRes({type:"res",id:e.id,ok:!1,error:`Unknown method: ${e.method}`})}}catch(a){if(t.propagateErrors)throw a;this.client.sendRes({type:"res",id:e.id,ok:!1,error:a instanceof Error?a.message:String(a)})}finally{this.activeRequests--}}evictIdleSessions(){if(this.sessions.size<We)return;let e=null;for(const[t,s]of this.sessions)(!e||s.lastActiveAt<e.session.lastActiveAt)&&(e={key:t,session:s});e&&(this.revokeManagedRoomToken(e.session),e.session.heartbeatTimer&&(clearInterval(e.session.heartbeatTimer),e.session.heartbeatTimer=null),e.session.adapter.removeAllListeners(),e.session.adapter.stop().catch(()=>{}),this.sessions.delete(e.key))}resolvePath(e){return Se(e)}resolveAuthorizedPath(e,t){return Re(e,ve(t))}async resolveAuthorizedPathAsync(e,t){return be(e,await we(t))}async cleanup(){this.acpDiscoveryWatcher.stop(),this.nativeFusion?.setExternalTerminalHandler?.(null),this.nativeCleanupOutbox.stop(),this.historySubscriptions.closeAll(),this.activityProbeTimer&&(clearInterval(this.activityProbeTimer),this.activityProbeTimer=null),this.attachmentCleanupTimer&&(clearInterval(this.attachmentCleanupTimer),this.attachmentCleanupTimer=null);for(const[,e]of this.sessions)this.revokeManagedRoomToken(e),e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),ee(this.getRuntime()),await this.managerRuntime.stop(),k(null)}revokeManagedRoomToken(e){const t=e.managedRoomContextToken;e.managedRoomContextToken=null,t&&this.managedRoomContextRevoker?.(t)}}export{Us as SessionManager,zs as resolveSessionWorkDir};
@@ -11,6 +11,9 @@ export type ManagedRoomQueueContext = {
11
11
  hopCount: number;
12
12
  };
13
13
  export type PreparedManagedRoomTurn = {
14
+ agentType: AgentType;
15
+ workDir: string;
16
+ agentSessionId: string;
14
17
  systemPrompt: string;
15
18
  sessionMode: SessionMode;
16
19
  managerConfig: ManagerConfig | null;
@@ -30,13 +33,16 @@ export declare class ChatQueueManager {
30
33
  private draining;
31
34
  private inFlightMessageIds;
32
35
  private prepareManagedRoomTurn;
36
+ private prepareManagedAgentDirectTurn;
33
37
  constructor(opts: {
34
38
  getRuntime: () => SessionManagerRuntime;
35
39
  dispatchReq: LocalReqDispatcher;
36
- dispatchQueuedReq: LocalReqDispatcher;
40
+ dispatchManagedReq?: (req: ReqFrame, prepared: PreparedManagedRoomTurn) => Promise<void>;
41
+ dispatchQueuedReq: (req: ReqFrame, prepared?: PreparedManagedRoomTurn) => Promise<void>;
37
42
  });
38
43
  getSnapshot(sessionId: string): ChatQueueSnapshot;
39
44
  setManagedRoomTurnPreparer(prepare: (contexts: ManagedRoomQueueContext[]) => Promise<PreparedManagedRoomTurn>): void;
45
+ setManagedAgentDirectTurnPreparer(prepare: (sessionId: string) => Promise<PreparedManagedRoomTurn | null>): void;
40
46
  enqueueManagedRoomBatch(inputs: Array<{
41
47
  params: ManagedRoomEnqueueParams;
42
48
  context: ManagedRoomQueueContext;
@@ -1,12 +1,12 @@
1
- import h from"node:fs";import{randomUUID as q}from"node:crypto";import{CODEX_DESKTOP_SEND_UNAVAILABLE as z,buildUserMessagePayload as j}from"@shennian/wire";import{isCodexThreadTailInterrupted as K}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as J}from"./projection.js";import{parsePersonalAttachmentReferences as D}from"./attachment-reference.js";import{assertFinishedAttachmentReferences as w,markAttachmentReferences as C}from"./attachment-transfer-store.js";import{resolvePersonalSyncV1Path as T}from"../personal-sync/paths.js";import{getPersonalSyncIdentity as S}from"../personal-sync/epoch.js";import{getDaemonInstallationId as R}from"../personal-sync/identity.js";import{appendMessage as v,getMessageDeliveryStatus as k,listSessionRecords as U,recordSession as b,tombstoneMessage as E,upsertMessage as P}from"./store.js";import{requestSessionIndexPublish as M}from"./index-publisher.js";class V extends Error{constructor(e){super(e),this.name="TerminalQueuedChatDispatchError"}}function G(){return{sessions:{}}}function $(){return new Date().toISOString()}function H(s){return`enqueue-send-${s}-${q()}`}function f(){try{const s=JSON.parse(h.readFileSync(A(),"utf-8"));return{sessions:s.sessions&&typeof s.sessions=="object"?s.sessions:{}}}catch(s){if(s.code==="ENOENT"||s instanceof Error&&s.message.startsWith("ENOENT:"))return G();throw s}}function p(s){const e=T();h.mkdirSync(e,{recursive:!0,mode:448});const n=A(),t=`${n}.tmp-${process.pid}-${q()}`;try{h.writeFileSync(t,`${JSON.stringify(s,null,2)}
2
- `,{encoding:"utf8",mode:384,flag:"wx"});const i=h.openSync(t,"r");if(typeof i=="number")try{h.fsyncSync(i)}finally{h.closeSync(i)}h.renameSync(t,n)}finally{try{h.unlinkSync(t)}catch{}}}function A(){return T("outbound-queue.json")}function Q(s){const e=$();return{...S(R()),id:s.queueMessageId||s.clientMessageId||`queue-${q()}`,sessionId:s.sessionId,text:s.text,agentType:s.agentType,sessionMode:s.sessionMode,managerConfig:s.managerConfig,workDir:s.workDir,agentSessionId:s.agentSessionId??null,modelId:s.modelId??null,systemPrompt:s.systemPrompt??null,managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:s.managerDefaultWorkerModelId??null,reasoningEffort:s.reasoningEffort??null,clientMessageId:s.clientMessageId??null,attachments:D(s.attachments),externalChannel:s.externalChannel??null,replyTarget:s.replyTarget??null,origin:s.origin,createdAt:e,updatedAt:e}}function x(s){return{id:s.clientMessageId??s.id,sessionId:s.sessionId,role:"user",ts:Date.parse(s.createdAt),payload:j(s.text,s.attachments)}}class he{opts;draining=new Set;inFlightMessageIds=new Map;prepareManagedRoomTurn=null;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}getSnapshot(e){return{...S(R()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:(f().sessions[e]??[]).map(B)}}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const n=this.opts.getRuntime(),t=f(),i=new Set,o=new Map;let a=!1;for(const r of e){const d=r.params;if(!d.sessionId||!d.text||!d.agentType||!d.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=Q({...S(R()),...d});u.origin="room",u.managedRoom=r.context;const c=t.sessions[d.sessionId]??[],l=N(c,u),I=u.clientMessageId??u.id,y=k(d.sessionId,I);if(!l&&y.state==="not_found")c.push(u),t.sessions[d.sessionId]=c,o.set(`${u.sessionId}\0${I}`,u),i.add(d.sessionId),a=!0;else if(l&&y.state==="not_found")o.set(`${l.sessionId}\0${I}`,l),i.add(d.sessionId);else if(l&&y.state==="queued")i.add(d.sessionId);else if(l){const m=c.filter(g=>g.id!==l.id);m.length?t.sessions[d.sessionId]=m:delete t.sessions[d.sessionId],a=!0}}a&&p(t);for(const r of o.values())v(r.sessionId,x(r),"queued"),b({sessionId:r.sessionId,agentType:r.agentType,sessionMode:r.sessionMode,managerConfig:r.managerConfig,workDir:r.workDir,agentSessionId:W(n,r),modelId:r.modelId});if(i.size!==0){M(n.client);for(const r of i)this.logLifecycle("enqueue.persisted",r,{origin:"room",pendingCount:f().sessions[r]?.length??0}),this.broadcast(r),this.drainNext(r)}}async handleEnqueue(e){const n=this.opts.getRuntime(),t=e.params;if(J(t.sessionListProjection),!t.sessionId||!t.text||!t.agentType||!t.workDir){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"});return}const i=n.resolvePath(t.workDir),o=D(t.attachments);o?.length&&w(i,o);const a={text:t.text,attachments:o,localized:!1},r=n.sessions.get(t.sessionId);if(t.agentType==="codex"&&t.agentSessionId&&r?.currentRunId?.startsWith("dispatch:")&&K(t.agentSessionId)){n.client.sendRes({type:"res",id:e.id,ok:!1,error:z});return}const d=await this.isSessionBusy(t.sessionId),u=f(),c=u.sessions[t.sessionId]??[];if(!d&&!c.length){await this.opts.dispatchReq({...e,id:H(e.id),method:"chat.send",params:{...t,text:a.text,responseId:e.id,clientMessageId:t.clientMessageId??t.queueMessageId,waitForDispatch:!0,attachments:a.attachments}});return}const l=Q({...t,text:a.text,attachments:a.attachments}),I=l.clientMessageId??l.id,y=k(t.sessionId,I);if(y.state!=="not_found"){n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:y.state==="queued",queueMessageId:l.id,...y,queue:this.getSnapshot(t.sessionId)}});return}const m=N(c,l);m||(u.sessions[t.sessionId]=[...c,l],p(u));const g=m??l;b({sessionId:g.sessionId,agentType:g.agentType,sessionMode:g.sessionMode,managerConfig:g.managerConfig,workDir:g.workDir,agentSessionId:g.agentSessionId,modelId:g.modelId}),v(g.sessionId,x(g),"queued"),C(n.resolvePath(g.workDir),g.attachments??[]),M(n.client),this.logLifecycle("enqueue.persisted",t.sessionId,{origin:t.origin??"app",queueMessageId:g.id,pendingCount:(f().sessions[t.sessionId]??[]).length}),this.broadcast(t.sessionId),n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:m?.id??l.id,...k(t.sessionId,m?.clientMessageId??m?.id??l.clientMessageId??l.id),queue:this.getSnapshot(t.sessionId),...a.localized?{localizedAttachments:!0}:{}}}),d||this.drainNext(t.sessionId)}async handleGet(e){const n=this.opts.getRuntime(),t=e.params;if(!t.sessionId){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}}),this.getSnapshot(t.sessionId).busy||this.drainNext(t.sessionId)}async handleEdit(e){const n=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const i=f(),o=i.sessions[t.sessionId]??[],a=o.findIndex(u=>u.id===t.queueMessageId);if(a<0){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(o[a].managedRoom){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const r=D(t.attachments),d=n.resolvePath(o[a].workDir);r?.length&&w(d,r),o[a]={...o[a],text:t.text,attachments:r,updatedAt:$()},i.sessions[t.sessionId]=o,p(i),P(t.sessionId,x(o[a]),"queued"),o[a].attachments?.length&&C(d,o[a].attachments??[]),M(n.client),this.broadcast(t.sessionId),n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const n=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const i=f(),o=i.sessions[t.sessionId]??[],a=o.find(d=>d.id===t.queueMessageId);if(a?.managedRoom){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const r=o.filter(d=>d.id!==t.queueMessageId);if(r.length===o.length){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}r.length?i.sessions[t.sessionId]=r:delete i.sessions[t.sessionId],p(i),E(t.sessionId,a?.clientMessageId??a?.id??t.queueMessageId),M(n.client),this.broadcast(t.sessionId),n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}noteTerminal(e){this.drainNext(e)}purgeSession(e){X(e),this.draining.delete(e)}discardPendingManagedRoomMessages(e){const n=f(),t=n.sessions[e]??[],i=this.inFlightMessageIds.get(e)??new Set,o=t.filter(r=>r.managedRoom&&!i.has(r.id));if(o.length===0)return 0;const a=t.filter(r=>!o.includes(r));a.length?n.sessions[e]=a:delete n.sessions[e],p(n);for(const r of o)E(e,r.clientMessageId??r.id);return M(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:o.length,pendingCount:a.length}),this.broadcast(e),o.length}drainIdleQueues(){const e=f();for(const n of Object.keys(e.sessions))this.drainNext(n)}async drainNext(e){if(this.draining.has(e))return;this.draining.add(e);let n=!1,t=[];try{if(await this.isSessionBusy(e)){this.logLifecycle("drain.deferred",e,{pendingCount:f().sessions[e]?.length??0});return}const i=f(),o=i.sessions[e]??[],a=o.filter(u=>{const c=u.clientMessageId??u.id,l=k(e,c).state;return l==="queued"||l==="not_found"});a.length!==o.length&&(a.length?i.sessions[e]=a:delete i.sessions[e],p(i),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:o.length-a.length,pendingCount:a.length}));const r=Y(a);if(r.length===0){this.broadcast(e);return}t=r,this.inFlightMessageIds.set(e,new Set(r.map(u=>u.id))),this.logLifecycle("dispatch.start",e,{batchCount:r.length,queueMessageId:r[0].id,origin:r[0].origin??"app"});for(const u of r)P(e,x(u),"queued");await this.dispatchQueuedMessages(r);const d=this.removeBatch(e,r);this.logLifecycle("dispatch.accepted",e,{batchCount:r.length,queueMessageId:r[0].id,pendingCount:d}),n=d>0,this.broadcast(e)}catch(i){if(i instanceof V){const o=this.removeBatch(e,t);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:t.length,queueMessageId:t[0]?.id,pendingCount:o,errorCode:O(i.message,"terminal_dispatch_failed")},"error"),n=o>0}else this.logLifecycle("dispatch.transient-failure",e,{errorCode:O(i instanceof Error?i.message:String(i),"transient_dispatch_failed")},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e),n&&queueMicrotask(()=>{this.drainNext(e)})}}removeBatch(e,n){if(n.length===0)return f().sessions[e]?.length??0;const t=f(),i=new Set(n.map(a=>a.id)),o=(t.sessions[e]??[]).filter(a=>!i.has(a.id));return o.length?t.sessions[e]=o:delete t.sessions[e],p(t),o.length}logLifecycle(e,n,t,i="info"){const o=`[chat.queue] ${JSON.stringify({event:e,sessionId:n,...t})}`;i==="error"?console.error(o):console.info(o)}async dispatchQueuedMessages(e){const n=e[0],t=e.flatMap(c=>c.managedRoom?[c.managedRoom]:[]),i=t.length>0?await this.prepareManagedRoomTurn?.(t):void 0;if(t.length>0&&!i)throw new Error("managed_room_turn_preparer_unavailable");const o=t.length>0?i:null,a=o?.sessionMode==="manager"?o.managerConfig:null,r=e.length===1?n.text:Z(e),d=te(e.flatMap(c=>c.attachments??[])),u=t.length>0?W(this.opts.getRuntime(),n):n.agentSessionId??null;await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${n.id}-${Date.now()}`,method:"chat.send",params:{...S(R()),sessionId:n.sessionId,text:r,agentType:n.agentType,sessionMode:o?.sessionMode??n.sessionMode,managerConfig:o?a:n.managerConfig,workDir:n.workDir,agentSessionId:u,modelId:n.modelId??void 0,systemPrompt:i?.systemPrompt??n.systemPrompt??null,managerDefaultWorkerAgentType:o?o.managerDefaultWorkerAgentType:n.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:o?o.managerDefaultWorkerModelId:n.managerDefaultWorkerModelId,reasoningEffort:n.reasoningEffort??void 0,clientMessageId:n.clientMessageId??n.id,attachments:d,externalChannel:n.externalChannel,replyTarget:n.replyTarget,...i??{},roomActivation:t.length>0||void 0,suppressExternalOwnerEcho:t.length>0||void 0,queuedBatchItems:e.map(B),waitForDispatch:!0}})}async isSessionBusy(e){const n=this.opts.getRuntime().sessions.get(e);if(!n)return!1;if(n.currentRunId)return!0;if(!n.adapter.getStatus)return!1;const t=await n.adapter.getStatus().catch(()=>null);return t?.active?(t.runId&&(n.currentRunId=t.runId,n.currentRunPhase=t.runPhase??n.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}function W(s,e){if(s.sessions.has(e.sessionId))return null;const t=U().find(i=>i.sessionId===e.sessionId)?.agentSessionId?.trim();return!t||t.startsWith("managed:")?null:t}function X(s){const e=f();e.sessions[s]&&(delete e.sessions[s],p(e))}function N(s,e){const n=_(e);if(n)return s.find(i=>_(i)===n)??null;if(e.origin!=="external")return null;const t=F(e);return s.find(i=>F(i)===t)??null}function B(s){const{origin:e,managedRoom:n,...t}=s;return t}function Y(s){const e=s[0];if(!e)return[];const n=L(e),t=[];for(const i of s){if(L(i)!==n)break;t.push(i)}return t}function L(s){return JSON.stringify({agentType:s.agentType,sessionMode:s.sessionMode??null,workDir:s.workDir,agentSessionId:s.agentSessionId??null,modelId:s.modelId??null,systemPrompt:s.systemPrompt??null,reasoningEffort:s.reasoningEffort??null,managerConfig:s.managerConfig??null,managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:s.managerDefaultWorkerModelId??null,externalChannel:s.externalChannel??null,replyTarget:s.replyTarget??null,roomRegistryKey:s.managedRoom?.registryKey??null})}function Z(s){return s.every(e=>e.managedRoom)?`[\u7FA4\u6D88\u606F]
3
- ${s.map(e=>ee(e.text)).filter(Boolean).join(`
1
+ import h from"node:fs";import{randomUUID as T}from"node:crypto";import{CODEX_DESKTOP_SEND_UNAVAILABLE as H,buildUserMessagePayload as V}from"@shennian/wire";import{isCodexThreadTailInterrupted as G}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as X}from"./projection.js";import{parsePersonalAttachmentReferences as C}from"./attachment-reference.js";import{assertFinishedAttachmentReferences as b,markAttachmentReferences as E}from"./attachment-transfer-store.js";import{resolvePersonalSyncV1Path as P}from"../personal-sync/paths.js";import{getPersonalSyncIdentity as R}from"../personal-sync/epoch.js";import{getDaemonInstallationId as k}from"../personal-sync/identity.js";import{appendMessage as $,getMessageDeliveryStatus as _,listSessionRecords as v,recordSession as W,tombstoneMessage as Q,upsertMessage as N}from"./store.js";import{requestSessionIndexPublish as D}from"./index-publisher.js";class Y extends Error{constructor(e){super(e),this.name="TerminalQueuedChatDispatchError"}}function Z(){return{sessions:{}}}function B(){return new Date().toISOString()}function ee(s){return`enqueue-send-${s}-${T()}`}function g(){try{const s=JSON.parse(h.readFileSync(L(),"utf-8"));return{sessions:s.sessions&&typeof s.sessions=="object"?s.sessions:{}}}catch(s){if(s.code==="ENOENT"||s instanceof Error&&s.message.startsWith("ENOENT:"))return Z();throw s}}function y(s){const e=P();h.mkdirSync(e,{recursive:!0,mode:448});const t=L(),n=`${t}.tmp-${process.pid}-${T()}`;try{h.writeFileSync(n,`${JSON.stringify(s,null,2)}
2
+ `,{encoding:"utf8",mode:384,flag:"wx"});const i=h.openSync(n,"r");if(typeof i=="number")try{h.fsyncSync(i)}finally{h.closeSync(i)}h.renameSync(n,t)}finally{try{h.unlinkSync(n)}catch{}}}function L(){return P("outbound-queue.json")}function F(s){const e=B();return{...R(k()),id:s.queueMessageId||s.clientMessageId||`queue-${T()}`,sessionId:s.sessionId,text:s.text,agentType:s.agentType,sessionMode:s.sessionMode,managerConfig:s.managerConfig,workDir:s.workDir,agentSessionId:s.agentSessionId??null,modelId:s.modelId??null,systemPrompt:s.systemPrompt??null,managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:s.managerDefaultWorkerModelId??null,reasoningEffort:s.reasoningEffort??null,clientMessageId:s.clientMessageId??null,attachments:C(s.attachments),externalChannel:s.externalChannel??null,replyTarget:s.replyTarget??null,origin:s.origin,createdAt:e,updatedAt:e}}function w(s){return{id:s.clientMessageId??s.id,sessionId:s.sessionId,role:"user",ts:Date.parse(s.createdAt),payload:V(s.text,s.attachments)}}class Me{opts;draining=new Set;inFlightMessageIds=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}getSnapshot(e){return{...R(k()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:(g().sessions[e]??[]).map(j)}}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const t=this.opts.getRuntime(),n=g(),i=new Set,r=new Map;let o=!1;for(const a of e){const d=a.params;if(!d.sessionId||!d.text||!d.agentType||!d.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=F({...R(k()),...d});u.origin="room",u.managedRoom=a.context;const p=n.sessions[d.sessionId]??[],c=z(p,u),l=u.clientMessageId??u.id,I=_(d.sessionId,l);if(!c&&I.state==="not_found")p.push(u),n.sessions[d.sessionId]=p,r.set(`${u.sessionId}\0${l}`,u),i.add(d.sessionId),o=!0;else if(c&&I.state==="not_found")r.set(`${c.sessionId}\0${l}`,c),i.add(d.sessionId);else if(c&&I.state==="queued")i.add(d.sessionId);else if(c){const m=p.filter(x=>x.id!==c.id);m.length?n.sessions[d.sessionId]=m:delete n.sessions[d.sessionId],o=!0}}o&&y(n);for(const a of r.values())$(a.sessionId,w(a),"queued"),W({sessionId:a.sessionId,agentType:a.agentType,sessionMode:a.sessionMode,managerConfig:a.managerConfig,workDir:a.workDir,agentSessionId:O(t,a),modelId:a.modelId});if(i.size!==0){D(t.client);for(const a of i)this.logLifecycle("enqueue.persisted",a,{origin:"room",pendingCount:g().sessions[a]?.length??0}),this.broadcast(a),this.drainNext(a)}}async handleEnqueue(e){const t=this.opts.getRuntime(),n=e.params;if(X(n.sessionListProjection),!n.sessionId||!n.text||!n.agentType||!n.workDir){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"});return}const i=n.managedAgentDirect?await this.prepareManagedAgentDirectTurn?.(n.sessionId):null;if(n.managedAgentDirect&&!i)throw new Error("managed_agent_context_unavailable");const r=i?v().find(S=>S.sessionId===n.sessionId):null,o=i?{...n,...i,agentType:i.agentType,sessionMode:i.sessionMode,managerConfig:i.managerConfig,workDir:i.workDir,agentSessionId:i.agentSessionId,modelId:r?.modelId??void 0,systemPrompt:i.systemPrompt,managerDefaultWorkerAgentType:i.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:i.managerDefaultWorkerModelId}:n,a=t.resolvePath(o.workDir),d=C(o.attachments);d?.length&&b(a,d);const u={text:o.text,attachments:d,localized:!1},p=t.sessions.get(o.sessionId);if(o.agentType==="codex"&&o.agentSessionId&&p?.currentRunId?.startsWith("dispatch:")&&G(o.agentSessionId)){t.client.sendRes({type:"res",id:e.id,ok:!1,error:H});return}const c=await this.isSessionBusy(o.sessionId),l=g(),I=l.sessions[o.sessionId]??[];if(!c&&!I.length){const S={...e,id:ee(e.id),method:"chat.send",params:{...o,text:u.text,responseId:e.id,clientMessageId:o.clientMessageId??o.queueMessageId,waitForDispatch:!0,attachments:u.attachments}};if(i){if(!this.opts.dispatchManagedReq)throw new Error("managed_agent_dispatch_unavailable");await this.opts.dispatchManagedReq(S,i)}else await this.opts.dispatchReq(S);return}const m=F({...o,text:u.text,attachments:u.attachments});i&&(m.managedAgentDirect=!0);const x=m.clientMessageId??m.id,q=_(o.sessionId,x);if(q.state!=="not_found"){t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:q.state==="queued",queueMessageId:m.id,...q,queue:this.getSnapshot(o.sessionId)}});return}const M=z(I,m);M||(l.sessions[o.sessionId]=[...I,m],y(l));const f=M??m;W({sessionId:f.sessionId,agentType:f.agentType,sessionMode:f.sessionMode,managerConfig:f.managerConfig,workDir:f.workDir,agentSessionId:f.agentSessionId,modelId:f.modelId}),$(f.sessionId,w(f),"queued"),E(t.resolvePath(f.workDir),f.attachments??[]),D(t.client),this.logLifecycle("enqueue.persisted",o.sessionId,{origin:o.origin??"app",queueMessageId:f.id,pendingCount:(g().sessions[o.sessionId]??[]).length}),this.broadcast(o.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:M?.id??m.id,..._(o.sessionId,M?.clientMessageId??M?.id??m.clientMessageId??m.id),queue:this.getSnapshot(o.sessionId),...u.localized?{localizedAttachments:!0}:{}}}),c||this.drainNext(o.sessionId)}async handleGet(e){const t=this.opts.getRuntime(),n=e.params;if(!n.sessionId){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(n.sessionId)}}),this.getSnapshot(n.sessionId).busy||this.drainNext(n.sessionId)}async handleEdit(e){const t=this.opts.getRuntime(),n=e.params;if(!n.sessionId||!n.queueMessageId||!n.text){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const i=g(),r=i.sessions[n.sessionId]??[],o=r.findIndex(u=>u.id===n.queueMessageId);if(o<0){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(r[o].managedRoom){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const a=C(n.attachments),d=t.resolvePath(r[o].workDir);a?.length&&b(d,a),r[o]={...r[o],text:n.text,attachments:a,updatedAt:B()},i.sessions[n.sessionId]=r,y(i),N(n.sessionId,w(r[o]),"queued"),r[o].attachments?.length&&E(d,r[o].attachments??[]),D(t.client),this.broadcast(n.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(n.sessionId)}})}async handleDelete(e){const t=this.opts.getRuntime(),n=e.params;if(!n.sessionId||!n.queueMessageId){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const i=g(),r=i.sessions[n.sessionId]??[],o=r.find(d=>d.id===n.queueMessageId);if(o?.managedRoom){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const a=r.filter(d=>d.id!==n.queueMessageId);if(a.length===r.length){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}a.length?i.sessions[n.sessionId]=a:delete i.sessions[n.sessionId],y(i),Q(n.sessionId,o?.clientMessageId??o?.id??n.queueMessageId),D(t.client),this.broadcast(n.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(n.sessionId)}})}noteTerminal(e){this.drainNext(e)}purgeSession(e){te(e),this.draining.delete(e)}discardPendingManagedRoomMessages(e){const t=g(),n=t.sessions[e]??[],i=this.inFlightMessageIds.get(e)??new Set,r=n.filter(a=>a.managedRoom&&!i.has(a.id));if(r.length===0)return 0;const o=n.filter(a=>!r.includes(a));o.length?t.sessions[e]=o:delete t.sessions[e],y(t);for(const a of r)Q(e,a.clientMessageId??a.id);return D(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:r.length,pendingCount:o.length}),this.broadcast(e),r.length}drainIdleQueues(){const e=g();for(const t of Object.keys(e.sessions))this.drainNext(t)}async drainNext(e){if(this.draining.has(e))return;this.draining.add(e);let t=!1,n=[];try{if(await this.isSessionBusy(e)){this.logLifecycle("drain.deferred",e,{pendingCount:g().sessions[e]?.length??0});return}const i=g(),r=i.sessions[e]??[],o=r.filter(u=>{const p=u.clientMessageId??u.id,c=_(e,p).state;return c==="queued"||c==="not_found"});o.length!==r.length&&(o.length?i.sessions[e]=o:delete i.sessions[e],y(i),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:r.length-o.length,pendingCount:o.length}));const a=ne(o);if(a.length===0){this.broadcast(e);return}n=a,this.inFlightMessageIds.set(e,new Set(a.map(u=>u.id))),this.logLifecycle("dispatch.start",e,{batchCount:a.length,queueMessageId:a[0].id,origin:a[0].origin??"app"});for(const u of a)N(e,w(u),"queued");await this.dispatchQueuedMessages(a);const d=this.removeBatch(e,a);this.logLifecycle("dispatch.accepted",e,{batchCount:a.length,queueMessageId:a[0].id,pendingCount:d}),t=d>0,this.broadcast(e)}catch(i){if(i instanceof Y){const r=this.removeBatch(e,n);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:n.length,queueMessageId:n[0]?.id,pendingCount:r,errorCode:U(i.message,"terminal_dispatch_failed")},"error"),t=r>0}else this.logLifecycle("dispatch.transient-failure",e,{errorCode:U(i instanceof Error?i.message:String(i),"transient_dispatch_failed")},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e),t&&queueMicrotask(()=>{this.drainNext(e)})}}removeBatch(e,t){if(t.length===0)return g().sessions[e]?.length??0;const n=g(),i=new Set(t.map(o=>o.id)),r=(n.sessions[e]??[]).filter(o=>!i.has(o.id));return r.length?n.sessions[e]=r:delete n.sessions[e],y(n),r.length}logLifecycle(e,t,n,i="info"){const r=`[chat.queue] ${JSON.stringify({event:e,sessionId:t,...n})}`;i==="error"?console.error(r):console.info(r)}async dispatchQueuedMessages(e){const t=e[0],n=e.flatMap(l=>l.managedRoom?[l.managedRoom]:[]),i=e.some(l=>l.managedAgentDirect),r=n.length>0?await this.prepareManagedRoomTurn?.(n):i?await this.prepareManagedAgentDirectTurn?.(t.sessionId):void 0;if(n.length>0&&!r)throw new Error("managed_room_turn_preparer_unavailable");if(i&&!r)throw new Error("managed_agent_context_unavailable");const o=r??null,a=o?.sessionMode==="manager"?o.managerConfig:null,d=e.length===1?t.text:se(e),u=oe(e.flatMap(l=>l.attachments??[])),p=i?r.agentSessionId:n.length>0?O(this.opts.getRuntime(),t):t.agentSessionId??null,c=r?v().find(l=>l.sessionId===t.sessionId):null;await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${t.id}-${Date.now()}`,method:"chat.send",params:{...R(k()),sessionId:t.sessionId,text:d,agentType:i?r.agentType:t.agentType,sessionMode:o?.sessionMode??t.sessionMode,managerConfig:o?a:t.managerConfig,workDir:i?r.workDir:t.workDir,agentSessionId:p,modelId:c?.modelId??t.modelId??void 0,systemPrompt:r?.systemPrompt??t.systemPrompt??null,managerDefaultWorkerAgentType:o?o.managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:o?o.managerDefaultWorkerModelId:t.managerDefaultWorkerModelId,reasoningEffort:t.reasoningEffort??void 0,clientMessageId:t.clientMessageId??t.id,attachments:u,externalChannel:t.externalChannel,replyTarget:t.replyTarget,roomContextRegistryKey:r?.roomContextRegistryKey,activationCauseMessageId:n.at(-1)?.causeMessageId,rootCauseMessageId:n.at(-1)?.rootCauseMessageId,activationSequence:n.at(-1)?.sequence,activationHopCount:n.at(-1)?.hopCount,managedRecoverySource:i?"managed_direct":n.length>0?"managed_room":void 0,managedRoomContextToken:r?.managedRoomContextToken,suppressExternalOwnerEcho:n.length>0||void 0,queuedBatchItems:e.map(j),waitForDispatch:!0}},r??void 0)}async isSessionBusy(e){const t=this.opts.getRuntime().recoveryBarrier;if(t?.isDispatchFrozen()||t?.isRecovering(e))return!0;const n=this.opts.getRuntime().sessions.get(e);if(!n)return!1;if(n.currentRunId)return!0;if(!n.adapter.getStatus)return!1;const i=await n.adapter.getStatus().catch(()=>null);return i?.active?(i.runId&&(n.currentRunId=i.runId,n.currentRunPhase=i.runPhase??n.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}function O(s,e){if(s.sessions.has(e.sessionId))return null;const n=v().find(i=>i.sessionId===e.sessionId)?.agentSessionId?.trim();return!n||n.startsWith("managed:")?null:n}function te(s){const e=g();e.sessions[s]&&(delete e.sessions[s],y(e))}function z(s,e){const t=A(e);if(t)return s.find(i=>A(i)===t)??null;if(e.origin!=="external")return null;const n=J(e);return s.find(i=>J(i)===n)??null}function j(s){const{origin:e,managedRoom:t,managedAgentDirect:n,...i}=s;return i}function ne(s){const e=s[0];if(!e)return[];const t=K(e),n=[];for(const i of s){if(K(i)!==t)break;n.push(i)}return n}function K(s){return JSON.stringify({agentType:s.agentType,sessionMode:s.sessionMode??null,workDir:s.workDir,agentSessionId:s.agentSessionId??null,modelId:s.modelId??null,systemPrompt:s.systemPrompt??null,reasoningEffort:s.reasoningEffort??null,managerConfig:s.managerConfig??null,managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:s.managerDefaultWorkerModelId??null,externalChannel:s.externalChannel??null,replyTarget:s.replyTarget??null,roomRegistryKey:s.managedRoom?.registryKey??null})}function se(s){return s.every(e=>e.managedRoom)?`[\u7FA4\u6D88\u606F]
3
+ ${s.map(e=>ie(e.text)).filter(Boolean).join(`
4
4
 
5
5
  `)}`:`[\u8FDE\u7EED\u7528\u6237\u6D88\u606F]
6
- ${s.map((e,n)=>`<\u6D88\u606F ${n+1}>
6
+ ${s.map((e,t)=>`<\u6D88\u606F ${t+1}>
7
7
  ${e.text.trim()}`).join(`
8
8
 
9
- `)}`}function ee(s){const e=s.trim(),n=e.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);return n?`<${n[1]}>
10
- ${n[2]}`:e.startsWith(`[\u7FA4\u6D88\u606F]
11
- `)?e.slice(6):e}function F(s){const e=_(s);return e?`id:${e}`:["body",ne(s.text),...(s.attachments??[]).map(n=>n.path||`${n.name}:${n.mimeType}`)].join(`
12
- `)}function _(s){const e=typeof s.clientMessageId=="string"?s.clientMessageId.trim():"";if(e)return e;const n=typeof s.id=="string"?s.id.trim():"";return n&&!n.startsWith("queue-")?n:""}function te(s){const e=new Set,n=[];for(const t of s){const i=t.path||`${t.name}:${t.mimeType}`;e.has(i)||(e.add(i),n.push(t))}return n.length?n:void 0}function ne(s){return String(s||"").replace(/\s+/g," ").trim()}function O(s,e){const n=s.toLowerCase();return n.includes("no rollout found")?"missing_rollout":n.includes("managed_room_permission_snapshot_mismatch")?"managed_room_permission_snapshot_mismatch":n.includes("managed_room_prompt_snapshot_mismatch")?"managed_room_prompt_snapshot_mismatch":n.includes("managed_room_turn_preparer_unavailable")?"managed_room_turn_preparer_unavailable":n.includes("timeout")?"timeout":n.includes("connection")?"connection_error":e}export{he as ChatQueueManager,V as TerminalQueuedChatDispatchError,X as purgeQueuedSessionData};
9
+ `)}`}function ie(s){const e=s.trim(),t=e.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);return t?`<${t[1]}>
10
+ ${t[2]}`:e.startsWith(`[\u7FA4\u6D88\u606F]
11
+ `)?e.slice(6):e}function J(s){const e=A(s);return e?`id:${e}`:["body",re(s.text),...(s.attachments??[]).map(t=>t.path||`${t.name}:${t.mimeType}`)].join(`
12
+ `)}function A(s){const e=typeof s.clientMessageId=="string"?s.clientMessageId.trim():"";if(e)return e;const t=typeof s.id=="string"?s.id.trim():"";return t&&!t.startsWith("queue-")?t:""}function oe(s){const e=new Set,t=[];for(const n of s){const i=n.path||`${n.name}:${n.mimeType}`;e.has(i)||(e.add(i),t.push(n))}return t.length?t:void 0}function re(s){return String(s||"").replace(/\s+/g," ").trim()}function U(s,e){const t=s.toLowerCase();return t.includes("no rollout found")?"missing_rollout":t.includes("managed_room_permission_snapshot_mismatch")?"managed_room_permission_snapshot_mismatch":t.includes("managed_room_prompt_snapshot_mismatch")?"managed_room_prompt_snapshot_mismatch":t.includes("managed_room_turn_preparer_unavailable")?"managed_room_turn_preparer_unavailable":t.includes("timeout")?"timeout":t.includes("connection")?"connection_error":e}export{Me as ChatQueueManager,Y as TerminalQueuedChatDispatchError,te as purgeQueuedSessionData};