shennian 0.4.30 → 0.4.32

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 (45) hide show
  1. package/dist/publish-build-manifest.json +52 -37
  2. package/dist/src/agents/acp/adapter.d.ts +5 -1
  3. package/dist/src/agents/acp/adapter.js +2 -2
  4. package/dist/src/agents/acp/client-services.d.ts +2 -1
  5. package/dist/src/agents/acp/client-services.js +2 -2
  6. package/dist/src/agents/acp/discovery.js +1 -1
  7. package/dist/src/agents/acp/process-client.js +1 -1
  8. package/dist/src/agents/claude.d.ts +1 -0
  9. package/dist/src/agents/claude.js +5 -5
  10. package/dist/src/agents/codex.js +3 -3
  11. package/dist/src/agents/command-spec.d.ts +1 -0
  12. package/dist/src/agents/command-spec.js +3 -3
  13. package/dist/src/agents/model-registry/service.js +1 -1
  14. package/dist/src/commands/daemon-instance-lock.d.ts +21 -0
  15. package/dist/src/commands/daemon-instance-lock.js +1 -0
  16. package/dist/src/commands/daemon.d.ts +2 -2
  17. package/dist/src/commands/daemon.js +6 -6
  18. package/dist/src/index.js +3 -3
  19. package/dist/src/relay/client.d.ts +4 -0
  20. package/dist/src/relay/client.js +1 -1
  21. package/dist/src/room/activation-service.d.ts +2 -0
  22. package/dist/src/room/activation-service.js +4 -4
  23. package/dist/src/room/managed-room-binding.d.ts +2 -1
  24. package/dist/src/room/managed-room-binding.js +1 -1
  25. package/dist/src/room/room-binding-client.js +1 -1
  26. package/dist/src/session/handlers/chat.js +2 -2
  27. package/dist/src/session/handlers/project-fs.d.ts +11 -0
  28. package/dist/src/session/handlers/project-fs.js +1 -0
  29. package/dist/src/session/handlers/room-managed.js +1 -1
  30. package/dist/src/session/manager.d.ts +6 -1
  31. package/dist/src/session/manager.js +2 -2
  32. package/dist/src/session/project-fs-store.d.ts +43 -0
  33. package/dist/src/session/project-fs-store.js +1 -0
  34. package/dist/src/session/queue.js +4 -4
  35. package/node_modules/@shennian/wire/dist/index.d.ts +1 -0
  36. package/node_modules/@shennian/wire/dist/index.js +1 -0
  37. package/node_modules/@shennian/wire/dist/methods.d.ts +3 -1
  38. package/node_modules/@shennian/wire/dist/methods.js +2 -0
  39. package/node_modules/@shennian/wire/dist/project-fs.d.ts +146 -0
  40. package/node_modules/@shennian/wire/dist/project-fs.js +462 -0
  41. package/node_modules/@shennian/wire/dist/project.d.ts +2 -0
  42. package/node_modules/@shennian/wire/dist/project.js +2 -0
  43. package/node_modules/@shennian/wire/dist/session-sync.d.ts +8 -1
  44. package/node_modules/@shennian/wire/dist/session-sync.js +1 -0
  45. package/package.json +1 -1
@@ -1 +1 @@
1
- import{HttpRoomActionClient as r}from"./room-action-client.js";class a extends Error{code;constructor(e,n){super(n),this.code=e,this.name="RoomBindingHttpError"}}class l{fetchImpl;baseUrl;actionClient;constructor(e,n=fetch){this.fetchImpl=n,this.baseUrl=e.replace(/\/+$/,""),this.actionClient=new r(e,n)}status=e=>this.actionClient.status(e);getAttachmentCapability=e=>this.actionClient.getAttachmentCapability(e);initializeAttachment=e=>this.actionClient.initializeAttachment(e);completeAttachment=e=>this.actionClient.completeAttachment(e);createRoom=e=>this.actionClient.createRoom(e);createOperatorRoom=e=>this.actionClient.createOperatorRoom(e);resolveNativeSession=e=>this.actionClient.resolveNativeSession(e);joinRoom=e=>this.actionClient.joinRoom(e);read=e=>this.actionClient.read(e);send=e=>this.actionClient.send(e);mode=e=>this.actionClient.mode(e);pause=e=>this.actionClient.pause(e);async create(e){let n;try{n=await this.fetchImpl(`${this.baseUrl}/api/rooms/${encodeURIComponent(e.roomId)}/bindings`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({bindingIntentId:e.bindingIntentId,agentIdentityId:e.agentIdentityId,nianSessionId:e.nianSessionId,runtimeAdapter:e.runtimeAdapter,sessionRegistryKey:e.sessionRegistryKey,activationCapability:e.activationCapability,activationMode:e.activationMode}),signal:AbortSignal.timeout(1e4)})}catch{throw new a("room_binding_server_unavailable","Shennian Room service is unavailable.")}if(!n.ok){let i=`room_binding_http_${n.status}`;try{const s=await n.json();typeof s.error=="string"&&/^[a-z0-9_-]{1,80}$/.test(s.error)&&(i=s.error)}catch{}throw new a(i,"Shennian Room binding request failed.")}let t;try{t=await n.json()}catch{throw new a("invalid_room_binding_response","Invalid Room binding response.")}return d(t,e)}async restart(e){let n;try{n=await this.fetchImpl(`${this.baseUrl}/api/rooms/${encodeURIComponent(e.roomId)}/bindings/${encodeURIComponent(e.bindingId)}/session`,{method:"PATCH",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({agentIdentityId:e.agentIdentityId,previousNianSessionId:e.previousNianSessionId,nianSessionId:e.nianSessionId,runtimeAdapter:e.runtimeAdapter,sessionRegistryKey:e.sessionRegistryKey}),signal:AbortSignal.timeout(1e4)})}catch{throw new a("room_binding_server_unavailable","Shennian Room service is unavailable.")}const t=await n.json().catch(()=>null);if(!n.ok){const s=t&&typeof t.error=="string"&&/^[a-z0-9_-]{1,80}$/.test(t.error)?t.error:`room_binding_http_${n.status}`;throw new a(s,"Shennian Room restart request failed.")}const i=t;if(!i?.binding||i.binding.id!==e.bindingId||i.binding.roomId!==e.roomId||i.binding.agentIdentityId!==e.agentIdentityId||i.binding.nianSessionId!==e.nianSessionId||i.binding.runtimeAdapter!==e.runtimeAdapter||typeof i.binding.agentMembershipId!="string"||typeof i.resumeSequence!="string"||!/^(0|[1-9]\d{0,19})$/.test(i.resumeSequence)||typeof i.idempotent!="boolean")throw new a("invalid_room_binding_response","Invalid Room restart response.");return{binding:i.binding,resumeSequence:i.resumeSequence,idempotent:i.idempotent}}}function d(o,e){if(!o||typeof o!="object")throw new a("invalid_room_binding_response","Invalid Room binding response.");const n=o,t=n.binding;if(!t||typeof t.id!="string"||typeof t.agentMembershipId!="string"||t.roomId!==e.roomId||t.agentIdentityId!==e.agentIdentityId||t.nianSessionId!==e.nianSessionId||t.runtimeAdapter!==e.runtimeAdapter||t.activationCapability!==e.activationCapability||!c(t.activationMode,e)||!["pending_approval","active","paused","detached"].includes(t.status??"")||typeof n.idempotent!="boolean")throw new a("invalid_room_binding_response","Room binding response does not match the request.");return{binding:t,idempotent:n.idempotent}}function c(o,e){return e.activationCapability==="managed"&&e.activationMode==="auto_follow"?o==="auto_follow"||o==="mention_only":o===e.activationMode}export{l as HttpRoomBindingClient,a as RoomBindingHttpError};
1
+ import{HttpRoomActionClient as r}from"./room-action-client.js";class o extends Error{code;constructor(e,n){super(n),this.code=e,this.name="RoomBindingHttpError"}}class l{fetchImpl;baseUrl;actionClient;constructor(e,n=fetch){this.fetchImpl=n,this.baseUrl=e.replace(/\/+$/,""),this.actionClient=new r(e,n)}status=e=>this.actionClient.status(e);getAttachmentCapability=e=>this.actionClient.getAttachmentCapability(e);initializeAttachment=e=>this.actionClient.initializeAttachment(e);completeAttachment=e=>this.actionClient.completeAttachment(e);createRoom=e=>this.actionClient.createRoom(e);createOperatorRoom=e=>this.actionClient.createOperatorRoom(e);resolveNativeSession=e=>this.actionClient.resolveNativeSession(e);joinRoom=e=>this.actionClient.joinRoom(e);read=e=>this.actionClient.read(e);send=e=>this.actionClient.send(e);mode=e=>this.actionClient.mode(e);pause=e=>this.actionClient.pause(e);async create(e){let n;try{n=await this.fetchImpl(`${this.baseUrl}/api/rooms/${encodeURIComponent(e.roomId)}/bindings`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({bindingIntentId:e.bindingIntentId,agentIdentityId:e.agentIdentityId,nianSessionId:e.nianSessionId,runtimeAdapter:e.runtimeAdapter,sessionRegistryKey:e.sessionRegistryKey,activationCapability:e.activationCapability,activationMode:e.activationMode}),signal:AbortSignal.timeout(1e4)})}catch{throw new o("room_binding_server_unavailable","Shennian Room service is unavailable.")}if(!n.ok){let i=`room_binding_http_${n.status}`;try{const s=await n.json();typeof s.error=="string"&&/^[a-z0-9_-]{1,80}$/.test(s.error)&&(i=s.error)}catch{}throw new o(i,"Shennian Room binding request failed.")}let t;try{t=await n.json()}catch{throw new o("invalid_room_binding_response","Invalid Room binding response.")}return d(t,e)}async restart(e){let n;try{n=await this.fetchImpl(`${this.baseUrl}/api/rooms/${encodeURIComponent(e.roomId)}/bindings/${encodeURIComponent(e.bindingId)}/session`,{method:"PATCH",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({agentIdentityId:e.agentIdentityId,previousNianSessionId:e.previousNianSessionId,nianSessionId:e.nianSessionId,runtimeAdapter:e.runtimeAdapter,sessionRegistryKey:e.sessionRegistryKey}),signal:AbortSignal.timeout(1e4)})}catch{throw new o("room_binding_server_unavailable","Shennian Room service is unavailable.")}const t=await n.json().catch(()=>null);if(!n.ok){const s=t&&typeof t.error=="string"&&/^[a-z0-9_-]{1,80}$/.test(t.error)?t.error:`room_binding_http_${n.status}`;throw new o(s,"Shennian Room restart request failed.")}const i=t;if(!i?.binding||i.binding.id!==e.bindingId||i.binding.roomId!==e.roomId||i.binding.agentIdentityId!==e.agentIdentityId||i.binding.nianSessionId!==e.nianSessionId||i.binding.runtimeAdapter!==e.runtimeAdapter||typeof i.binding.agentMembershipId!="string"||typeof i.resumeSequence!="string"||!/^(0|[1-9]\d{0,19})$/.test(i.resumeSequence)||typeof i.idempotent!="boolean")throw new o("invalid_room_binding_response","Invalid Room restart response.");return{binding:i.binding,resumeSequence:i.resumeSequence,idempotent:i.idempotent}}}function d(a,e){if(!a||typeof a!="object")throw new o("invalid_room_binding_response","Invalid Room binding response.");const n=a,t=n.binding;if(!t||typeof t.id!="string"||typeof t.agentMembershipId!="string"||t.roomId!==e.roomId||t.agentIdentityId!==e.agentIdentityId||t.nianSessionId!==e.nianSessionId||t.runtimeAdapter!==e.runtimeAdapter||t.activationCapability!==e.activationCapability||!c(t.activationMode,e)||!["pending_approval","active","paused","detached"].includes(t.status??"")||typeof n.idempotent!="boolean")throw new o("invalid_room_binding_response","Room binding response does not match the request.");return{binding:t,idempotent:n.idempotent}}function c(a,e){return a===e.activationMode}export{l as HttpRoomBindingClient,o as RoomBindingHttpError};
@@ -1,2 +1,2 @@
1
- import fe from"node:os";import{createHash as C,randomUUID as be}from"node:crypto";import{createAgent as qe}from"../../agents/adapter.js";import{buildApprovalPendingPayload as Ce,buildUserMessagePayload as me}from"@shennian/wire";import{ManagerModeAdapter as $e}from"../../agents/manager.js";import{reportLog as $}from"../../log-reporter.js";import{lookupClaudeTranscriptCwd as je}from"../../native-fusion/parsers.js";import{appendMessage as j,getDaemonInstallationId as oe,getMessageDeliveryStatus as Oe,recordSession as U,upsertMessage as D}from"../store.js";import{mergeProjectedSessions as We}from"../projection.js";import{buildManagedAgentEnv as He}from"../../agents/config-status.js";import{parsePersonalAttachmentReferences as Je}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as Be,markAttachmentReferences as Ne}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as w}from"../index-publisher.js";import{writeToolDetail as Fe}from"../tool-detail-store.js";import{getPersonalSyncIdentity as ye}from"../../personal-sync/epoch.js";import{buildManagedAgentPermissionPrompt as Ie}from"../../room/managed-prompt-composer.js";import{getManagedRoomIpcRoot as ze,MANAGED_ROOM_IPC_ROOT_ENV as Ve}from"../../daemon-ipc/paths.js";import{refreshMachineAgents as Ue}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 o=C("sha256").update(JSON.stringify({requestedPolicy:e.managedAgentPolicy.requestedPolicy,enforcement:e.managedAgentPolicy.enforcement})).digest("hex"),u=C("sha256").update(e.managedAgentPolicy.adapterPolicy.workDir).digest("hex");if(s.runtimePermissionPolicyHash!==o||s.resolvedWorkDirHash!==u||s.enforcementLevel!==e.managedAgentPolicy.enforcement.level)throw new Error("managed_room_prompt_snapshot_mismatch")}function Le(e){const n=e.indexOf(`
2
- `),a=n>0?Math.min(n,80):Math.min(e.length,80);return e.slice(0,a)}function Re(e,n,a){return`${e}:${C("sha256").update(`${n}\0${a??""}`).digest("hex")}`}function Ke(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 Qe=3e4;function Ge(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 Se(e,n){return`Agent send failed: ${n instanceof Error?n.message:String(n)}`}function we(e,n){return e!=="manager"?e:n==="claude"?"claude":"codex"}function Xe(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 He(e)}function _e(e,n,a){return{}}function L(e,n,a,s,o,u,c,f){const l=a==="codex";e.configure?.({sessionId:n,externalChannel:s??null,env:{...ke(a),..._e(n,s,o),...u&&!l?{SHENNIAN_MANAGED_ROOM_CONTEXT:u,[Ve]:ze()}:{}},...c?{managedAgentPolicy:c}:{},...l?{managedRoomContextToken:u??null,managedRoomExecutor:u?f??null:null}:{}})}function re(e,n,a){const s=n.managedRoomContextToken;s&&s!==a&&e.revokeManagedRoomContextToken?.(s),n.managedRoomContextToken=a}function N(e,n){const a=n?.managedRoomContextToken;!n||!a||(n.managedRoomContextToken=null,e.revokeManagedRoomContextToken?.(a))}function Ye(e,n,a){if(e!=="claude"||!a)return n;const s=n.trim();return!!s&&s.startsWith("-")&&!s.includes("/")?je(a)??n:n}function Ze(e,n,a,s){let o=null;function u(t,r={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:Ke(t,n,r),seq:t.seq,id:`agent-evt-${t.runId}-${t.seq}`})}function c(t){t?.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null)}function f(t){if(!t.currentRunId||!t.currentRunPhase)return;const r=t.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:n,runId:t.currentRunId,seq:r,runPhase:t.currentRunPhase},seq:r,id:`agent-heartbeat-${t.currentRunId}-${r}-${Date.now()}`})}function l(t){!t||t.heartbeatTimer||(t.heartbeatTimer=setInterval(()=>{f(t)},Qe),t.heartbeatTimer.unref?.())}function y(t){const r=t?.pendingTextEvent;!t||!r||!r.text||(j(n,{id:`agent-${r.runId}-${r.seq}`,sessionId:n,role:"agent",ts:Date.now(),payload:r.text}),w(e.client),u({state:"delta",runId:r.runId,seq:r.seq,text:r.text,thinking:r.thinking||void 0}),t.pendingTextEvent=null)}s.on("agentEvent",t=>{t.errorCode==="acp_auth_required"&&Ue(e).catch(()=>{});const r=e.sessions.get(n),k=Ge(t),x=t.state==="final"||t.state==="error"||t.state==="aborted";if(r&&(r.nextEventSeq=t.seq+1,x||(r.currentRunId=t.runId),!x&&k&&(r.currentRunPhase=k,l(r)),t.agentSessionId&&(r.agentSessionId=t.agentSessionId),r.journalRunId)){const I={agentSessionId:t.agentSessionId??r.agentSessionId,providerRunId:t.runId||null,lastObservedEventSeq:t.seq,...x?{}:{phase:k==="waiting_approval"?"waiting_approval":"running"}};e.runJournal?.update(n,r.journalRunId,I)}if(e.managerRuntime?.noteAgentEvent(n,t),t.state==="delta"&&t.thinking)return;t.state!=="delta"&&$({level:"info",sessionId:n,wsEvent:`agent.${t.state}`,wsDirection:"out",metadata:{runId:t.runId,seq:t.seq,agentType:a}});let P=!1;if(t.state==="tool-call"||t.state==="tool-result"){const I=t.toolCallId?`agent-tool-${C("sha256").update(`${t.runId}\0${t.toolCallId}`).digest("hex")}`:`agent-${t.runId}-${t.seq}`;Fe({sessionId:n,toolCallId:I,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?D:j)(n,{id:I,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:I,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"})}),P=!0}else t.state==="approval-pending"?(j(n,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:n,role:"agent",ts:Date.now(),payload:Ce({...t.approval,permission:t.permission})}),P=!0,r&&U({sessionId:n,agentType:a,workDir:r.workDir,agentSessionId:r.agentSessionId,attentionId:Re("approval",t.runId,t.seq)})):(t.state==="error"||t.state==="aborted")&&t.message&&(j(n,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:n,role:"agent",ts:Date.now(),payload:t.message}),P=!0);P&&w(e.client);const _=`${n}:${t.runId}`;if(t.state==="delta"&&!t.thinking&&t.text&&e.runTextAcc.set(_,(e.runTextAcc.get(_)??"")+t.text),t.agentSessionId&&t.agentSessionId!==o&&(o=t.agentSessionId,e.nativeFusion?.noteManagedSourceSession(n,we(a,t.source),t.agentSessionId),r&&U({sessionId:n,agentType:a,workDir:r.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 I=t.text??"";if(!I)return;r?.pendingTextEvent&&r.pendingTextEvent.runId!==t.runId&&y(r),r&&!r.pendingTextEvent&&(r.pendingTextEvent={runId:t.runId,seq:t.seq,text:"",thinking:!1}),r?.pendingTextEvent&&(r.pendingTextEvent.text+=I,r.pendingTextEvent.seq=t.seq);return}let F={};if(t.state==="final"){y(r);const I=e.runTextAcc.get(_)??"";I&&(F={messageSummary:Le(I)}),e.runTextAcc.delete(_)}else t.state==="error"||t.state==="aborted"?(y(r),e.runTextAcc.delete(_)):(t.state==="tool-call"||t.state==="tool-result"||t.state==="approval-pending")&&y(r);x&&r?.currentRunId===t.runId&&(N(e,r),r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,c(r),e.chatQueue?.noteTerminal(n),U({sessionId:n,agentType:a,workDir:r.workDir,agentSessionId:r.agentSessionId,status:t.state==="final"?"completed":"failed",attentionId:Re("terminal",t.runId),lastActivityAt:new Date().toISOString()}),w(e.client),r.journalRunId&&(e.runJournal?.remove(n,r.journalRunId),e.recoveryBarrier?.end(n),r.journalRunId=null)),u(t,F)}),s.on("error",t=>{console.error(`[chat.send] adapter error sessionId=${n} agentType=${a}: ${t.message}`);const r=e.sessions.get(n);N(e,r),r?.journalRunId&&(e.runJournal?.update(n,r.journalRunId,{phase:"terminal"}),e.runJournal?.remove(n,r.journalRunId),e.recoveryBarrier?.end(n),r.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 en(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){N(e,n),n.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null),n.adapter.removeAllListeners(),await n.adapter.stop().catch(()=>{})}function ve(e,n,a,s,o,u,c){return c?e.send(n,a,s,o,u,c):u?.trim()?e.send(n,a,s,o,u):o?.length?e.send(n,a,s,o):s?e.send(n,a,s):e.send(n,a)}function nn(e,n,a,s,o,u,c){return e.prepareSend?e.prepareSend(n,a,s,o,u,c):Promise.resolve()}async function Ae(e,n,a,s,o,u,c,f,l,y="user"){e.evictIdleSessions();const t=qe(a);if(!t)throw new Error(`Unsupported agent: ${a}`);const r=s==="manager"&&a!=="manager"?new $e(t,a):t;L(r,n,a,c,f,null,l,e.managedRoomExecutor),y==="internal"?await r.start(n,o,u,{nativeSessionVisibility:y}):await r.start(n,o,u);const k={adapter:r,workDir:o,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:f??null,externalChannelEnv:{...ke(a),..._e(n,c,f)},managedRoomContextToken:null,journalRunId:null};return e.sessions.set(n,k),Ze(e,n,a,r),k}async function vn(e,n,a=()=>{}){let s=null,o=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,o=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(C("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=Ie({requestedPolicy:s.requestedPolicy,enforcement:s.enforcement,resolvedWorkDir:s.adapterPolicy.workDir})}c??={source:"upgrade_recovery",recovery:"upgrade"};let f=e.sessions.get(n.sessionId);return f||(f=await Ae(e,n.sessionId,n.agentType,n.sessionMode,n.workDir,n.agentSessionId,null,null,s,n.nativeSessionVisibility)),o&&(re(e,f,o),L(f.adapter,n.sessionId,n.agentType,null,null,o,s,e.managedRoomExecutor)),f.journalRunId=n.runId,f.currentRunId=n.providerRunId??`recovery:${n.runId}`,f.currentRunPhase="thinking",n.agentType==="codex"&&n.deliveryMode==="external_owner_queue"&&n.agentSessionId&&n.externalRunId&&e.nativeFusion?.restoreExternalObservation({sessionId:n.sessionId,sourceSessionKey:n.agentSessionId,runId:n.externalRunId,canonicalMessageId:n.clientMessageId,createdAt:Date.parse(n.createdAt)})?{result:"reattached"}:f.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 tn(e,n){const a=e.sessions.get(n),s=a?.currentRunId;if(!a||!s)return;const o=a.nextEventSeq;e.runTextAcc.delete(`${n}:${s}`),N(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:o},seq:o,id:`agent-evt-${s}-${o}`}),a.journalRunId&&(e.runJournal?.remove(n,a.journalRunId),e.recoveryBarrier?.end(n),a.journalRunId=null),e.chatQueue?.noteTerminal(n)}async function An(e,n,a){const s=a?n.params.managedRoomContextToken??null:null;let o=!1;try{await an(e,n,a,()=>{o=!0})}finally{s&&!o&&e.revokeManagedRoomContextToken?.(s)}}async function an(e,n,a,s){if(e.processedReqIds.has(n.id)){e.client.sendRes({type:"res",id:n.id,ok:!0});return}const{sessionId:o,text:u,agentType:c,sessionMode:f,managerConfig:l,workDir:y,agentSessionId:t,modelId:r,systemPrompt:k,managerDefaultWorkerAgentType:x,managerDefaultWorkerModelId:P,reasoningEffort:_,clientMessageId:F,sessionListProjection:I,waitForDispatch:Q,responseId:xe,managedRoomContextToken:O,suppressExternalOwnerEcho:Pe,queuedBatchItems:G,managedWorkerParentSessionId:E,managedRecoverySource:Me}=n.params,h=xe||n.id;We(I);const z=null,X="";if(a?.source==="room_activation"&&(he(a,k,O),c==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(en(e,n.id),!o||!u){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:"sessionId and text are required"});return}if(e.recoveryBarrier?.isDispatchFrozen()){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:"upgrade_in_progress"});return}e.recoveryBarrier?.isRecovering(o)&&await e.recoveryBarrier.wait(o);const p=c,b=p==="manager"||f==="manager"?"manager":"standard";b==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(o,x??null,P??null);const W=(p==="claude"||p==="codex"||p==="workbuddy"||p==="pi-coding-agent"||p.startsWith("acp:"))&&typeof _=="string"&&_.trim()?_.trim():void 0,v=e.resolvePath(Ye(p,y||fe.homedir(),t)||fe.homedir()),A=a?.managedAgentPolicy??(E?e.managerRuntime?.getManagedAgentPolicyForWorker(E,v)??null:null);if(E&&!A)throw new Error("managed_worker_permission_context_unavailable");const Y=p==="codex"&&(a?.source==="room_activation"||E)?"internal":"user",Z=E&&A?Ie({requestedPolicy:A.requestedPolicy,enforcement:A.enforcement,resolvedWorkDir:A.adapterPolicy.workDir}):k,Ee=v,V=Je(n.params.attachments);V?.length&&Be(v,V);const S={text:u,attachments:V,localized:!1};let d=e.sessions.get(o);if(d){if(d.lastActiveAt=Date.now(),d.agentType!==p||(d.sessionMode??(d.agentType==="manager"?"manager":"standard"))!==b||d.workDir!==v||(d.nativeSessionVisibility??"user")!==Y||JSON.stringify(d.externalChannel??null)!==JSON.stringify(z??null)){e.sessions.delete(o);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(o);try{await K(e,d)}catch{e.processedReqIds.delete(n.id)}d=void 0}}if(!d)try{d=await Ae(e,o,p,b,v,t,z,X,A,Y)}catch(i){const m=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=${o} agentType=${c} workDir=${v} agentSessionId=${t??""}: ${m}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:m,runId:"",seq:0}}),e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:m});return}const R={id:F??`user-${n.id}`,sessionId:o,role:"user",ts:Date.now(),payload:me(S.text,S.attachments)},se=Array.isArray(G)&&G.length>0?G.map(i=>({id:i.clientMessageId??i.id,sessionId:o,role:"user",ts:Date.parse(i.createdAt),payload:me(i.text,i.attachments)})):null,H=se??[R],M=Oe(o,R.id),ie=M.state==="queued";if(M.state!=="not_found"&&!ie){e.client.sendRes({type:"res",id:h,ok:!0,payload:M});return}if(Q){a?.source==="room_activation"&&L(d.adapter,o,p,z,X,O,a.managedAgentPolicy,e.managedRoomExecutor);try{await nn(d.adapter,S.text,r,W,S.attachments,Z,a?{source:a.source}:void 0)}catch(i){e.processedReqIds.delete(n.id),e.sessions.delete(o),await K(e,d),e.client.sendRes({type:"res",id:h,ok:!1,error:i instanceof Error?i.message:String(i)});return}}$({level:"info",sessionId:o,wsEvent:"chat.send.start",metadata:{reqId:n.id,agentType:p,sessionMode:b,managerConfig:l??null,modelId:r,managerDefaultWorkerAgentType:x??null,managerDefaultWorkerModelId:P??null,reasoningEffort:W,...a?.source==="room_activation"?{managedRoomPromptSnapshot:a.managedRoomPromptSnapshot}:{}}});const J=(i,m)=>({...ye(oe()),sessionId:o,clientMessageId:R.id,messageId:R.id,deliveryState:i,bodyRevision:m,committedAt:new Date(R.ts).toISOString()}),ee=i=>{e.nativeFusion?.registerManagedSend({sessionId:o,agentType:p,sourceAgentType:we(p,r),canonicalMessageId:R.id,sourceSessionKey:d.agentSessionId??t??null,text:S.text,managedEchoPolicy:i?.deliveryMode==="external_owner_queue"&&!Pe?"import_following":"suppress_following",externalRunId:i?.deliveryMode==="external_owner_queue"?`codex-external-${i.queuedSubmissionId??R.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:${be()}`;e.runJournal.begin({daemonInstallationId:oe(),sessionId:o,runId:i,clientMessageId:R.id,agentType:p,sessionMode:b,workDir:v,agentSessionId:d.agentSessionId??t??null,modelId:r??null,reasoningEffort:W??null,nativeSessionVisibility:Y,providerRunId:null,source:a?.source==="room_activation"?Me==="managed_direct"?"managed_direct":"managed_room":E?"manager_worker":"personal",parentSessionId:E??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??(A?C("sha256").update(JSON.stringify({requestedPolicy:A.requestedPolicy,enforcement:A.enforcement})).digest("hex"):null)}),d.journalRunId=i},ue=()=>{if(ee(),ie)return{...ye(oe()),sessionId:o,clientMessageId:M.clientMessageId,messageId:M.messageId,deliveryState:M.deliveryState,bodyRevision:M.bodyRevision,committedAt:M.committedAt};try{Xe(e,{sessionId:o,agentType:p,sessionMode:b,managerConfig:l??null,workDir:Ee,agentSessionId:d.agentSessionId??t??null,modelId:r,managerDefaultWorkerAgentType:x??null,managerDefaultWorkerModelId:P??null});let i=0;for(const[m,g]of H.entries()){const T=se?D(o,g,"committed"):j(o,g,"committed");m===0&&(i=T)}return Ne(v,V??[]),w(e.client),J("committed",i)}catch(i){throw e.nativeFusion?.cancelManagedSend?.(R.id),i}},ce=i=>{d.journalRunId&&e.runJournal?.update(o,d.journalRunId,{deliveryMode:i?.deliveryMode==="external_owner_queue"?"external_owner_queue":"direct",externalRunId:i?.deliveryMode==="external_owner_queue"?`codex-external-${i.queuedSubmissionId??R.id}`:null})},ge=i=>{d.currentRunId||(d.nextEventSeq=0),ce(i),ee(i);const m=i?.deliveryMode==="external_owner_queue"?"queued":"executing";let g=0;for(const[T,B]of H.entries()){const q=D(o,B,m);T===0&&(g=q)}return w(e.client),J(m,g)},ne=async(i,m)=>{e.nativeFusion?.cancelManagedSend?.(R.id);const g=Se(p,i);if(console.error(`[chat.send] send failed reqId=${n.id} sessionId=${o} agentType=${c} workDir=${v} agentSessionId=${d.agentSessionId??t??""}: ${g}`),d.adapter.hasPendingAuthenticationRetry?.()===!0){m&&e.client.sendRes({type:"res",id:h,ok:!0,payload:J("queued",0)});return}e.sessions.delete(o),d.journalRunId&&(e.runJournal?.update(o,d.journalRunId,{phase:"terminal"}),e.runJournal?.remove(o,d.journalRunId),e.recoveryBarrier?.end(o),d.journalRunId=null);try{await K(e,d)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:g,runId:"",seq:0}}),!m){const B={id:`agent-error-${n.id}-${Date.now()}`,sessionId:o,role:"agent",ts:Date.now(),payload:g};j(o,B),w(e.client)}m&&(e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:g}))};if(Q){try{ue()}catch(g){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:`Durable message commit failed: ${g instanceof Error?g.message:String(g)}`});return}de(),le();let i;try{a?.source==="room_activation"&&(re(e,d,O),s());const g=S.attachments;i=await ve(d.adapter,S.text,r,W,g,Z,a?{source:a.source}:void 0),$({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}catch(g){if(d.adapter.hasPendingAuthenticationRetry?.()){let q=0;for(const[te,ae]of H.entries()){const De=D(o,ae,"queued");te===0&&(q=De)}w(e.client),await ne(g,!1),e.client.sendRes({type:"res",id:h,ok:!0,payload:J("queued",q)});return}let T=0;for(const[q,te]of H.entries()){const ae=D(o,te,"failed");q===0&&(T=ae)}w(e.client);const B=Se(p,g);await ne(g,!1),e.client.sendRes({type:"res",id:h,ok:!0,payload:{...J("failed",T),failureMessage:B}});return}const m=ge(i??void 0);e.client.sendRes({type:"res",id:h,ok:!0,payload:{...m,...S.localized?{localizedAttachments:!0}:{},...i?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:i.deliveryMode,queuedSubmissionId:i.queuedSubmissionId}:{}}}),$({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});return}let pe;try{pe=ue()}catch(i){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:`Durable message commit failed: ${i instanceof Error?i.message:String(i)}`});return}de(),le(),a?.source==="room_activation"&&(L(d.adapter,o,p,z,X,O,a.managedAgentPolicy,e.managedRoomExecutor),re(e,d,O),s()),ge(),e.client.sendRes({type:"res",id:h,ok:!0,payload:{...pe,...S.localized?{localizedAttachments:!0}:{}}}),$({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});const Te=S.attachments;ve(d.adapter,S.text,r,W,Te,Z,a?{source:a.source}:void 0).then(i=>{if(i?.deliveryMode==="external_owner_queue"){ce(i),ee(i);for(const m of H)D(o,m,"queued");w(e.client)}$({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}).catch(i=>{D(o,R,d.adapter.hasPendingAuthenticationRetry?.()?"queued":"failed"),w(e.client),ne(i,!1)})}async function xn(e,n){const{sessionId:a}=n.params,s=e.sessions.get(a);if(s){s.journalRunId&&e.runJournal?.markAborted(a,s.journalRunId),N(e,s);try{await s.adapter.stop()}catch{e.client.sendRes({type:"res",id:n.id,ok:!1,error:"agent_stop_failed"});return}tn(e,a),e.activityPublisher?.publish(a,null)}e.client.sendRes({type:"res",id:n.id,ok:!0})}export{xn as handleChatAbort,An as handleChatSend,vn as recoverJournaledRun,Xe as sendSessionUpdateEvent};
1
+ import fe from"node:os";import{createHash as C,randomUUID as be}from"node:crypto";import{createAgent as qe}from"../../agents/adapter.js";import{buildApprovalPendingPayload as Ce,buildUserMessagePayload as me}from"@shennian/wire";import{ManagerModeAdapter as $e}from"../../agents/manager.js";import{reportLog as $}from"../../log-reporter.js";import{lookupClaudeTranscriptCwd as je}from"../../native-fusion/parsers.js";import{appendMessage as j,getDaemonInstallationId as oe,getMessageDeliveryStatus as Oe,recordSession as U,upsertMessage as D}from"../store.js";import{mergeProjectedSessions as We}from"../projection.js";import{buildManagedAgentEnv as He}from"../../agents/config-status.js";import{parsePersonalAttachmentReferences as Je}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as Be,markAttachmentReferences as Ne}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as w}from"../index-publisher.js";import{writeToolDetail as Fe}from"../tool-detail-store.js";import{getPersonalSyncIdentity as ye}from"../../personal-sync/epoch.js";import{buildManagedAgentPermissionPrompt as Ie}from"../../room/managed-prompt-composer.js";import{getManagedRoomIpcRoot as ze,MANAGED_ROOM_IPC_ROOT_ENV as Ve}from"../../daemon-ipc/paths.js";import{refreshMachineAgents as Ue}from"./agents.js";function he(e,n,a){if(!e.managedAgentPolicy)throw new Error("managed_agent_policy_missing");const i=e.managedRoomPromptSnapshot;if(!i)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 o=C("sha256").update(JSON.stringify({requestedPolicy:e.managedAgentPolicy.requestedPolicy,enforcement:e.managedAgentPolicy.enforcement})).digest("hex"),u=C("sha256").update(e.managedAgentPolicy.adapterPolicy.workDir).digest("hex");if(i.runtimePermissionPolicyHash!==o||i.resolvedWorkDirHash!==u||i.enforcementLevel!==e.managedAgentPolicy.enforcement.level)throw new Error("managed_room_prompt_snapshot_mismatch")}function Le(e){const n=e.indexOf(`
2
+ `),a=n>0?Math.min(n,80):Math.min(e.length,80);return e.slice(0,a)}function Re(e,n,a){return`${e}:${C("sha256").update(`${n}\0${a??""}`).digest("hex")}`}function Ke(e,n,a={}){if(e.state==="tool-call"||e.state==="tool-result"){const i={runId:e.runId,sourceSeq:e.seq};return{state:e.state,runId:e.runId,seq:e.seq,sessionId:n,detailRef:i,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 Qe=3e4;function Ge(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 Se(e,n){return`Agent send failed: ${n instanceof Error?n.message:String(n)}`}function we(e,n){return e!=="manager"?e:n==="claude"?"claude":"codex"}function Xe(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 He(e)}function _e(e,n,a){return{}}function L(e,n,a,i,o,u,c,f){const l=a==="codex";e.configure?.({sessionId:n,externalChannel:i??null,env:{...ke(a),..._e(n,i,o),...u&&!l?{SHENNIAN_MANAGED_ROOM_CONTEXT:u,[Ve]:ze()}:{}},...c?{managedAgentPolicy:c}:{},...l?{managedRoomContextToken:u??null,managedRoomExecutor:u?f??null:null}:{}})}function re(e,n,a){const i=n.managedRoomContextToken;i&&i!==a&&e.revokeManagedRoomContextToken?.(i),n.managedRoomContextToken=a}function N(e,n){const a=n?.managedRoomContextToken;!n||!a||(n.managedRoomContextToken=null,e.revokeManagedRoomContextToken?.(a))}function Ye(e,n,a){if(e!=="claude"||!a)return n;const i=n.trim();return!!i&&i.startsWith("-")&&!i.includes("/")?je(a)??n:n}function Ze(e,n,a,i){let o=null;function u(t,r={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:Ke(t,n,r),seq:t.seq,id:`agent-evt-${t.runId}-${t.seq}`})}function c(t){t?.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null)}function f(t){if(!t.currentRunId||!t.currentRunPhase)return;const r=t.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:n,runId:t.currentRunId,seq:r,runPhase:t.currentRunPhase},seq:r,id:`agent-heartbeat-${t.currentRunId}-${r}-${Date.now()}`})}function l(t){!t||t.heartbeatTimer||(t.heartbeatTimer=setInterval(()=>{f(t)},Qe),t.heartbeatTimer.unref?.())}function y(t){const r=t?.pendingTextEvent;!t||!r||!r.text||(j(n,{id:`agent-${r.runId}-${r.seq}`,sessionId:n,role:"agent",ts:Date.now(),payload:r.text}),w(e.client),u({state:"delta",runId:r.runId,seq:r.seq,text:r.text,thinking:r.thinking||void 0}),t.pendingTextEvent=null)}i.on("agentEvent",t=>{t.errorCode==="acp_auth_required"&&Ue(e).catch(()=>{});const r=e.sessions.get(n),k=Ge(t),x=t.state==="final"||t.state==="error"||t.state==="aborted";if(r&&(r.nextEventSeq=t.seq+1,x||(r.currentRunId=t.runId),!x&&k&&(r.currentRunPhase=k,l(r)),t.agentSessionId&&(r.agentSessionId=t.agentSessionId),r.journalRunId)){const I={agentSessionId:t.agentSessionId??r.agentSessionId,providerRunId:t.runId||null,lastObservedEventSeq:t.seq,...x?{}:{phase:k==="waiting_approval"?"waiting_approval":"running"}};e.runJournal?.update(n,r.journalRunId,I)}if(e.managerRuntime?.noteAgentEvent(n,t),t.state==="delta"&&t.thinking)return;t.state!=="delta"&&$({level:"info",sessionId:n,wsEvent:`agent.${t.state}`,wsDirection:"out",metadata:{runId:t.runId,seq:t.seq,agentType:a}});let P=!1;if(t.state==="tool-call"||t.state==="tool-result"){const I=t.toolCallId?`agent-tool-${C("sha256").update(`${t.runId}\0${t.toolCallId}`).digest("hex")}`:`agent-${t.runId}-${t.seq}`;Fe({sessionId:n,toolCallId:I,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?D:j)(n,{id:I,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:I,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"})}),P=!0}else t.state==="approval-pending"?(j(n,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:n,role:"agent",ts:Date.now(),payload:Ce({...t.approval,permission:t.permission})}),P=!0,r&&U({sessionId:n,agentType:a,workDir:r.workDir,agentSessionId:r.agentSessionId,attentionId:Re("approval",t.runId,t.seq)})):(t.state==="error"||t.state==="aborted")&&t.message&&(j(n,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:n,role:"agent",ts:Date.now(),payload:t.message}),P=!0);P&&w(e.client);const _=`${n}:${t.runId}`;if(t.state==="delta"&&!t.thinking&&t.text&&e.runTextAcc.set(_,(e.runTextAcc.get(_)??"")+t.text),t.agentSessionId&&t.agentSessionId!==o&&(o=t.agentSessionId,e.nativeFusion?.noteManagedSourceSession(n,we(a,t.source),t.agentSessionId),r&&U({sessionId:n,agentType:a,workDir:r.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 I=t.text??"";if(!I)return;r?.pendingTextEvent&&r.pendingTextEvent.runId!==t.runId&&y(r),r&&!r.pendingTextEvent&&(r.pendingTextEvent={runId:t.runId,seq:t.seq,text:"",thinking:!1}),r?.pendingTextEvent&&(r.pendingTextEvent.text+=I,r.pendingTextEvent.seq=t.seq);return}let F={};if(t.state==="final"){y(r);const I=e.runTextAcc.get(_)??"";I&&(F={messageSummary:Le(I)}),e.runTextAcc.delete(_)}else t.state==="error"||t.state==="aborted"?(y(r),e.runTextAcc.delete(_)):(t.state==="tool-call"||t.state==="tool-result"||t.state==="approval-pending")&&y(r);x&&r?.currentRunId===t.runId&&(N(e,r),r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,c(r),e.chatQueue?.noteTerminal(n),U({sessionId:n,agentType:a,workDir:r.workDir,agentSessionId:r.agentSessionId,status:t.state==="final"?"completed":"failed",attentionId:Re("terminal",t.runId),lastActivityAt:new Date().toISOString()}),w(e.client),r.journalRunId&&(e.runJournal?.remove(n,r.journalRunId),e.recoveryBarrier?.end(n),r.journalRunId=null)),u(t,F)}),i.on("error",t=>{console.error(`[chat.send] adapter error sessionId=${n} agentType=${a}: ${t.message}`);const r=e.sessions.get(n);N(e,r),r?.journalRunId&&(e.runJournal?.update(n,r.journalRunId,{phase:"terminal"}),e.runJournal?.remove(n,r.journalRunId),e.recoveryBarrier?.end(n),r.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 en(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){N(e,n),n.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null),n.adapter.removeAllListeners(),await n.adapter.stop().catch(()=>{})}function ve(e,n,a,i,o,u,c){return c?e.send(n,a,i,o,u,c):u?.trim()?e.send(n,a,i,o,u):o?.length?e.send(n,a,i,o):i?e.send(n,a,i):e.send(n,a)}function nn(e,n,a,i,o,u,c){return e.prepareSend?e.prepareSend(n,a,i,o,u,c):Promise.resolve()}async function Ae(e,n,a,i,o,u,c,f,l,y="user"){e.evictIdleSessions();const t=qe(a);if(!t)throw new Error(`Unsupported agent: ${a}`);const r=i==="manager"&&a!=="manager"?new $e(t,a):t;L(r,n,a,c,f,null,l,e.managedRoomExecutor),y==="internal"?await r.start(n,o,u,{nativeSessionVisibility:y}):await r.start(n,o,u);const k={adapter:r,workDir:o,agentType:a,sessionMode:i,nativeSessionVisibility:y,agentSessionId:u??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:c??null,externalReplyTarget:f??null,externalChannelEnv:{...ke(a),..._e(n,c,f)},managedRoomContextToken:null,journalRunId:null};return e.sessions.set(n,k),Ze(e,n,a,r),k}async function vn(e,n,a=()=>{}){let i=null,o=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),i=l.managedAgentPolicy,o=l.managedRoomContextToken,u=l.systemPrompt,c={...y,recovery:"upgrade"}}else if(n.source==="manager_worker"){const l=n.parentSessionId;if(i=l?e.managerRuntime?.getManagedAgentPolicyForWorker(l,n.workDir)??null:null,!i)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker recovery requires the current parent policy"};if(C("sha256").update(JSON.stringify({requestedPolicy:i.requestedPolicy,enforcement:i.enforcement})).digest("hex")!==n.managedRuntimePolicyHash)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker policy changed after interruption"};u=Ie({requestedPolicy:i.requestedPolicy,enforcement:i.enforcement,resolvedWorkDir:i.adapterPolicy.workDir})}c??={source:"upgrade_recovery",recovery:"upgrade"};let f=e.sessions.get(n.sessionId);return f||(f=await Ae(e,n.sessionId,n.agentType,n.sessionMode,n.workDir,n.agentSessionId,null,null,i,n.nativeSessionVisibility)),o&&(re(e,f,o),L(f.adapter,n.sessionId,n.agentType,null,null,o,i,e.managedRoomExecutor)),f.journalRunId=n.runId,f.currentRunId=n.providerRunId??`recovery:${n.runId}`,f.currentRunPhase="thinking",n.agentType==="codex"&&n.deliveryMode==="external_owner_queue"&&n.agentSessionId&&n.externalRunId&&e.nativeFusion?.restoreExternalObservation({sessionId:n.sessionId,sourceSessionKey:n.agentSessionId,runId:n.externalRunId,canonicalMessageId:n.clientMessageId,createdAt:Date.parse(n.createdAt)})?{result:"reattached"}:f.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 tn(e,n){const a=e.sessions.get(n),i=a?.currentRunId;if(!a||!i)return;const o=a.nextEventSeq;e.runTextAcc.delete(`${n}:${i}`),N(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:i,seq:o},seq:o,id:`agent-evt-${i}-${o}`}),a.journalRunId&&(e.runJournal?.remove(n,a.journalRunId),e.recoveryBarrier?.end(n),a.journalRunId=null),e.chatQueue?.noteTerminal(n)}async function An(e,n,a){const i=a?n.params.managedRoomContextToken??null:null;let o=!1;try{await an(e,n,a,()=>{o=!0})}finally{i&&!o&&e.revokeManagedRoomContextToken?.(i)}}async function an(e,n,a,i){if(e.processedReqIds.has(n.id)){e.client.sendRes({type:"res",id:n.id,ok:!0});return}const{sessionId:o,text:u,agentType:c,sessionMode:f,managerConfig:l,workDir:y,agentSessionId:t,modelId:r,systemPrompt:k,managerDefaultWorkerAgentType:x,managerDefaultWorkerModelId:P,reasoningEffort:_,clientMessageId:F,sessionListProjection:I,waitForDispatch:Q,responseId:xe,managedRoomContextToken:O,suppressExternalOwnerEcho:Pe,queuedBatchItems:G,managedWorkerParentSessionId:E,managedRecoverySource:Me}=n.params,h=xe||n.id;We(I);const z=null,X="";if(a?.source==="room_activation"&&(he(a,k,O),c==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(en(e,n.id),!o||!u){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:"sessionId and text are required"});return}if(e.recoveryBarrier?.isDispatchFrozen()){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:"upgrade_in_progress"});return}e.recoveryBarrier?.isRecovering(o)&&await e.recoveryBarrier.wait(o);const p=c,b=p==="manager"||f==="manager"?"manager":"standard";b==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(o,x??null,P??null);const W=(p==="claude"||p==="codex"||p==="workbuddy"||p==="pi-coding-agent"||p.startsWith("acp:"))&&typeof _=="string"&&_.trim()?_.trim():void 0,v=e.resolvePath(Ye(p,y||fe.homedir(),t)||fe.homedir()),A=a?.managedAgentPolicy??(E?e.managerRuntime?.getManagedAgentPolicyForWorker(E,v)??null:null);if(E&&!A)throw new Error("managed_worker_permission_context_unavailable");const Y=p==="codex"&&(a?.source==="room_activation"||E)?"internal":"user",Z=E&&A?Ie({requestedPolicy:A.requestedPolicy,enforcement:A.enforcement,resolvedWorkDir:A.adapterPolicy.workDir}):k,Ee=v,V=Je(n.params.attachments);V?.length&&Be(v,V);const S={text:u,attachments:V,localized:!1};let d=e.sessions.get(o);if(d){if(d.lastActiveAt=Date.now(),d.agentType!==p||(d.sessionMode??(d.agentType==="manager"?"manager":"standard"))!==b||d.workDir!==v||(d.nativeSessionVisibility??"user")!==Y||JSON.stringify(d.externalChannel??null)!==JSON.stringify(z??null)){e.sessions.delete(o);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(o);try{await K(e,d)}catch{e.processedReqIds.delete(n.id)}d=void 0}}if(!d)try{d=await Ae(e,o,p,b,v,t,z,X,A,Y)}catch(s){const m=s instanceof Error&&s.message.startsWith("Unsupported agent:")?s.message:`Failed to start ${c}: ${s instanceof Error?s.message:String(s)}`;console.error(`[chat.send] start failed reqId=${n.id} sessionId=${o} agentType=${c} workDir=${v} agentSessionId=${t??""}: ${m}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:m,runId:"",seq:0}}),e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:m});return}const R={id:F??`user-${n.id}`,sessionId:o,role:"user",ts:Date.now(),payload:me(S.text,S.attachments)},se=Array.isArray(G)&&G.length>0?G.map(s=>({id:s.clientMessageId??s.id,sessionId:o,role:"user",ts:Date.parse(s.createdAt),payload:me(s.text,s.attachments)})):null,H=se??[R],M=Oe(o,R.id),ie=M.state==="queued";if(M.state!=="not_found"&&!ie){e.client.sendRes({type:"res",id:h,ok:!0,payload:M});return}if(Q){a?.source==="room_activation"&&L(d.adapter,o,p,z,X,O,a.managedAgentPolicy,e.managedRoomExecutor);try{await nn(d.adapter,S.text,r,W,S.attachments,Z,a?{source:a.source}:void 0)}catch(s){e.processedReqIds.delete(n.id),e.sessions.delete(o),await K(e,d),e.client.sendRes({type:"res",id:h,ok:!1,error:s instanceof Error?s.message:String(s)});return}}$({level:"info",sessionId:o,wsEvent:"chat.send.start",metadata:{reqId:n.id,agentType:p,sessionMode:b,managerConfig:l??null,modelId:r,managerDefaultWorkerAgentType:x??null,managerDefaultWorkerModelId:P??null,reasoningEffort:W,...a?.source==="room_activation"?{managedRoomPromptSnapshot:a.managedRoomPromptSnapshot}:{}}});const J=(s,m)=>({...ye(oe()),sessionId:o,clientMessageId:R.id,messageId:R.id,deliveryState:s,bodyRevision:m,committedAt:new Date(R.ts).toISOString()}),ee=s=>{e.nativeFusion?.registerManagedSend({sessionId:o,agentType:p,sourceAgentType:we(p,r),canonicalMessageId:R.id,sourceSessionKey:d.agentSessionId??t??null,text:S.text,managedEchoPolicy:s?.deliveryMode==="external_owner_queue"&&!Pe?"import_following":"suppress_following",externalRunId:s?.deliveryMode==="external_owner_queue"?`codex-external-${s.queuedSubmissionId??R.id}`:null})},de=()=>{d.currentRunId||(d.currentRunId=`dispatch:${n.id}`,d.currentRunPhase="thinking",d.nextEventSeq=0)},le=()=>{if(!e.runJournal||d.journalRunId)return;const s=`run:${be()}`;e.runJournal.begin({daemonInstallationId:oe(),sessionId:o,runId:s,clientMessageId:R.id,agentType:p,sessionMode:b,workDir:v,agentSessionId:d.agentSessionId??t??null,modelId:r??null,reasoningEffort:W??null,nativeSessionVisibility:Y,providerRunId:null,source:a?.source==="room_activation"?Me==="managed_direct"?"managed_direct":"managed_room":E?"manager_worker":"personal",parentSessionId:E??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??(A?C("sha256").update(JSON.stringify({requestedPolicy:A.requestedPolicy,enforcement:A.enforcement})).digest("hex"):null)}),d.journalRunId=s},ue=()=>{if(ee(),ie)return{...ye(oe()),sessionId:o,clientMessageId:M.clientMessageId,messageId:M.messageId,deliveryState:M.deliveryState,bodyRevision:M.bodyRevision,committedAt:M.committedAt};try{Xe(e,{sessionId:o,agentType:p,sessionMode:b,managerConfig:l??null,workDir:Ee,agentSessionId:d.agentSessionId??t??null,modelId:r,managerDefaultWorkerAgentType:x??null,managerDefaultWorkerModelId:P??null});let s=0;for(const[m,g]of H.entries()){const T=se?D(o,g,"committed"):j(o,g,"committed");m===0&&(s=T)}return Ne(v,V??[]),w(e.client),J("committed",s)}catch(s){throw e.nativeFusion?.cancelManagedSend?.(R.id),s}},ce=s=>{d.journalRunId&&e.runJournal?.update(o,d.journalRunId,{deliveryMode:s?.deliveryMode==="external_owner_queue"?"external_owner_queue":"direct",externalRunId:s?.deliveryMode==="external_owner_queue"?`codex-external-${s.queuedSubmissionId??R.id}`:null})},ge=s=>{d.currentRunId||(d.nextEventSeq=0),ce(s),ee(s);const m=s?.deliveryMode==="external_owner_queue"?"queued":"executing";let g=0;for(const[T,B]of H.entries()){const q=D(o,B,m);T===0&&(g=q)}return w(e.client),J(m,g)},ne=async(s,m)=>{e.nativeFusion?.cancelManagedSend?.(R.id);const g=Se(p,s);if(console.error(`[chat.send] send failed reqId=${n.id} sessionId=${o} agentType=${c} workDir=${v} agentSessionId=${d.agentSessionId??t??""}: ${g}`),d.adapter.hasPendingAuthenticationRetry?.()===!0){m&&e.client.sendRes({type:"res",id:h,ok:!0,payload:J("queued",0)});return}e.sessions.delete(o),d.journalRunId&&(e.runJournal?.update(o,d.journalRunId,{phase:"terminal"}),e.runJournal?.remove(o,d.journalRunId),e.recoveryBarrier?.end(o),d.journalRunId=null);try{await K(e,d)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:g,runId:"",seq:0}}),!m){const B={id:`agent-error-${n.id}-${Date.now()}`,sessionId:o,role:"agent",ts:Date.now(),payload:g};j(o,B),w(e.client)}m&&(e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:g}))};if(Q){try{ue()}catch(g){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:`Durable message commit failed: ${g instanceof Error?g.message:String(g)}`});return}de(),le();let s;try{a?.source==="room_activation"&&(re(e,d,O),i());const g=S.attachments;s=await ve(d.adapter,S.text,r,W,g,Z,a?{source:a.source}:void 0),$({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}catch(g){if(d.adapter.hasPendingAuthenticationRetry?.()){let q=0;for(const[te,ae]of H.entries()){const De=D(o,ae,"queued");te===0&&(q=De)}w(e.client),await ne(g,!1),e.client.sendRes({type:"res",id:h,ok:!0,payload:J("queued",q)});return}let T=0;for(const[q,te]of H.entries()){const ae=D(o,te,"failed");q===0&&(T=ae)}w(e.client);const B=Se(p,g);await ne(g,!1),e.client.sendRes({type:"res",id:h,ok:!0,payload:{...J("failed",T),failureMessage:B}});return}const m=ge(s??void 0);e.client.sendRes({type:"res",id:h,ok:!0,payload:{...m,...S.localized?{localizedAttachments:!0}:{},...s?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:s.deliveryMode,queuedSubmissionId:s.queuedSubmissionId}:{}}}),$({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});return}let pe;try{pe=ue()}catch(s){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:`Durable message commit failed: ${s instanceof Error?s.message:String(s)}`});return}de(),le(),a?.source==="room_activation"&&(L(d.adapter,o,p,z,X,O,a.managedAgentPolicy,e.managedRoomExecutor),re(e,d,O),i()),ge(),e.client.sendRes({type:"res",id:h,ok:!0,payload:{...pe,...S.localized?{localizedAttachments:!0}:{}}}),$({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});const Te=S.attachments;ve(d.adapter,S.text,r,W,Te,Z,a?{source:a.source}:void 0).then(s=>{if(s?.deliveryMode==="external_owner_queue"){ce(s),ee(s);for(const m of H)D(o,m,"queued");w(e.client)}$({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}).catch(s=>{D(o,R,d.adapter.hasPendingAuthenticationRetry?.()?"queued":"failed"),w(e.client),ne(s,!1)})}async function xn(e,n){const{sessionId:a,discardPending:i}=n.params,o=e.sessions.get(a);if(o){o.journalRunId&&e.runJournal?.markAborted(a,o.journalRunId),N(e,o);try{await o.adapter.stop()}catch{e.client.sendRes({type:"res",id:n.id,ok:!1,error:"agent_stop_failed"});return}tn(e,a),e.activityPublisher?.publish(a,null)}i===!0&&e.chatQueue?.purgeSession(a),e.client.sendRes({type:"res",id:n.id,ok:!0})}export{xn as handleChatAbort,An as handleChatSend,vn as recoverJournaledRun,Xe as sendSessionUpdateEvent};
@@ -0,0 +1,11 @@
1
+ import { type ReqFrame } from '@shennian/wire';
2
+ import type { CliRelayClient } from '../../relay/client.js';
3
+ import { ProjectFsHandleStore, type TrustedProjectFsAuthority } from '../project-fs-store.js';
4
+ /** Only a future Server-verifying relay adapter may implement this resolver. */
5
+ export type TrustedProjectFsRequestResolver = (req: ReqFrame) => {
6
+ authority: TrustedProjectFsAuthority;
7
+ params: unknown;
8
+ } | null;
9
+ /** Verifies that the authority was signed by Server with this machine's credential. */
10
+ export declare function createTrustedProjectFsRequestResolver(machineToken: string, now?: () => number): TrustedProjectFsRequestResolver;
11
+ export declare function handleProjectFsRequest(client: CliRelayClient, store: ProjectFsHandleStore, req: ReqFrame, resolveTrustedRequest: TrustedProjectFsRequestResolver | null): Promise<void>;
@@ -0,0 +1 @@
1
+ import c from"node:crypto";import{PROJECT_FS_AUTHORITY_PARAM as p,isProjectFsMethod as l,parseProjectFsCloseParams as m,parseProjectFsListParams as f,parseProjectFsOpenParams as h,parseProjectFsReadParams as y,parseProjectFsTrustedEnvelope as j,projectFsAuthoritySigningPayload as P}from"@shennian/wire";import{ProjectFsStoreError as d}from"../project-fs-store.js";function b(i,s=Date.now){return t=>{const o=j(t.params[p]);if(!o||o.requestId!==t.id||o.method!==t.method||o.authority.expiresAt<=s())return null;const a=c.createHmac("sha256",i).update(P(o)).digest();let e;try{e=Buffer.from(o.signature,"base64url")}catch{return null}return e.length!==a.length||!c.timingSafeEqual(e,a)?null:{authority:o.authority,params:o.params}}}async function A(i,s,t,o){const a=o?.(t)??null;if(!a||!l(t.method)){u(i,t.id,{code:"permission_denied",retryable:!1,requiredAction:"reauthenticate"});return}try{let e;switch(t.method){case"project.fs.open":{const r=h(a.params);if(!r)throw n();e=await s.open(a.authority,r);break}case"project.fs.list":{const r=f(a.params);if(!r)throw n();e=await s.list(a.authority,r);break}case"project.fs.read":{const r=y(a.params);if(!r)throw n();e=await s.read(a.authority,r);break}case"project.fs.close":{const r=m(a.params);if(!r)throw n();s.close(a.authority,r.projectRef,r.leaseId),e={closed:!0};break}}i.sendRes({type:"res",id:t.id,ok:!0,payload:e})}catch(e){const r=e instanceof d?{code:e.code,retryable:e.retryable,requiredAction:e.requiredAction}:{code:"io_error",retryable:!0,requiredAction:"retry"};u(i,t.id,r)}}function n(){return new d("handle_invalid",!1,"none")}function u(i,s,t){i.sendRes({type:"res",id:s,ok:!1,error:t.code,payload:{rejection:t}})}export{b as createTrustedProjectFsRequestResolver,A as handleProjectFsRequest};
@@ -1 +1 @@
1
- import{isAvailableAgentType as S}from"@shennian/wire";const w=/^[A-Za-z0-9_.:@/-]{1,191}$/,f=new Set(["roomId","agentIdentityId","nianSessionId","assignmentIntentId","agentType"]),R=new Set(["roomId","bindingId","agentIdentityId","previousNianSessionId","nianSessionId","assignmentIntentId","agentType"]);async function T(e,t,n){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(n.params).some(g=>!f.has(g)))throw new Error("Managed Room binding contains unsupported parameters.");const d=s(n.params.roomId,"roomId"),r=s(n.params.agentIdentityId,"agentIdentityId"),a=s(n.params.nianSessionId,"nianSessionId"),o=s(n.params.assignmentIntentId,"assignmentIntentId"),i=m(n.params.agentType);await c(e,a);const I=await t.bind({roomId:d,agentIdentityId:r,nianSessionId:a,assignmentIntentId:o,sourceSessionKey:p(a),agentType:i});e.client.sendRes({type:"res",id:n.id,ok:!0,payload:I})}async function b(e,t,n){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(n.params).some(u=>!R.has(u)))throw new Error("Managed Room restart contains unsupported parameters.");const d=s(n.params.roomId,"roomId"),r=s(n.params.bindingId,"bindingId"),a=s(n.params.agentIdentityId,"agentIdentityId"),o=s(n.params.previousNianSessionId,"previousNianSessionId"),i=s(n.params.nianSessionId,"nianSessionId"),I=s(n.params.assignmentIntentId,"assignmentIntentId"),g=m(n.params.agentType);if(e.sessions.get(o)?.currentRunId||(e.chatQueue?.getSnapshot(o).pending.length??0)>0)throw new Error("managed_agent_active_turn");await c(e,i);const y=await t.restart({roomId:d,bindingId:r,agentIdentityId:a,previousNianSessionId:o,nianSessionId:i,assignmentIntentId:I,sourceSessionKey:p(i),agentType:g});e.client.sendRes({type:"res",id:n.id,ok:!0,payload:y})}function s(e,t){if(typeof e!="string"||!w.test(e))throw new Error(`Invalid ${t}.`);return e}function m(e){if(typeof e!="string"||!S(e)||e==="manager"||e==="pi"||e==="openclaw")throw new Error("managed_agent_runtime_unsupported");return e}function p(e){return`managed:${e}`}async function c(e,t){const n=e.sessions.get(t);n&&(e.sessions.delete(t),n.managedRoomContextToken&&(e.revokeManagedRoomContextToken?.(n.managedRoomContextToken),n.managedRoomContextToken=null),n.heartbeatTimer&&clearInterval(n.heartbeatTimer),n.adapter.removeAllListeners(),await n.adapter.stop())}export{T as handleManagedRoomBind,b as handleManagedRoomRestart};
1
+ import{isAvailableAgentType as w}from"@shennian/wire";const f=/^[A-Za-z0-9_.:@/-]{1,191}$/,S=new Set(["roomId","agentIdentityId","nianSessionId","assignmentIntentId","agentType","activationMode"]),l=new Set(["roomId","bindingId","agentIdentityId","previousNianSessionId","nianSessionId","assignmentIntentId","agentType"]);async function M(n,t,e){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(e.params).some(g=>!S.has(g)))throw new Error("Managed Room binding contains unsupported parameters.");const d=a(e.params.roomId,"roomId"),r=a(e.params.agentIdentityId,"agentIdentityId"),o=a(e.params.nianSessionId,"nianSessionId"),s=a(e.params.assignmentIntentId,"assignmentIntentId"),i=c(e.params.agentType),I=R(e.params.activationMode);await p(n,o);const m=await t.bind({roomId:d,agentIdentityId:r,nianSessionId:o,assignmentIntentId:s,sourceSessionKey:y(o),agentType:i,activationMode:I});n.client.sendRes({type:"res",id:e.id,ok:!0,payload:m})}async function h(n,t,e){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(e.params).some(u=>!l.has(u)))throw new Error("Managed Room restart contains unsupported parameters.");const d=a(e.params.roomId,"roomId"),r=a(e.params.bindingId,"bindingId"),o=a(e.params.agentIdentityId,"agentIdentityId"),s=a(e.params.previousNianSessionId,"previousNianSessionId"),i=a(e.params.nianSessionId,"nianSessionId"),I=a(e.params.assignmentIntentId,"assignmentIntentId"),m=c(e.params.agentType);await p(n,s),n.chatQueue?.purgeSession(s),await p(n,i);const g=await t.restart({roomId:d,bindingId:r,agentIdentityId:o,previousNianSessionId:s,nianSessionId:i,assignmentIntentId:I,sourceSessionKey:y(i),agentType:m});n.client.sendRes({type:"res",id:e.id,ok:!0,payload:g})}function a(n,t){if(typeof n!="string"||!f.test(n))throw new Error(`Invalid ${t}.`);return n}function c(n){if(typeof n!="string"||!w(n)||n==="manager"||n==="pi"||n==="openclaw")throw new Error("managed_agent_runtime_unsupported");return n}function R(n){if(n===void 0)return"auto_follow";if(n==="auto_follow"||n==="mention_only"||n==="paused")return n;throw new Error("Invalid activationMode.")}function y(n){return`managed:${n}`}async function p(n,t){const e=n.sessions.get(t);e&&(n.sessions.delete(t),e.managedRoomContextToken&&(n.revokeManagedRoomContextToken?.(e.managedRoomContextToken),e.managedRoomContextToken=null),e.heartbeatTimer&&clearInterval(e.heartbeatTimer),e.adapter.removeAllListeners(),await e.adapter.stop())}export{M as handleManagedRoomBind,h as handleManagedRoomRestart};
@@ -15,6 +15,7 @@ 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
17
  import { ActiveRunJournal } from './run-recovery/journal.js';
18
+ import { type TrustedProjectFsRequestResolver } from './handlers/project-fs.js';
18
19
  export { resolveSessionWorkDir } from '../fs/boundary.js';
19
20
  export declare class SessionManager {
20
21
  private client;
@@ -25,6 +26,7 @@ export declare class SessionManager {
25
26
  private remoteAccessPauseController;
26
27
  private managedRoomContextRevoker;
27
28
  private managedRoomExecutor;
29
+ private trustedProjectFsRequestResolver;
28
30
  private sessions;
29
31
  /** Track processed request IDs to deduplicate replayed offline messages */
30
32
  private processedReqIds;
@@ -45,7 +47,8 @@ export declare class SessionManager {
45
47
  private recoveryCoordinator;
46
48
  private recoveryPass;
47
49
  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);
50
+ private projectFsHandleStore;
51
+ 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, trustedProjectFsRequestResolver?: TrustedProjectFsRequestResolver | null);
49
52
  private getRuntime;
50
53
  resumePendingAcpAuthentication(agentType: string): Promise<boolean>;
51
54
  getUpgradeIdleState(): UpgradeIdleState;
@@ -55,6 +58,8 @@ export declare class SessionManager {
55
58
  cancelUpgradePreparation(): void;
56
59
  recoverInterruptedRuns(): Promise<void>;
57
60
  getPendingRecoveryCount(): number;
61
+ /** Relay loss revokes every short-lived Project file capability. */
62
+ revokeProjectFsHandles(): void;
58
63
  private commitRecoveryTerminal;
59
64
  setManagedRoomTurnPreparer(prepare: (contexts: ManagedRoomQueueContext[]) => Promise<PreparedManagedRoomTurn>): void;
60
65
  setManagedAgentDirectTurnPreparer(prepare: (sessionId: string) => Promise<PreparedManagedRoomTurn | null>): void;
@@ -1,3 +1,3 @@
1
- import{SESSION_SYNC_PROTOCOL_VERSION as w,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 z}from"../agents/acp/watcher.js";import{handleAgentCapabilitiesList as B}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as N,handleAgentConfigGet as W,handleAgentConfigTest as J,handleAgentConfigUpsert as j}from"./handlers/agent-config.js";import{handleChatAbort as V,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 I}from"../manager/runtime.js";import{ChatQueueManager as fe,TerminalQueuedChatDispatchError as ye}from"./queue.js";import{createAuthorizedFsRoot as ve,createAuthorizedFsRootAsync as Re,resolveAuthorizedPath as we,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 Ie}from"../agents/custom.js";import{handleManagedRoomBind as ke,handleManagedRoomRestart as Ae}from"./handlers/room-managed.js";import{getDaemonInstallationId as y,listSessionRecords as v,purgeCanonicalSession as Te,purgeSessionRecord as Ce,recordSession as k,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 R}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 ze}from"./run-recovery/journal.js";import{RunRecoveryBarrier as Be}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 Bs}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 Be;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 ze(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 z(async()=>{D(),await Q(this.getRuntime())}),this.nativeFusion?.setExternalTerminalHandler?.(n=>{this.handleExternalTerminal(n)}),this.nativeCleanupOutbox.start(),I(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
1
+ import{SESSION_SYNC_PROTOCOL_VERSION as b,SESSION_SYNC_DATA_EPOCH as S,isAvailableAgentType as k}from"@shennian/wire";import m 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 F,handleUpgradeStatus as O}from"../commands/upgrade.js";import{handleAgentsRefresh as j,handleModelsRefresh as $,refreshMachineAgents as U}from"./handlers/agents.js";import{AcpDiscoveryWatcher as Q}from"../agents/acp/watcher.js";import{handleAgentCapabilitiesList as z}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as B,handleAgentConfigGet as N,handleAgentConfigTest as W,handleAgentConfigUpsert as H}from"./handlers/agent-config.js";import{handleChatAbort as J,handleChatSend as v,recoverJournaledRun as V}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 I}from"../manager/runtime.js";import{ChatQueueManager as fe,TerminalQueuedChatDispatchError as ve}from"./queue.js";import{createAuthorizedFsRoot as ye,createAuthorizedFsRootAsync as Re,resolveAuthorizedPath as we,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 R,purgeCanonicalSession as Te,purgeSessionRecord as Pe,recordSession as A,upsertMessage as T}from"./store.js";import{requestSessionIndexPublish as P}from"./index-publisher.js";import{reportUpgradeAggregateMetric as Ce}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 Fe,clearDeletionSyncFailure as Oe,DeletionSyncRetryWaitError as je,DeletionSyncTerminalError as $e,recordDeletionSyncFailure as w}from"./deletion-sync-state.js";import{NativeSessionCleanupOutbox as Ue}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as p}from"../personal-sync/epoch.js";import{ActiveRunJournal as Qe}from"./run-recovery/journal.js";import{RunRecoveryBarrier as ze}from"./run-recovery/barrier.js";import{RunRecoveryCoordinator as Be}from"./run-recovery/coordinator.js";import{ProjectFsHandleStore as Ne}from"./project-fs-store.js";import{handleProjectFsRequest as We}from"./handlers/project-fs.js";const He=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||!k(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 Hs}from"../fs/boundary.js";class Bs{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;remoteAccessPauseController;managedRoomContextRevoker;managedRoomExecutor;trustedProjectFsRequestResolver;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;projectFsHandleStore=new Ne;constructor(e,t=null,s,a=null,i=null,d=null,r=null,o=null,u=null,g=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.trustedProjectFsRequestResolver=g,this.runJournal=u??new Qe(y());for(const n of this.runJournal.loadPendingReconciliation().records)this.recoveryBarrier.begin(n.sessionId);this.recoveryCoordinator=new Be({journal:this.runJournal,barrier:this.recoveryBarrier,recover:(n,c)=>V(this.getRuntime(),n,c),commitTerminal:(n,c)=>this.commitRecoveryTerminal(n,c),isCanonicalTerminal:n=>{const c=R().find(f=>f.sessionId===n.sessionId),h=`terminal:${m.createHash("sha256").update(`${n.runId}\0`).digest("hex")}`;return c?.lastAttentionId===h},onOutcome:n=>Ce({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 Ue({logger:console.info}),this.acpDiscoveryWatcher=new Q(async()=>{D(),await U(this.getRuntime())}),this.nativeFusion?.setExternalTerminalHandler?.(n=>{this.handleExternalTerminal(n)}),this.nativeCleanupOutbox.start(),I(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([...R().map(c=>c.workDir),...Array.from(this.pendingTransfers.values()).map(c=>c.rootPath)]);for(const c of n)try{xe(this.resolvePath(c))}catch(h){console.error("[attachment.cleanup] failed",h instanceof Error?h.message:String(h))}},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}revokeProjectFsHandles(){this.projectFsHandleStore.revokeAll()}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
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})}),k({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)?"transport_unavailable":/invalid (?:deletion sync|session deletion)|deletion revision gap/i.test(t)?"invalid_deletion_sync_response":"deletion_sync_failed";throw console.warn("[session.deletions] retrying failed synchronization",{code:s}),R({code:s,retryable:!0}),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:w,dataEpoch:b,daemonInstallationId:e,afterRevision:t}});if(!s.ok){const r=s.rejection;throw r?(R({code:r.code,retryable:r.retryable&&r.requiredAction==="retry"}),new Error(r.code)):(R({code:"unstructured_rejection",retryable:!0}),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:w,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){const i=t.find(a.agentType,a.sourceSessionKey);i&&i.sessionId!==e.sessionId||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&&(t.journalRunId&&(this.recoveryCoordinator.completeFromProvider(e,t.journalRunId),t.journalRunId=null),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??{}))Ie(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})}k({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 V(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 ke(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 j(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 B(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 we(e,ve(t))}async resolveAuthorizedPathAsync(e,t){return be(e,await Re(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(),I(null)}revokeManagedRoomToken(e){const t=e.managedRoomContextToken;e.managedRoomContextToken=null,t&&this.managedRoomContextRevoker?.(t)}}export{Us as SessionManager,Bs as resolveSessionWorkDir};
3
+ The task could not be recovered after the Shennian upgrade (${t.reason}). The session is preserved; retry or send a new message.`;T(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})}),A({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()}),P(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=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)}managedChatContext(e){return{source:"room_activation",managedAgentPolicy:e.managedAgentPolicy,managedRoomPromptSnapshot:e.managedRoomPromptSnapshot}}async dispatchManagedChat(e,t){await v(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 v({...this.getRuntime(),client:a},e,t?this.managedChatContext(t):void 0),s.value?.payload?.deliveryState==="failed")throw new ve(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(){Fe();try{await this.synchronizeSessionDeletionsOnce(),Oe()}catch(e){if(e instanceof je||e instanceof $e)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)?"transport_unavailable":/invalid (?:deletion sync|session deletion)|deletion revision gap/i.test(t)?"invalid_deletion_sync_response":"deletion_sync_failed";throw console.warn("[session.deletions] retrying failed synchronization",{code:s}),w({code:s,retryable:!0}),e}}async synchronizeSessionDeletionsOnce(){const e=y(),t=_e(),s=await this.client.sendReq({type:"req",id:`session-deletions-sync-${m.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:b,dataEpoch:S,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:!0}),new Error(s.error??"unstructured_rejection"))}const a=s.payload;p(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-${m.randomUUID()}`,method:"session.deletions.ack",params:{protocolVersion:b,dataEpoch:S,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){const i=t.find(a.agentType,a.sourceSessionKey);i&&i.sessionId!==e.sessionId||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),Pe(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&&(t.journalRunId&&(this.recoveryCoordinator.completeFromProvider(e,t.journalRunId),t.journalRunId=null),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:")||!k(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-${m.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??""),u=String(e.params.sessionId??"");if(!o||!u)throw new Error("ACP and Shennian session ids are required");const g=await a.loadExternalSession(o),n=Date.now();for(const[c,h]of g.entries()){const f=`acp-import-${m.createHash("sha256").update(`${t}\0${o}\0${h.sourceId}`).digest("hex")}`;T(u,{id:f,sessionId:u,role:h.role,ts:n+c,payload:h.payload})}A({sessionId:u,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"}),P(this.client),this.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:u,agentType:t,agentSessionId:o,workDir:i,status:"active"}}}),r={sessionId:u,agentSessionId:o,importedMessageCount:g.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":p(e.params),this.recoveryBarrier.isDispatchFrozen()||this.recoveryBarrier.isRecovering(String(e.params.sessionId??""))?await this.chatQueue.handleEnqueue({...e,method:"chat.enqueue"}):await v(s,e);break;case"chat.enqueue":p(e.params),await this.chatQueue.handleEnqueue(e);break;case"chat.queue.get":p(e.params),await this.chatQueue.handleGet(e);break;case"chat.queue.edit":p(e.params),await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":p(e.params),await this.chatQueue.handleDelete(e);break;case"chat.abort":p(e.params),await J(s,e);break;case"session.refresh":p(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"project.fs.open":case"project.fs.list":case"project.fs.read":case"project.fs.close":await We(this.client,this.projectFsHandleStore,e,this.trustedProjectFsRequestResolver);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 F(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 O(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 j(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 $(s,e);break;case"agent.config.get":await N(s,e);break;case"agent.config.upsert":await H(s,e);break;case"agent.config.clear":await B(s,e);break;case"agent.config.test":await W(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<He)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 we(e,ye(t))}async resolveAuthorizedPathAsync(e,t){return be(e,await Re(t))}async cleanup(){this.projectFsHandleStore.revokeAll(),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(),I(null)}revokeManagedRoomToken(e){const t=e.managedRoomContextToken;e.managedRoomContextToken=null,t&&this.managedRoomContextRevoker?.(t)}}export{Bs as SessionManager,Hs as resolveSessionWorkDir};
@@ -0,0 +1,43 @@
1
+ import { type ProjectFsAuthority, type ProjectFsErrorCode, type ProjectFsListParams, type ProjectFsListResult, type ProjectFsOpenParams, type ProjectFsOpenResult, type ProjectFsReadParams, type ProjectFsReadResult, type ProjectFsRequiredAction } from '@shennian/wire';
2
+ export type TrustedProjectFsAuthority = ProjectFsAuthority;
3
+ export declare class ProjectFsStoreError extends Error {
4
+ readonly code: ProjectFsErrorCode;
5
+ readonly retryable: boolean;
6
+ readonly requiredAction: ProjectFsRequiredAction;
7
+ constructor(code: ProjectFsErrorCode, retryable: boolean, requiredAction: ProjectFsRequiredAction);
8
+ }
9
+ export type ProjectFsHandleStoreOptions = {
10
+ now?: () => number;
11
+ randomToken?: () => string;
12
+ protectedRoots?: () => readonly string[];
13
+ };
14
+ /**
15
+ * Keeps every native path daemon-side. Callers must obtain authority through a
16
+ * server-verifying resolver; no method accepts a renderer-supplied path.
17
+ */
18
+ export declare class ProjectFsHandleStore {
19
+ private readonly leases;
20
+ private readonly now;
21
+ private readonly randomToken;
22
+ private readonly protectedRoots;
23
+ constructor(options?: ProjectFsHandleStoreOptions);
24
+ open(authority: TrustedProjectFsAuthority, params: ProjectFsOpenParams): Promise<ProjectFsOpenResult>;
25
+ list(authority: TrustedProjectFsAuthority, params: ProjectFsListParams): Promise<ProjectFsListResult>;
26
+ read(authority: TrustedProjectFsAuthority, params: ProjectFsReadParams): Promise<ProjectFsReadResult>;
27
+ close(authority: TrustedProjectFsAuthority, projectRef: string, leaseId: string): void;
28
+ revokeScope(scopeId: string): void;
29
+ revokeAll(): void;
30
+ private listPage;
31
+ private readDirectorySnapshot;
32
+ private verifyRecord;
33
+ private assertContained;
34
+ private assertNotProtected;
35
+ private requireLease;
36
+ private requireHandle;
37
+ private assertAuthority;
38
+ private assertLeaseAuthority;
39
+ private revokeStale;
40
+ private renew;
41
+ private scopeHash;
42
+ private issueUniqueToken;
43
+ }
@@ -0,0 +1 @@
1
+ import j from"node:crypto";import y from"node:fs";import h from"node:path";import{PROJECT_FS_LIMITS as S}from"@shennian/wire";import{resolveProtectedShennianDirectories as R}from"../security/managed-agent-credential-paths.js";const I=10*6e4,E=30*6e4,L=8,b=2e3,M=1e4,H=25*1024*1024;class P extends Error{code;retryable;requiredAction;constructor(e,t,s){super(e),this.code=e,this.retryable=t,this.requiredAction=s,this.name="ProjectFsStoreError"}}class U{leases=new Map;now;randomToken;protectedRoots;constructor(e={}){this.now=e.now??Date.now,this.randomToken=e.randomToken??(()=>j.randomBytes(32).toString("base64url")),this.protectedRoots=e.protectedRoots??R}async open(e,t){this.assertAuthority(e,t.projectRef),this.revokeStale(e);const s=this.now(),r=this.scopeHash(e);if(Array.from(this.leases.values()).filter(_=>_.authority.accountScope===e.accountScope&&_.authority.machineId===e.machineId&&_.expiresAt>s).length>=L)throw n("rate_limited",!0,"retry");const l=h.resolve(e.rootPath),d=await g(l,"project_not_found");if(d.isSymbolicLink()||!d.isDirectory())throw n("unsupported_entry",!1,"refresh_project");const i=await k(l,"project_not_found");if(i!==l)throw n("project_scope_changed",!1,"refresh_project");this.assertNotProtected(i);const u=await g(i,"project_not_found");if(!m(d,u))throw n("source_changed",!0,"refresh_project");const c=this.issueUniqueToken(this.leases),f=this.issueUniqueToken(),p=Math.min(e.expiresAt,s+I),A={handle:f,leaseId:c,targetPath:i,parentHandle:null,kind:"directory",identity:x(u)},w={leaseId:c,scopeHash:r,authority:{...e,rootPath:i},rootPath:i,rootHandle:f,createdAt:s,expiresAt:p,handles:new Map([[f,A]])};this.leases.set(c,w);try{const _=await this.listPage(w,A,0,t.limit);return{leaseId:c,root:{handle:f,name:T(i),kind:"directory"},page:_,expiresAt:w.expiresAt}}catch(_){throw this.leases.delete(c),_}}async list(e,t){const s=this.requireLease(e,t.projectRef,t.leaseId),r=this.requireHandle(s,t.directoryHandle,"directory"),a=await this.listPage(s,r,t.cursor??0,t.limit);return this.renew(s),{...a,leaseId:s.leaseId,expiresAt:s.expiresAt}}async read(e,t){const s=this.requireLease(e,t.projectRef,t.leaseId),r=this.requireHandle(s,t.fileHandle,"file");if(t.limitBytes>S.maxReadBytes)throw n("read_too_large",!1,"none");if(t.offset!==(r.nextReadOffset??0))throw n("source_changed",!0,"retry");const a=await this.verifyRecord(s,r);if(a.size>H||t.offset>a.size)throw n("read_too_large",!1,"none");let l=null;try{const d=typeof y.constants.O_NOFOLLOW=="number"?y.constants.O_NOFOLLOW:0;l=await y.promises.open(r.targetPath,y.constants.O_RDONLY|d);const i=await l.stat();if(!i.isFile()||i.nlink!==1||!m(a,i))throw n("source_changed",!0,"retry");const u=Math.min(t.limitBytes,i.size-t.offset),c=Buffer.alloc(u),f=await l.read(c,0,u,t.offset),p=await l.stat();if(!m(i,p))throw n("source_changed",!0,"retry");const A=await g(r.targetPath,"source_changed");if(!m(p,A))throw n("source_changed",!0,"retry");await this.assertContained(s.rootPath,r.targetPath);const w=c.subarray(0,f.bytesRead),_=t.offset+f.bytesRead<p.size?t.offset+f.bytesRead:null;return r.nextReadOffset=_??p.size,this.renew(s),{leaseId:s.leaseId,fileHandle:r.handle,offset:t.offset,bytesRead:f.bytesRead,nextOffset:_,totalSize:p.size,dataBase64:w.toString("base64"),mimeType:N(r.targetPath),expiresAt:s.expiresAt}}catch(d){throw d instanceof P?d:n("io_error",!0,"retry")}finally{await l?.close().catch(()=>{})}}close(e,t,s){const r=this.leases.get(s);r&&(this.assertLeaseAuthority(r,e,t),this.leases.delete(s))}revokeScope(e){for(const[t,s]of this.leases)s.authority.scopeId===e&&this.leases.delete(t)}revokeAll(){this.leases.clear()}async listPage(e,t,s,r){await this.verifyRecord(e,t);const a=await this.readDirectorySnapshot(e,t.targetPath);if(t.directorySnapshot){if(!z(t.directorySnapshot,a)||s!==t.nextCursor)throw n("source_changed",!0,"retry")}else{if(s!==0)throw n("source_changed",!0,"retry");t.directorySnapshot=a}const l=r??S.defaultDirectoryPageItems,d=a.slice(s,s+l),i=[];for(const c of d){if(e.handles.size>=b)throw n("rate_limited",!0,"refresh_project");const f=h.join(t.targetPath,c.name),p=await g(f,"source_changed");if(!m(c.identity,p))throw n("source_changed",!0,"retry");await this.assertContained(e.rootPath,f);const A=await g(f,"source_changed");if(!m(p,A))throw n("source_changed",!0,"retry");const w=this.issueUniqueToken(e.handles);e.handles.set(w,{handle:w,leaseId:e.leaseId,targetPath:f,parentHandle:t.handle,kind:c.kind,identity:c.identity}),i.push({handle:w,name:c.name,kind:c.kind,...c.kind==="file"?{size:c.identity.size}:{},modifiedAt:c.identity.mtimeMs})}const u=s+i.length<a.length?s+i.length:null;return t.nextCursor=u,{directoryHandle:t.handle,cursor:s,nextCursor:u,entries:i}}async readDirectorySnapshot(e,t){let s;try{s=await y.promises.readdir(t)}catch{throw n("io_error",!0,"retry")}if(s.length>M)throw n("rate_limited",!0,"none");s.sort(q);const r=[];for(const a of s){if(!v(a)||O(a))continue;const l=h.join(t,a),d=await g(l,"source_changed");if(d.isSymbolicLink())continue;const i=d.isDirectory()?"directory":d.isFile()&&d.nlink===1?"file":null;if(!i)continue;await this.assertContained(e.rootPath,l);const u=await g(l,"source_changed");if(!m(d,u))throw n("source_changed",!0,"retry");r.push({name:a,kind:i,identity:x(d)})}return r}async verifyRecord(e,t){const s=await g(t.targetPath,"source_changed");if(s.isSymbolicLink()||!m(t.identity,s))throw n("source_changed",!0,"retry");if(t.kind==="file"&&(!s.isFile()||s.nlink!==1)||t.kind==="directory"&&!s.isDirectory())throw n("unsupported_entry",!1,"none");return await this.assertContained(e.rootPath,t.targetPath),s}async assertContained(e,t){const s=await k(t,"source_changed"),r=h.relative(e,s);if(r===".."||r.startsWith(`..${h.sep}`)||h.isAbsolute(r))throw n("permission_denied",!1,"refresh_project");this.assertNotProtected(s)}assertNotProtected(e){let t;try{t=this.protectedRoots()}catch{throw n("sensitive_path_denied",!1,"none")}if(t.some(s=>D(e,h.resolve(s))))throw n("sensitive_path_denied",!1,"none")}requireLease(e,t,s){this.assertAuthority(e,t),this.revokeStale(e);const r=this.leases.get(s);if(!r)throw n("handle_invalid",!1,"refresh_project");if(r.expiresAt<=this.now())throw this.leases.delete(s),n("handle_expired",!1,"refresh_project");return this.assertLeaseAuthority(r,e,t),r.expiresAt=Math.min(r.expiresAt,e.expiresAt),r}requireHandle(e,t,s){const r=e.handles.get(t);if(!r||r.kind!==s)throw n("handle_invalid",!1,"refresh_project");return r}assertAuthority(e,t){if(!e||e.projectRef!==t||!e.scopeId||!e.accountScope||!e.machineId||!e.projectId||!e.rootDigest||!e.daemonInstallationId||!e.generation||!h.isAbsolute(e.rootPath)||!Number.isSafeInteger(e.authorizationEpoch)||e.authorizationEpoch<0||!Number.isSafeInteger(e.expiresAt)||e.expiresAt<=this.now())throw n("permission_denied",!1,"reauthenticate")}assertLeaseAuthority(e,t,s){if(e.scopeHash!==this.scopeHash(t)||e.authority.projectRef!==s)throw n("project_scope_changed",!1,"refresh_project")}revokeStale(e){const t=this.now();for(const[s,r]of this.leases){const a=r.authority.accountScope===e.accountScope&&r.authority.machineId===e.machineId&&r.authority.projectId===e.projectId;(r.expiresAt<=t||a&&r.scopeHash!==this.scopeHash(e))&&this.leases.delete(s)}}renew(e){e.expiresAt=Math.min(e.authority.expiresAt,e.createdAt+E,this.now()+I)}scopeHash(e){return j.createHash("sha256").update([e.scopeId,e.accountScope,e.machineId,e.projectId,h.resolve(e.rootPath),e.rootDigest,String(e.authorizationEpoch),e.daemonInstallationId,e.generation].join("\0")).digest("base64url")}issueUniqueToken(e){for(let t=0;t<8;t+=1){const s=this.randomToken();if(/^[A-Za-z0-9_-]{32,512}$/.test(s)&&!this.leases.has(s)&&!e?.has(s))return s}throw n("io_error",!0,"retry")}}function n(o,e,t){return new P(o,e,t)}async function g(o,e){try{return await y.promises.lstat(o)}catch{throw n(e,e==="source_changed",e==="project_not_found"?"refresh_project":"retry")}}async function k(o,e){try{return await y.promises.realpath(o)}catch{throw n(e,e==="source_changed",e==="project_not_found"?"refresh_project":"retry")}}function x(o){return{dev:o.dev,ino:o.ino,mode:o.mode,size:o.size,mtimeMs:o.mtimeMs,ctimeMs:o.ctimeMs}}function m(o,e){return o.dev===e.dev&&o.ino===e.ino&&o.mode===e.mode&&o.size===e.size&&o.mtimeMs===e.mtimeMs&&o.ctimeMs===e.ctimeMs}function z(o,e){return o.length===e.length&&o.every((t,s)=>{const r=e[s];return r!==void 0&&t.name===r.name&&t.kind===r.kind&&m(t.identity,r.identity)})}function v(o){return o.length>0&&o.length<=S.maxNameLength&&o!=="."&&o!==".."&&!/[\u0000-\u001f\u007f\\/]/.test(o)}function O(o){const e=o.toLowerCase();return e.startsWith(".")||e.endsWith(".key")||e.endsWith(".pem")||e==="credentials"||e==="id_rsa"||e==="id_ed25519"}function q(o,e){return o<e?-1:o>e?1:0}function D(o,e){const t=r=>process.platform==="win32"?h.resolve(r).toLowerCase():h.resolve(r),s=h.relative(t(e),t(o));return s===""||s!==".."&&!s.startsWith(`..${h.sep}`)&&!h.isAbsolute(s)}function T(o){const e=h.basename(o);return v(e)?e:"Project"}function N(o){const e=h.extname(o).toLowerCase();return{".txt":"text/plain",".md":"text/markdown",".json":"application/json",".csv":"text/csv",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".pdf":"application/pdf"}[e]??"application/octet-stream"}export{U as ProjectFsHandleStore,P as ProjectFsStoreError};
@@ -1,5 +1,5 @@
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]
1
+ import h from"node:fs";import{randomUUID as C}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 T}from"./attachment-reference.js";import{assertFinishedAttachmentReferences as A,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 _}from"../personal-sync/identity.js";import{appendMessage as $,getMessageDeliveryStatus as k,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}-${C()}`}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}-${C()}`;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(_()),id:s.queueMessageId||s.clientMessageId||`queue-${C()}`,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:T(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(_()),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(_()),...d});u.origin="room",u.managedRoom=a.context;const p=n.sessions[d.sessionId]??[],c=O(p,u),l=u.clientMessageId??u.id,I=k(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:z(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=T(o.attachments);d?.length&&A(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=k(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=O(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,...k(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=T(n.attachments),d=t.resolvePath(r[o].workDir);a?.length&&A(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=k(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,failureClass:U(i.message,"terminal_dispatch_failed")},"error"),t=r>0}else this.logLifecycle("dispatch.transient-failure",e,{failureClass: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?z(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(_()),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 z(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 O(s,e){const t=b(e);if(t)return s.find(i=>b(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
3
  ${s.map(e=>ie(e.text)).filter(Boolean).join(`
4
4
 
5
5
  `)}`:`[\u8FDE\u7EED\u7528\u6237\u6D88\u606F]
@@ -8,5 +8,5 @@ ${e.text.trim()}`).join(`
8
8
 
9
9
  `)}`}function ie(s){const e=s.trim(),t=e.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);return t?`<${t[1]}>
10
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};
11
+ `)?e.slice(6):e}function J(s){const e=b(s);return e?`id:${e}`:["body",re(s.text),...(s.attachments??[]).map(t=>t.path||`${t.name}:${t.mimeType}`)].join(`
12
+ `)}function b(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(),n=t.match(/\b(?:managed_(?:agent|room)|agent_validation|acp|native)_[a-z0-9_]+\b/)?.[0];return n||(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};
@@ -12,6 +12,7 @@ export * from './native-session.ts';
12
12
  export * from './tool-detail.ts';
13
13
  export * from './voice-chief.ts';
14
14
  export * from './project.ts';
15
+ export * from './project-fs.ts';
15
16
  export * from './room.ts';
16
17
  export * from './agent-capabilities.ts';
17
18
  export * from './agent-workspace.ts';
@@ -12,6 +12,7 @@ export * from "./native-session.js";
12
12
  export * from "./tool-detail.js";
13
13
  export * from "./voice-chief.js";
14
14
  export * from "./project.js";
15
+ export * from "./project-fs.js";
15
16
  export * from "./room.js";
16
17
  export * from "./agent-capabilities.js";
17
18
  export * from "./agent-workspace.js";
@@ -1 +1,3 @@
1
- export type ReqMethod = 'chat.send' | 'chat.enqueue' | 'chat.queue.get' | 'chat.queue.edit' | 'chat.queue.delete' | 'chat.abort' | 'session.resume' | 'session.history' | 'session.history.open' | 'session.history.sync' | 'session.history.page' | 'session.history.close' | 'session.body.replica.subscribe' | 'session.body.replica.unsubscribe' | 'session.index.update' | 'session.deletions.sync' | 'session.deletions.ack' | 'session.message.status' | 'voice.session.body.window' | 'session.refresh' | 'session.tool.detail' | 'session.title.set' | 'session.title.generate' | 'fs.ls' | 'fs.read' | 'fs.write' | 'fs.export.markdown-pdf' | 'fs.export.markdown-pdf.setup' | 'fs.archive.zip' | 'fs.transfer.start' | 'fs.transfer.chunk' | 'fs.transfer.finish' | 'fs.transfer.abort' | 'fs.rename' | 'skill.list' | 'skill.install' | 'skill.doctor' | 'skill.setup' | 'skill.use' | 'region.probe' | 'region.switch' | 'upgrade.start' | 'upgrade.status' | 'upgrade.set-policy' | 'machine.remote-access.pause' | 'agents.refresh' | 'models.refresh' | 'agent.config.get' | 'agent.config.upsert' | 'agent.config.clear' | 'agent.config.test' | 'agent.workspace.validate' | 'agent.capabilities.list' | 'agent.permission.respond' | 'agent.auth.start' | 'agent.auth.logout' | 'agent.session-options.get' | 'agent.external-sessions.list' | 'agent.external-sessions.import' | 'agent.external-sessions.close' | 'agent.external-sessions.delete' | 'manager.channel.get' | 'manager.channel.upsert' | 'session.wechat-rpa.channel.get' | 'session.wechat-rpa.channel.upsert' | 'session.wechat-rpa.channel.sync' | 'session.wechat-rpa.outbound.cancel' | 'manager.wechat-rpa.channel.get' | 'manager.wechat-rpa.channel.upsert' | 'manager.wechat-rpa.channel.sync' | 'manager.wechat-rpa.outbound.cancel' | 'wechat-rpa.helper.runtime' | 'wecom.send' | 'external.send' | 'room.sync' | 'room.send' | 'room.bind' | 'room.agent.bind-managed' | 'room.agent.restart-managed' | 'room.unbind';
1
+ export type ReqMethod = 'chat.send' | 'chat.enqueue' | 'chat.queue.get' | 'chat.queue.edit' | 'chat.queue.delete' | 'chat.abort' | 'session.resume' | 'session.history' | 'session.history.open' | 'session.history.sync' | 'session.history.page' | 'session.history.close' | 'session.body.replica.subscribe' | 'session.body.replica.unsubscribe' | 'session.index.update' | 'session.deletions.sync' | 'session.deletions.ack' | 'session.message.status' | 'voice.session.body.window' | 'session.refresh' | 'session.tool.detail' | 'session.title.set' | 'session.title.generate' | 'fs.ls' | 'fs.read' | 'fs.write' | 'fs.export.markdown-pdf' | 'fs.export.markdown-pdf.setup' | 'fs.archive.zip' | 'fs.transfer.start' | 'fs.transfer.chunk' | 'fs.transfer.finish' | 'fs.transfer.abort' | 'fs.rename' | 'project.fs.open' | 'project.fs.list' | 'project.fs.read' | 'project.fs.close'
2
+ /** Server-originated per-connection challenge; never accepted from an App client. */
3
+ | 'project.fs.negotiate' | 'skill.list' | 'skill.install' | 'skill.doctor' | 'skill.setup' | 'skill.use' | 'region.probe' | 'region.switch' | 'upgrade.start' | 'upgrade.status' | 'upgrade.set-policy' | 'machine.remote-access.pause' | 'agents.refresh' | 'models.refresh' | 'agent.config.get' | 'agent.config.upsert' | 'agent.config.clear' | 'agent.config.test' | 'agent.workspace.validate' | 'agent.capabilities.list' | 'agent.permission.respond' | 'agent.auth.start' | 'agent.auth.logout' | 'agent.session-options.get' | 'agent.external-sessions.list' | 'agent.external-sessions.import' | 'agent.external-sessions.close' | 'agent.external-sessions.delete' | 'manager.channel.get' | 'manager.channel.upsert' | 'session.wechat-rpa.channel.get' | 'session.wechat-rpa.channel.upsert' | 'session.wechat-rpa.channel.sync' | 'session.wechat-rpa.outbound.cancel' | 'manager.wechat-rpa.channel.get' | 'manager.wechat-rpa.channel.upsert' | 'manager.wechat-rpa.channel.sync' | 'manager.wechat-rpa.outbound.cancel' | 'wechat-rpa.helper.runtime' | 'wecom.send' | 'external.send' | 'room.sync' | 'room.send' | 'room.bind' | 'room.agent.bind-managed' | 'room.agent.restart-managed' | 'room.unbind';
@@ -1 +1,3 @@
1
+ // @arch docs/features/desktop-project-files-opaque-protocol.md
2
+ // @test src/__tests__/project-fs.test.ts
1
3
  export {};