shennian 0.4.71 → 0.4.73

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 (35) hide show
  1. package/dist/publish-build-manifest.json +47 -37
  2. package/dist/src/agents/acp/adapter.js +1 -1
  3. package/dist/src/agents/acp/client-services.d.ts +4 -0
  4. package/dist/src/agents/acp/client-services.js +3 -3
  5. package/dist/src/agents/acp/process-client.d.ts +2 -0
  6. package/dist/src/agents/adapter.d.ts +1 -1
  7. package/dist/src/agents/claude.d.ts +1 -0
  8. package/dist/src/agents/claude.js +5 -5
  9. package/dist/src/agents/event-terminal-gate.d.ts +16 -0
  10. package/dist/src/agents/event-terminal-gate.js +1 -0
  11. package/dist/src/agents/model-registry/discovery.js +3 -3
  12. package/dist/src/agents/model-registry/parsers.js +4 -4
  13. package/dist/src/agents/model-registry/service.d.ts +6 -2
  14. package/dist/src/agents/model-registry/service.js +1 -1
  15. package/dist/src/agents/model-registry/types.d.ts +9 -1
  16. package/dist/src/agents/model-registry/types.js +1 -1
  17. package/dist/src/commands/agent-readiness.js +2 -2
  18. package/dist/src/commands/manager.js +1 -1
  19. package/dist/src/commands/worker.d.ts +11 -0
  20. package/dist/src/commands/worker.js +2 -0
  21. package/dist/src/daemon-ipc/agent-readiness.js +1 -1
  22. package/dist/src/index.js +3 -3
  23. package/dist/src/manager/prompt.d.ts +1 -1
  24. package/dist/src/manager/prompt.js +6 -6
  25. package/dist/src/manager/registry.d.ts +49 -2
  26. package/dist/src/manager/registry.js +1 -1
  27. package/dist/src/manager/runtime.d.ts +2 -2
  28. package/dist/src/manager/runtime.js +5 -5
  29. package/dist/src/manager/worker-ipc.d.ts +7 -0
  30. package/dist/src/manager/worker-ipc.js +1 -1
  31. package/dist/src/session/handlers/agent-workspace-validation.js +1 -1
  32. package/dist/src/session/handlers/chat-events.js +2 -2
  33. package/dist/src/session/queue.js +1 -1
  34. package/node_modules/@shennian/wire/dist/session.d.ts +6 -0
  35. package/package.json +1 -1
@@ -1,13 +1,13 @@
1
- import N from"node:http";import{createHash as _,randomBytes as T,randomUUID as k}from"node:crypto";import m from"node:fs";import E from"node:os";import M from"node:path";import{ManagerRegistry as W}from"./registry.js";import{appendMessage as O}from"../session/store.js";import{requestSessionIndexPublish as D}from"../session/index-publisher.js";import{RetiredChannelRuntime as q}from"./retired-external-channel-compat.js";import{resolveShennianPath as P}from"../config/index.js";import{buildExternalChannelInstructions as C}from"../agents/external-channel-instructions.js";import{getPersonalSyncIdentity as w}from"../personal-sync/epoch.js";import{handleWorkerIpcRoute as A}from"./worker-ipc.js";import{deriveWorkerTitle as B,formatWorkerMarkdown as H}from"./worker-markdown.js";import{markTrustedManagerEvent as S}from"../session/manager-event-authority.js";const $=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024),y=12*6e4;let b=null;function ue(u){b=u}function ge(){return b}function p(u){return M.resolve(u||E.homedir())}function f(u,e,t){u.writeHead(e,{"content-type":"application/json; charset=utf-8"}),u.end(JSON.stringify(t))}function R(u){return u.replace(/\r\n/g,`
2
- `).trim()}function U(u){return u==="worker.final"?"completed":u==="worker.error"?"failed":u==="worker.aborted"?"stopped":"progress"}function j(u,e){return`manager-inbox:${_("sha256").update(u).update("\0").update(e.map(r=>r.id).join("\0")).digest("hex").slice(0,32)}`}async function G(u){const e=[];let t=0;for await(const r of u){const n=Buffer.isBuffer(r)?r:Buffer.from(r);if(t+=n.length,t>$)throw new Error("Request body too large");e.push(n)}return e.length===0?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}function F(u,e,t,r){u.client.sendRes({type:"res",id:e,ok:t,...t?{payload:r}:{error:String(r.error||"unknown error")}})}function x(){return J()?M.join(E.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):P("runtime","manager-ipc.json")}function J(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class de{opts;managedPolicies=new Map;registry=new W;channelRuntime;server=null;ipcUrl=null;ipcToken=T(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;pendingWorkerOrigins=new Map;prepareWorkerTurnOrigin(e,t){t?this.pendingWorkerOrigins.set(e,t):this.pendingWorkerOrigins.delete(e)}constructor(e){this.opts=e,this.channelRuntime=e.channelRuntime??new q}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.registry.clearWorkerRunOrigins(),this.server=N.createServer((t,r)=>{this.handleIpc(t,r)}),this.server.requestTimeout=y,this.server.timeout=y,this.server.keepAliveTimeout=y,this.server.headersTimeout=y+5e3,await new Promise((t,r)=>{this.server.once("error",r),this.server.listen(0,"127.0.0.1",()=>t())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref(),this.recoverPersistedInboxDeliveries()}async ready(){await this.start()}async stop(){this.healthTimer&&clearInterval(this.healthTimer),this.healthTimer=null,await this.channelRuntime.stop(),await new Promise(e=>{if(!this.server)return e();this.server.close(()=>e())}),this.server=null,this.ipcUrl=null,this.removeIpcRuntimeFile()}writeIpcRuntimeFile(){if(this.ipcUrl)try{const e=x();m.mkdirSync(M.dirname(e),{recursive:!0}),m.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),m.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=x(),t=JSON.parse(m.readFileSync(e,"utf-8"));(t.url===this.ipcUrl||t.token===this.ipcToken)&&m.unlinkSync(e)}catch{}}getInjectedEnv(e,t,r,n){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:t??"",SHENNIAN_MANAGER_WORKDIR:p(r),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:p(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null,managedRoomBound:e.managedRoomBound??(this.managedPolicies.has(e.sessionId)||void 0)})}setManagedAgentPolicy(e,t){if(t){this.managedPolicies.set(e,t);const r=this.registry.getManager(e);r&&!r.managedRoomBound&&this.registry.upsertManager({...r,managedRoomBound:!0})}else this.managedPolicies.delete(e)}getManagedAgentPolicyForWorker(e,t){const r=this.registry.getManager(e),n=this.managedPolicies.get(e);return!r||!n||p(t)!==r.workDir?null:n}isNativeSkillManager(e,t){const r=this.registry.getManager(e);return r?.controllerKind==="native_skill"&&p(t)===r.workDir}isManagedWorkerSession(e){return!!this.findWorker(e)}isManagedRoomBoundManager(e){return this.registry.getManager(e)?.managedRoomBound===!0}async retireManagerAndWorkers(e){const t=this.registry.retireManager(e);this.managedPolicies.delete(e);const r=this.opts.getRuntime(),n=[],i=async(s,d)=>{const a=[];try{r.runJournal?.markSessionAborted(s)}catch(l){a.push(l)}try{r.recoveryBarrier?.end(s)}catch(l){a.push(l)}try{r.chatQueue?.purgeSession(s)}catch(l){a.push(l)}const o=r.sessions.get(s);let c=!o;if(o){if(o.managedRoomContextToken)try{r.revokeManagedRoomContextToken?.(o.managedRoomContextToken),o.managedRoomContextToken=null}catch(l){a.push(l)}try{await o.adapter.stop(),c=!0}catch(l){a.push(l)}}if(o&&c&&a.length===0&&(r.sessions.delete(s),o.heartbeatTimer&&clearInterval(o.heartbeatTimer),o.adapter.removeAllListeners()),c&&a.length===0)try{d?.()}catch(l){a.push(l),n.push(l)}else n.push(...a)};await i(e);for(const s of t)await i(s.sessionId,()=>this.registry.completeRetiredWorker(e,s.sessionId));if(n.length)throw new AggregateError(n,"Failed to retire every Manager or Worker session");return t.map(s=>s.sessionId)}setManagerWorkerDefaults(e,t,r){const n=this.registry.getManager(e);n&&this.registry.upsertManager({...n,defaultWorkerAgentType:t??null,defaultWorkerModelId:r??null})}getManagerWorkerDefaults(e){const t=this.registry.getManager(e);return{agentType:t?.defaultWorkerAgentType??null,modelId:t?.defaultWorkerModelId??null}}noteManagerAgentSession(e,t,r,n){const i=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:t,workDir:p(r),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n,agentType:i?.agentType,providerModelId:i?.providerModelId})}noteAgentEvent(e,t){const r=this.findWorker(e);if(!r)return;const n={lastActivityAt:new Date().toISOString(),runId:t.runId};if(t.agentSessionId&&(n.agentSessionId=t.agentSessionId),t.state==="start"){const a=this.pendingWorkerOrigins.get(e);this.pendingWorkerOrigins.delete(e),a&&(n.runRoomOrigins={...r.runRoomOrigins??{},[t.runId]:a})}const i=`${e}:${t.runId}`;let s=t.message||r.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)";if(t.state==="delta"&&t.text&&!t.thinking){const a=(this.workerTextAcc.get(i)??"")+t.text;this.workerTextAcc.set(i,a);const o=R(a);o&&(n.summary=o.length>160?`${o.slice(0,160)}...`:o)}if(t.state==="final"||t.state==="error"||t.state==="aborted"){n.status=t.state;const a=R(this.workerTextAcc.get(i)??"");if(a&&(n.summary=a.length>240?`${a.slice(0,240)}...`:a,s=t.message||a),this.workerTextAcc.delete(i),r.runRoomOrigins?.[t.runId]){const o={...r.runRoomOrigins};delete o[t.runId],n.runRoomOrigins=o}}else t.state==="start"&&(n.status="running");const d=this.registry.updateWorker(e,n);d&&(t.state==="final"||t.state==="error"||t.state==="aborted")&&this.enqueueWorkerEvent(d.managedBy,d,t.state,t.runId,s,r.runRoomOrigins?.[t.runId]??null)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){F(this.opts.getRuntime(),e.id,!1,{error:"feature_retired"})}async handleIpc(e,t){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){f(t,401,{ok:!1,error:"Unauthorized"});return}try{const r=new URL(e.url??"/","http://127.0.0.1"),n=await G(e);if(r.pathname.startsWith("/channel/")||r.pathname.startsWith("/external/")||r.pathname.startsWith("/wechat-rpa/")){f(t,410,{ok:!1,error:"feature_retired"});return}const i=await A({registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},r.pathname,n,String(e.headers["x-shennian-manager-session-id"]||""));if(i){f(t,i.status,i.body);return}f(t,404,{ok:!1,error:`Unknown manager IPC path: ${r.pathname}`})}catch(r){f(t,400,{ok:!1,error:r instanceof Error?r.message:String(r)})}}async executeScopedWorkerAction(e,t,r,n){const i={"manager.sessions.list":"/sessions/list","manager.sessions.start":"/sessions/start","manager.sessions.send":"/sessions/send","manager.sessions.queue":"/sessions/queue","manager.sessions.queue.edit":"/sessions/queue/edit","manager.sessions.queue.delete":"/sessions/queue/delete","manager.sessions.stop":"/sessions/stop","manager.sessions.read":"/sessions/read","manager.memory.path":"/memory/path"},s=await A({registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},i[t],r,e,{roomOrigin:n});if(!s||s.status<200||s.status>=300)throw new Error(String(s?.body.error||"Managed Manager action failed"));return JSON.parse(JSON.stringify(s.body))}async dispatchChatEnqueue(e,t,r,n,i,s,d,a){if((await this.opts.dispatchReq(S({type:"req",id:`manager-enqueue-${k()}`,method:"chat.enqueue",params:{...w(),sessionId:e,text:n,agentType:t,workDir:r,agentSessionId:i,modelId:s,managedWorkerParentSessionId:d,managerWorkerRoomOrigin:a}})))?.durableQueueHandoff===!1)throw new Error("manager_worker_queue_handoff_unconfirmed")}appendManagerVisibleMarkdown(e,t,r){this.registry.getManager(e)?.controllerKind!=="native_skill"&&(O(e,{id:r,sessionId:e,role:"agent",ts:Date.now(),payload:t},"committed"),D(this.opts.getRuntime().client))}enqueueWorkerEvent(e,t,r,n,i,s){const d=this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:t.sessionId,kind:r==="final"?"worker.final":r==="error"?"worker.error":"worker.aborted",priority:"normal",runId:n??null,roomOrigin:s??null,summary:i||t.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}),a=this.registry.getManager(e);d&&a?.controllerKind!=="native_skill"&&this.drainManagerInbox(e)}scanWorkerHealth(){const e=Date.now(),t=this.registry.load(),r=this.opts.getRuntime();for(const n of Object.values(t.workers)){if(n.status!=="running")continue;const i=e-Date.parse(n.createdAt);if(i<10*6e4)continue;const s=e-Date.parse(n.lastActivityAt),d=r.sessions.get(n.sessionId)?.currentRunId,a=!!r.chatQueue?.getSnapshot(n.sessionId).pending.length;if(!d){if(!a&&s>=10*6e4){const v="\u672A\u627E\u5230\u6D3B\u52A8\u4E2D\u7684 Provider Run \u6216\u5F85\u5904\u7406\u6D88\u606F\uFF1B\u5DF2\u5C06\u9057\u7559\u7684\u8FD0\u884C\u72B6\u6001\u6536\u655B\u4E3A\u5931\u8D25\u3002\u539F Worker \u4F1A\u8BDD\u4ECD\u4FDD\u7559\uFF0C\u53EF\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002",I=this.registry.updateWorker(n.sessionId,{status:"error",lastActivityAt:new Date(e).toISOString(),summary:v});I&&this.enqueueWorkerEvent(I.managedBy,I,"error",n.runId??void 0,v)}continue}const o=n.healthNotifiedAt?Date.parse(n.healthNotifiedAt):0;if(o&&e-o<10*6e4)continue;const c=t.managers[n.managedBy];if(!c)continue;const l=Math.max(0,Math.floor(s/6e4)),g=R(n.runId?this.workerTextAcc.get(`${n.sessionId}:${n.runId}`)??"":""),h=`\u5DF2\u8FD0\u884C ${Math.floor(i/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
1
+ import _ from"node:http";import{createHash as N,randomBytes as W,randomUUID as k}from"node:crypto";import p from"node:fs";import A from"node:os";import M from"node:path";import{ManagerRegistry as T}from"./registry.js";import{appendMessage as O}from"../session/store.js";import{requestSessionIndexPublish as q}from"../session/index-publisher.js";import{RetiredChannelRuntime as D}from"./retired-external-channel-compat.js";import{resolveShennianPath as P}from"../config/index.js";import{buildExternalChannelInstructions as C}from"../agents/external-channel-instructions.js";import{getPersonalSyncIdentity as w}from"../personal-sync/epoch.js";import{EphemeralWorkerError as B,handleEphemeralWorkerIpcRoute as H,handleWorkerIpcRoute as v}from"./worker-ipc.js";import{deriveWorkerTitle as $,formatWorkerMarkdown as U}from"./worker-markdown.js";import{markTrustedManagerEvent as S}from"../session/manager-event-authority.js";const j=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024),y=12*6e4;let b=null;function ge(d){b=d}function he(){return b}function f(d){return M.resolve(d||A.homedir())}function m(d,e,r){d.writeHead(e,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify(r))}function R(d){return d.replace(/\r\n/g,`
2
+ `).trim()}function G(d){return d==="worker.final"?"completed":d==="worker.error"?"failed":d==="worker.aborted"?"stopped":"progress"}function F(d,e){return`manager-inbox:${N("sha256").update(d).update("\0").update(e.map(t=>t.id).join("\0")).digest("hex").slice(0,32)}`}async function J(d){const e=[];let r=0;for await(const t of d){const n=Buffer.isBuffer(t)?t:Buffer.from(t);if(r+=n.length,r>j)throw new Error("Request body too large");e.push(n)}return e.length===0?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}function K(d,e,r,t){d.client.sendRes({type:"res",id:e,ok:r,...r?{payload:t}:{error:String(t.error||"unknown error")}})}function x(){return V()?M.join(A.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):P("runtime","manager-ipc.json")}function V(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class le{opts;managedPolicies=new Map;registry=new T;channelRuntime;server=null;ipcUrl=null;ipcToken=W(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;pendingWorkerOrigins=new Map;prepareWorkerTurnOrigin(e,r){r?this.pendingWorkerOrigins.set(e,r):this.pendingWorkerOrigins.delete(e)}constructor(e){this.opts=e,this.channelRuntime=e.channelRuntime??new D}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.registry.clearWorkerRunOrigins(),this.server=_.createServer((r,t)=>{this.handleIpc(r,t)}),this.server.requestTimeout=y,this.server.timeout=y,this.server.keepAliveTimeout=y,this.server.headersTimeout=y+5e3,await new Promise((r,t)=>{this.server.once("error",t),this.server.listen(0,"127.0.0.1",()=>r())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref(),this.recoverPersistedInboxDeliveries()}async ready(){await this.start()}async stop(){this.healthTimer&&clearInterval(this.healthTimer),this.healthTimer=null,await this.channelRuntime.stop(),await new Promise(e=>{if(!this.server)return e();this.server.close(()=>e())}),this.server=null,this.ipcUrl=null,this.removeIpcRuntimeFile()}writeIpcRuntimeFile(){if(this.ipcUrl)try{const e=x();p.mkdirSync(M.dirname(e),{recursive:!0}),p.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),p.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=x(),r=JSON.parse(p.readFileSync(e,"utf-8"));(r.url===this.ipcUrl||r.token===this.ipcToken)&&p.unlinkSync(e)}catch{}}getInjectedEnv(e,r,t,n){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:r??"",SHENNIAN_MANAGER_WORKDIR:f(t),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:f(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null,managedRoomBound:e.managedRoomBound??(this.managedPolicies.has(e.sessionId)||void 0)})}setManagedAgentPolicy(e,r){if(r){this.managedPolicies.set(e,r);const t=this.registry.getManager(e);t&&!t.managedRoomBound&&this.registry.upsertManager({...t,managedRoomBound:!0})}else this.managedPolicies.delete(e)}getManagedAgentPolicyForWorker(e,r){const t=this.registry.getManager(e),n=this.managedPolicies.get(e);return!t||!n||f(r)!==t.workDir?null:n}isExternalWorkerController(e,r){const t=this.registry.getManager(e);return t?.controllerKind==="ephemeral"&&f(r)===t.workDir}isManagedWorkerSession(e){return!!this.findWorker(e)}isManagedRoomBoundManager(e){return this.registry.getManager(e)?.managedRoomBound===!0}async retireManagerAndWorkers(e){const r=this.registry.retireManager(e);this.managedPolicies.delete(e);const t=this.opts.getRuntime(),n=[],a=async(s,u)=>{const i=[];try{t.runJournal?.markSessionAborted(s)}catch(h){i.push(h)}try{t.recoveryBarrier?.end(s)}catch(h){i.push(h)}try{t.chatQueue?.purgeSession(s)}catch(h){i.push(h)}const o=t.sessions.get(s);let c=!o;if(o){if(o.managedRoomContextToken)try{t.revokeManagedRoomContextToken?.(o.managedRoomContextToken),o.managedRoomContextToken=null}catch(h){i.push(h)}try{await o.adapter.stop(),c=!0}catch(h){i.push(h)}}if(o&&c&&i.length===0&&(t.sessions.delete(s),o.heartbeatTimer&&clearInterval(o.heartbeatTimer),o.adapter.removeAllListeners()),c&&i.length===0)try{u?.()}catch(h){i.push(h),n.push(h)}else n.push(...i)};await a(e);for(const s of r)await a(s.sessionId,()=>this.registry.completeRetiredWorker(e,s.sessionId));if(n.length)throw new AggregateError(n,"Failed to retire every Manager or Worker session");return r.map(s=>s.sessionId)}setManagerWorkerDefaults(e,r,t){const n=this.registry.getManager(e);n&&this.registry.upsertManager({...n,defaultWorkerAgentType:r??null,defaultWorkerModelId:t??null})}getManagerWorkerDefaults(e){const r=this.registry.getManager(e);return{agentType:r?.defaultWorkerAgentType??null,modelId:r?.defaultWorkerModelId??null}}noteManagerAgentSession(e,r,t,n){const a=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:r,workDir:f(t),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n,agentType:a?.agentType,providerModelId:a?.providerModelId})}noteAgentEvent(e,r){const t=this.findWorker(e);if(!t)return;const n={lastActivityAt:new Date().toISOString(),runId:r.runId};if(r.agentSessionId&&(n.agentSessionId=r.agentSessionId),r.state==="start"){const i=this.pendingWorkerOrigins.get(e);this.pendingWorkerOrigins.delete(e),i&&(n.runRoomOrigins={...t.runRoomOrigins??{},[r.runId]:i})}const a=`${e}:${r.runId}`;let s=r.message||t.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)";if(r.state==="delta"&&r.text&&!r.thinking){const i=(this.workerTextAcc.get(a)??"")+r.text;this.workerTextAcc.set(a,i);const o=R(i);o&&(n.summary=o.length>160?`${o.slice(0,160)}...`:o)}if(r.state==="final"||r.state==="error"||r.state==="aborted"){n.status=r.state;const i=R(this.workerTextAcc.get(a)??"");if(i&&(n.summary=i.length>240?`${i.slice(0,240)}...`:i,s=r.state==="final"?i:r.message||i),this.workerTextAcc.delete(a),t.runRoomOrigins?.[r.runId]){const o={...t.runRoomOrigins};delete o[r.runId],n.runRoomOrigins=o}}else r.state==="start"&&(n.status="running");const u=this.registry.updateWorker(e,n);u&&r.errorCode==="auth_retrying"&&this.registry.enqueueInboxEvent({managerSessionId:u.managedBy,workerSessionId:u.sessionId,kind:"worker.health",priority:"normal",runId:r.runId,roomOrigin:t.runRoomOrigins?.[r.runId]??null,summary:r.message||"Worker authentication is retrying.",code:"auth_retrying"}),u&&(r.state==="final"||r.state==="error"||r.state==="aborted")&&this.enqueueWorkerEvent(u.managedBy,u,r.state,r.runId,s,t.runRoomOrigins?.[r.runId]??null,r.errorCode==="auth_required"||r.errorCode==="acp_auth_required"?"auth_required":void 0)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){K(this.opts.getRuntime(),e.id,!1,{error:"feature_retired"})}async handleIpc(e,r){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){m(r,401,{ok:!1,error:"Unauthorized"});return}try{const t=new URL(e.url??"/","http://127.0.0.1"),n=await J(e);if(t.pathname.startsWith("/channel/")||t.pathname.startsWith("/external/")||t.pathname.startsWith("/wechat-rpa/")){m(r,410,{ok:!1,error:"feature_retired"});return}const a={registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},s=await H(a,t.pathname,n)??await v(a,t.pathname,n,String(e.headers["x-shennian-manager-session-id"]||""));if(s){m(r,s.status,s.body);return}m(r,404,{ok:!1,error:`Unknown manager IPC path: ${t.pathname}`})}catch(t){if(t instanceof B){m(r,t.status,{ok:!1,error:t.code,message:t.message,nextAction:t.nextAction});return}m(r,400,{ok:!1,error:t instanceof Error?t.message:String(t)})}}async executeScopedWorkerAction(e,r,t,n){const a={"manager.sessions.list":"/sessions/list","manager.sessions.start":"/sessions/start","manager.sessions.send":"/sessions/send","manager.sessions.queue":"/sessions/queue","manager.sessions.queue.edit":"/sessions/queue/edit","manager.sessions.queue.delete":"/sessions/queue/delete","manager.sessions.stop":"/sessions/stop","manager.sessions.read":"/sessions/read","manager.memory.path":"/memory/path"},s=await v({registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},a[r],t,e,{roomOrigin:n});if(!s||s.status<200||s.status>=300)throw new Error(String(s?.body.error||"Managed Manager action failed"));return JSON.parse(JSON.stringify(s.body))}async dispatchChatEnqueue(e,r,t,n,a,s,u,i){if((await this.opts.dispatchReq(S({type:"req",id:`manager-enqueue-${k()}`,method:"chat.enqueue",params:{...w(),sessionId:e,text:n,agentType:r,workDir:t,agentSessionId:a,modelId:s,managedWorkerParentSessionId:u,managerWorkerRoomOrigin:i}})))?.durableQueueHandoff===!1)throw new Error("manager_worker_queue_handoff_unconfirmed")}appendManagerVisibleMarkdown(e,r,t){this.registry.getManager(e)?.controllerKind==="managed_session"&&(O(e,{id:t,sessionId:e,role:"agent",ts:Date.now(),payload:r},"committed"),q(this.opts.getRuntime().client))}enqueueWorkerEvent(e,r,t,n,a,s,u){const i=this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:r.sessionId,kind:t==="final"?"worker.final":t==="error"?"worker.error":"worker.aborted",priority:"normal",runId:n??null,roomOrigin:s??null,summary:a||r.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)",...u?{code:u}:{}}),o=this.registry.getManager(e);i&&o?.controllerKind==="managed_session"&&this.drainManagerInbox(e)}scanWorkerHealth(){const e=Date.now(),r=this.registry.load(),t=this.opts.getRuntime();for(const n of Object.values(r.workers)){if(n.status!=="running")continue;const a=e-Date.parse(n.createdAt);if(a<10*6e4)continue;const s=e-Date.parse(n.lastActivityAt),u=t.sessions.get(n.sessionId)?.currentRunId,i=!!t.chatQueue?.getSnapshot(n.sessionId).pending.length;if(!u){if(!i&&s>=10*6e4){const E="\u672A\u627E\u5230\u6D3B\u52A8\u4E2D\u7684 Provider Run \u6216\u5F85\u5904\u7406\u6D88\u606F\uFF1B\u5DF2\u5C06\u9057\u7559\u7684\u8FD0\u884C\u72B6\u6001\u6536\u655B\u4E3A\u5931\u8D25\u3002\u539F Worker \u4F1A\u8BDD\u4ECD\u4FDD\u7559\uFF0C\u53EF\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002",I=this.registry.updateWorker(n.sessionId,{status:"error",lastActivityAt:new Date(e).toISOString(),summary:E});I&&this.enqueueWorkerEvent(I.managedBy,I,"error",n.runId??void 0,E)}continue}const o=n.healthNotifiedAt?Date.parse(n.healthNotifiedAt):0;if(o&&e-o<10*6e4)continue;const c=r.managers[n.managedBy];if(!c)continue;const h=Math.max(0,Math.floor(s/6e4)),g=R(n.runId?this.workerTextAcc.get(`${n.sessionId}:${n.runId}`)??"":""),l=`\u5DF2\u8FD0\u884C ${Math.floor(a/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
3
3
 
4
4
  \u5F53\u524D\u53EF\u89C1\u8FDB\u5C55\uFF1A
5
5
  - \u6700\u8FD1\u6587\u672C\uFF1A${g||n.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)"}
6
6
  - \u6700\u8FD1\u6D3B\u52A8\u65F6\u95F4\uFF1A${n.lastActivityAt}
7
- - \u6700\u8FD1 ${l} \u5206\u949F\u6CA1\u6709\u65B0\u6D3B\u52A8\u3002`;this.registry.updateWorker(n.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:h}),this.registry.enqueueInboxEvent({managerSessionId:c.sessionId,workerSessionId:n.sessionId,kind:"worker.health",priority:"normal",runId:n.runId??null,roomOrigin:n.runRoomOrigins?.[n.runId??""]??null,summary:h})&&c.controllerKind!=="native_skill"&&this.drainManagerInbox(c.sessionId)}}async interruptAndResumeManager(e,t,r,n){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${k()}`,method:"chat.abort",params:{...w(),sessionId:e.sessionId}}),await this.dispatchManagerContinuation(e,t,!1,r,n)}bindManagerAdapterEvents(e,t){t.on("agentEvent",r=>{if(r.state==="start"&&r.agentSessionId){const n=this.registry.getManager(e);n&&this.noteManagerAgentSession(e,r.agentSessionId,n.workDir,n.modelId)}r.state==="start"&&(this.acknowledgeManagerInboxDelivery(e),this.updateManagerStatus(e,"running")),(r.state==="final"||r.state==="error"||r.state==="aborted")&&(this.updateManagerStatus(e,"idle"),queueMicrotask(()=>{this.drainManagerInbox(e)}))})}updateManagerStatus(e,t){const r=this.registry.getManager(e);r&&this.registry.upsertManager({...r,status:t})}async drainManagerInbox(e){const t=this.registry.getManager(e);if(!t||t.deliveringInboxEventIds?.length)return;const r=this.registry.listInboxEvents(e);if(!r.length)return;const n=r.find(g=>g.priority==="high"),i=this.opts.getRuntime().sessions.get(e);if(t.status!=="idle"&&!n||i?.currentRunId&&!n)return;const d=(n??r[0]).roomOrigin??null,a=JSON.stringify(d),o=n?[n]:r.filter(g=>JSON.stringify(g.roomOrigin??null)===a).slice(0,20),c=o.map(g=>{const h=this.registry.getWorkerForManager(e,g.workerSessionId);return H({kind:U(g.kind),sessionId:g.workerSessionId,title:h?.title||B(h?.userGoal||h?.summary||g.summary),body:g.summary})}).join(`
7
+ - \u6700\u8FD1 ${h} \u5206\u949F\u6CA1\u6709\u65B0\u6D3B\u52A8\u3002`;this.registry.updateWorker(n.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:l}),this.registry.enqueueInboxEvent({managerSessionId:c.sessionId,workerSessionId:n.sessionId,kind:"worker.health",priority:"normal",runId:n.runId??null,roomOrigin:n.runRoomOrigins?.[n.runId??""]??null,summary:l})&&c.controllerKind==="managed_session"&&this.drainManagerInbox(c.sessionId)}}async interruptAndResumeManager(e,r,t,n){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${k()}`,method:"chat.abort",params:{...w(),sessionId:e.sessionId}}),await this.dispatchManagerContinuation(e,r,!1,t,n)}bindManagerAdapterEvents(e,r){r.on("agentEvent",t=>{if(t.state==="start"&&t.agentSessionId){const n=this.registry.getManager(e);n&&this.noteManagerAgentSession(e,t.agentSessionId,n.workDir,n.modelId)}t.state==="start"&&(this.acknowledgeManagerInboxDelivery(e),this.updateManagerStatus(e,"running")),(t.state==="final"||t.state==="error"||t.state==="aborted")&&(this.updateManagerStatus(e,"idle"),queueMicrotask(()=>{this.drainManagerInbox(e)}))})}updateManagerStatus(e,r){const t=this.registry.getManager(e);t&&this.registry.upsertManager({...t,status:r})}async drainManagerInbox(e){const r=this.registry.getManager(e);if(!r||r.deliveringInboxEventIds?.length)return;const t=this.registry.listInboxEvents(e);if(!t.length)return;const n=t.find(g=>g.priority==="high"),a=this.opts.getRuntime().sessions.get(e);if(r.status!=="idle"&&!n||a?.currentRunId&&!n)return;const u=(n??t[0]).roomOrigin??null,i=JSON.stringify(u),o=n?[n]:t.filter(g=>JSON.stringify(g.roomOrigin??null)===i).slice(0,20),c=o.map(g=>{const l=this.registry.getWorkerForManager(e,g.workerSessionId);return U({kind:G(g.kind),sessionId:g.workerSessionId,title:l?.title||$(l?.userGoal||l?.summary||g.summary),body:g.summary})}).join(`
8
8
 
9
9
  ---
10
10
 
11
- `),l=j(e,o);try{n&&i?.currentRunId?(this.registry.upsertManager({...t,status:"interrupting",deliveringInboxEventIds:o.map(h=>h.id)}),await this.interruptAndResumeManager(t,c,l,d)):(this.registry.upsertManager({...t,status:"running",deliveringInboxEventIds:o.map(h=>h.id)}),await this.dispatchManagerContinuation(t,c,!0,l,d)),this.registry.removeInboxEvents(e,o.map(h=>h.id));const g=this.registry.getManager(e);g&&this.registry.upsertManager({...g,deliveringInboxEventIds:[]})}catch(g){const h=this.registry.getManager(e);h&&this.registry.upsertManager({...h,status:"idle",deliveringInboxEventIds:[]}),console.error(`[manager-inbox] delivery failed sessionId=${e}: ${g instanceof Error?g.message:String(g)}`)}}acknowledgeManagerInboxDelivery(e){const t=this.registry.getManager(e),r=t?.deliveringInboxEventIds??[];!t||r.length===0||(this.registry.removeInboxEvents(e,r),this.registry.upsertManager({...t,deliveringInboxEventIds:[]}))}recoverPersistedInboxDeliveries(){const e=this.registry.load(),t=this.opts.getRuntime();for(const r of Object.values(e.managers)){if(t.sessions.get(r.sessionId)?.currentRunId)continue;(r.status!=="idle"||r.deliveringInboxEventIds?.length)&&this.registry.upsertManager({...r,status:"idle",deliveringInboxEventIds:[]});const i=this.registry.listInboxEvents(r.sessionId).filter(s=>s.kind==="worker.health"&&!t.sessions.get(s.workerSessionId)?.currentRunId).map(s=>s.id);this.registry.removeInboxEvents(r.sessionId,i),this.registry.listInboxEvents(r.sessionId).length>0&&queueMicrotask(()=>{this.drainManagerInbox(r.sessionId)})}}async dispatchManagerContinuation(e,t,r=!1,n,i){const s=e.agentType??"manager",d=s==="manager"?e.modelId:e.providerModelId??void 0,a={...w(),sessionId:e.sessionId,text:t,agentType:s,sessionMode:"manager",workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:d,managerDefaultWorkerAgentType:e.defaultWorkerAgentType??null,managerDefaultWorkerModelId:e.defaultWorkerModelId??null,...e.managedRoomBound?{managedAgentDirect:!0}:{},clientMessageId:n,managerEventId:n,...i?{managerEventRoomOrigin:i}:{}};if((await this.opts.dispatchReq(S({type:"req",id:`manager-${r?"enqueue":"send"}-${k()}`,method:"chat.enqueue",params:a})))?.durableQueueHandoff===!1)throw new Error("manager_event_queue_handoff_unconfirmed")}getExternalChannelStatus(e){return this.channelRuntime.getManagerChannelStatus(e)}getManagerExternalChannelSystemPrompt(e){return this.channelRuntime.listManagerExternalChannels(e).map(t=>C(t,void 0,e,"manager")).join(`
11
+ `),h=F(e,o);try{n&&a?.currentRunId?(this.registry.upsertManager({...r,status:"interrupting",deliveringInboxEventIds:o.map(l=>l.id)}),await this.interruptAndResumeManager(r,c,h,u)):(this.registry.upsertManager({...r,status:"running",deliveringInboxEventIds:o.map(l=>l.id)}),await this.dispatchManagerContinuation(r,c,!0,h,u)),this.registry.removeInboxEvents(e,o.map(l=>l.id));const g=this.registry.getManager(e);g&&this.registry.upsertManager({...g,deliveringInboxEventIds:[]})}catch(g){const l=this.registry.getManager(e);l&&this.registry.upsertManager({...l,status:"idle",deliveringInboxEventIds:[]}),console.error(`[manager-inbox] delivery failed sessionId=${e}: ${g instanceof Error?g.message:String(g)}`)}}acknowledgeManagerInboxDelivery(e){const r=this.registry.getManager(e),t=r?.deliveringInboxEventIds??[];!r||t.length===0||(this.registry.removeInboxEvents(e,t),this.registry.upsertManager({...r,deliveringInboxEventIds:[]}))}recoverPersistedInboxDeliveries(){const e=this.registry.load(),r=this.opts.getRuntime();for(const t of Object.values(e.managers)){if(r.sessions.get(t.sessionId)?.currentRunId)continue;(t.status!=="idle"||t.deliveringInboxEventIds?.length)&&this.registry.upsertManager({...t,status:"idle",deliveringInboxEventIds:[]});const a=this.registry.listInboxEvents(t.sessionId).filter(s=>s.kind==="worker.health"&&!r.sessions.get(s.workerSessionId)?.currentRunId).map(s=>s.id);this.registry.removeInboxEvents(t.sessionId,a),this.registry.listInboxEvents(t.sessionId).length>0&&queueMicrotask(()=>{this.drainManagerInbox(t.sessionId)})}}async dispatchManagerContinuation(e,r,t=!1,n,a){const s=e.agentType??"manager",u=s==="manager"?e.modelId:e.providerModelId??void 0,i={...w(),sessionId:e.sessionId,text:r,agentType:s,sessionMode:"manager",workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:u,managerDefaultWorkerAgentType:e.defaultWorkerAgentType??null,managerDefaultWorkerModelId:e.defaultWorkerModelId??null,...e.managedRoomBound?{managedAgentDirect:!0}:{},clientMessageId:n,managerEventId:n,...a?{managerEventRoomOrigin:a}:{}};if((await this.opts.dispatchReq(S({type:"req",id:`manager-${t?"enqueue":"send"}-${k()}`,method:"chat.enqueue",params:i})))?.durableQueueHandoff===!1)throw new Error("manager_event_queue_handoff_unconfirmed")}getExternalChannelStatus(e){return this.channelRuntime.getManagerChannelStatus(e)}getManagerExternalChannelSystemPrompt(e){return this.channelRuntime.listManagerExternalChannels(e).map(r=>C(r,void 0,e,"manager")).join(`
12
12
 
13
- `).trim()}}export{de as ManagerRuntimeService,ge as getManagerRuntimeService,ue as setManagerRuntimeService};
13
+ `).trim()}}export{le as ManagerRuntimeService,he as getManagerRuntimeService,ge as setManagerRuntimeService};
@@ -13,7 +13,14 @@ type WorkerIpcHost = {
13
13
  dispatchChatEnqueue: (sessionId: string, agentType: AgentType, workDir: string, text: string, agentSessionId: string | null, modelId?: string, managedWorkerParentSessionId?: string, roomOrigin?: ManagedWorkerRoomOrigin) => Promise<void>;
14
14
  appendManagerVisibleMarkdown: (managerSessionId: string, markdown: string, id: string) => void;
15
15
  };
16
+ export declare class EphemeralWorkerError extends Error {
17
+ readonly code: string;
18
+ readonly nextAction: string;
19
+ readonly status: number;
20
+ constructor(code: string, message: string, nextAction: string, status?: number);
21
+ }
16
22
  export declare function handleWorkerIpcRoute(host: WorkerIpcHost, pathname: string, body: Record<string, unknown>, headerManagerSessionId: string, trustedOrigin?: {
17
23
  roomOrigin?: ManagedWorkerRoomOrigin;
18
24
  }): Promise<WorkerIpcResponse | null>;
25
+ export declare function handleEphemeralWorkerIpcRoute(host: WorkerIpcHost, pathname: string, body: Record<string, unknown>): Promise<WorkerIpcResponse | null>;
19
26
  export {};
@@ -1 +1 @@
1
- import w from"node:path";import{createHash as M,randomUUID as p}from"node:crypto";import{AVAILABLE_BUILTIN_AGENT_TYPES as q,extractPayloadText as W,isAgentHiddenPayload as E,isToolPayload as T}from"@shennian/wire";import{loadConfig as _}from"../config/index.js";import{getPersonalSyncIdentity as S}from"../personal-sync/epoch.js";import{readMessages as x,recordSession as D}from"../session/store.js";import{deriveWorkerTitle as k,formatWorkerMarkdown as f,normalizeWorkerTitle as b}from"./worker-markdown.js";function h(e){return w.resolve(e||process.cwd())}function v(e){return e==="manager"||e==="pi"?!1:e.startsWith("custom:")?!0:q.includes(e)}function A(e){return e==="worker.final"?"completed":e==="worker.error"?"failed":e==="worker.aborted"?"stopped":"progress"}function $(e,i){const s=[];for(const g of e)if(!E(g.payload))if(T(g.payload)){let c="tool",a="completed";try{const l=JSON.parse(g.payload);c=l.name||c,a=l.status||a}catch{}s.push({...g,payload:`[\u5DE5\u5177] ${c} \xB7 ${a}`})}else s.push({...g,payload:W(g.payload)});return s.slice(-Math.max(1,Math.min(i,500)))}async function O(e,i,s,g,c){if(i==="/skill/context"){const r=_(),o=String(s.host||""),n=String(s.hostSessionId||""),d=h(String(s.workDir||""));if(!r.machineId||!r.machineToken&&!r.accessToken)throw new Error("Shennian CLI must be paired and logged in");if(o!=="codex"&&o!=="claude")throw new Error("unsupported_native_agent_host");if(!n)throw new Error("native_agent_session_unavailable");if(!d||!w.isAbsolute(d))throw new Error("workDir is required");const m=`skill_manager_${M("sha256").update(r.machineId).update("\0").update(o).update("\0").update(n).update("\0").update(d).digest("hex").slice(0,32)}`;return e.registry.upsertManager({sessionId:m,agentSessionId:null,workDir:d,machineId:r.machineId,modelId:o,agentType:o,controllerKind:"native_skill",status:"idle"}),{status:200,body:{ok:!0,managerSessionId:m}}}if(!i.startsWith("/sessions/")&&i!=="/memory/path")return null;const a=String(s.managerSessionId||g||"");if(!a)throw new Error("managerSessionId is required");const l=e.registry.getManager(a);if(!l)throw new Error("Manager runtime is not registered");if(i==="/sessions/list"){const r=new Set([...e.getRuntime().sessions.entries()].filter(([,n])=>!!n.currentRunId).map(([n])=>n));return{status:200,body:{ok:!0,sessions:e.registry.listWorkers(a,{runningSessionIds:r})}}}if(i==="/memory/path")return{status:200,body:{ok:!0,path:w.join(l.workDir,".shennian")}};if(i==="/sessions/start"){const r=String(s.agentType||s.agent||l.defaultWorkerAgentType||"codex");if(!v(r))throw new Error(`Unsupported manager worker agent: ${r}`);const o=h(String(s.workDir||l.workDir));if(o!==l.workDir)throw new Error("Manager can only start workers in the same workDir");const n=String(s.message||"");if(!n)throw new Error("message is required");const d=s.title?b(String(s.title)):k(n),m=e.registry.addWorker({managerSessionId:a,agentType:r,workDir:o,title:d,summary:n.slice(0,120)}),I=String(s.modelId||(r===l.defaultWorkerAgentType?l.defaultWorkerModelId??"":""));D({sessionId:m.sessionId,agentType:r,workDir:o,agentSessionId:null,modelId:I||null,title:d,titleSource:"shennian_generated"}),await e.dispatchChatEnqueue(m.sessionId,r,o,n,null,I||void 0,a,c?.roomOrigin);const y=f({kind:"created",sessionId:m.sessionId,title:d,body:n});return e.appendManagerVisibleMarkdown(a,y,`worker-created:${m.sessionId}`),{status:200,body:{ok:!0,session:m,markdown:y}}}const u=String(s.sessionId||""),t=e.registry.getWorkerForManager(a,u);if(!t||t.role!=="managed_worker"||t.managedBy!==a)throw new Error("Worker not found in this manager scope");if(i==="/sessions/send"){const r=String(s.message||"");if(!r.trim())throw new Error("message is required");const o=s.enqueue===void 0?!0:!!s.enqueue,n=String(s.modelId||(t.agentType===l.defaultWorkerAgentType?l.defaultWorkerModelId??"":""));await e.dispatchChatEnqueue(t.sessionId,t.agentType,t.workDir,r,t.agentSessionId??null,n||void 0,a,c?.roomOrigin);const d=f({kind:"message",sessionId:u,title:t.title||k(t.userGoal||t.summary||r),body:r});return e.appendManagerVisibleMarkdown(a,d,`worker-message:${p()}`),{status:200,body:{ok:!0,markdown:d,queued:!0,directRequested:!o}}}if(i==="/sessions/queue")return{status:200,body:{ok:!0,queue:e.getRuntime().chatQueue?.getSnapshot(u)}};if(i==="/sessions/queue/edit"||i==="/sessions/queue/delete"){const r=i.endsWith("/edit");return await e.dispatchReq({type:"req",id:`manager-queue-${r?"edit":"delete"}-${p()}`,method:r?"chat.queue.edit":"chat.queue.delete",params:{...S(),sessionId:u,queueMessageId:String(s.queueMessageId||s.messageId||""),...r?{text:String(s.message||s.text||"")}:{}}}),{status:200,body:{ok:!0,queue:e.getRuntime().chatQueue?.getSnapshot(u)}}}if(i==="/sessions/stop"||i==="/sessions/terminate"){if(t.status==="final"||t.status==="error"||t.status==="aborted"){const r=t.status==="final"?"completed":t.status==="error"?"failed":"stopped";return{status:200,body:{ok:!0,alreadyTerminal:!0,status:t.status,markdown:f({kind:r,sessionId:u,title:t.title||k(t.userGoal||t.summary||""),body:t.summary||"\u672C\u6B21\u6267\u884C\u5DF2\u7ECF\u7ED3\u675F\u3002"})}}}return await e.dispatchReq({type:"req",id:`manager-abort-${p()}`,method:"chat.abort",params:{...S(),sessionId:u}}),e.registry.updateWorker(u,{status:"aborted"}),{status:200,body:{ok:!0,markdown:f({kind:"stopped",sessionId:u,title:t.title||k(t.userGoal||t.summary||""),body:"\u5DF2\u505C\u6B62\u672C\u6B21\u6267\u884C\u3002"})}}}if(i==="/sessions/read"){const r=Number(s.limit||200),o=x(u,{limit:Math.max(r*20,r)});return{status:200,body:{ok:!0,messages:$(o,r),rawMessageCount:o.length}}}if(i==="/sessions/wait"){const r=Math.max(1e3,Math.min(Number(s.timeoutMs||6e5),6e5)),o=Date.now()+r;let n;for(;Date.now()<o&&(n=e.registry.listInboxEvents(a).find(m=>m.workerSessionId===u),!n);)await new Promise(m=>setTimeout(m,1e3));if(!n)return{status:200,body:{ok:!0,timedOut:!0,status:e.registry.getWorkerForManager(a,u)?.status}};const d=f({kind:A(n.kind),sessionId:u,title:t.title||k(t.userGoal||t.summary||n.summary),body:n.summary});return e.registry.removeInboxEvents(a,[n.id]),{status:200,body:{ok:!0,timedOut:!1,markdown:d,event:n}}}return null}export{O as handleWorkerIpcRoute};
1
+ import I from"node:fs";import q from"node:path";import{randomUUID as _}from"node:crypto";import{extractPayloadText as $,isAvailableAgentType as P,isAgentHiddenPayload as C,isToolPayload as U}from"@shennian/wire";import{loadConfig as O}from"../config/index.js";import{detectAgents as j}from"../agents/detect.js";import{getCachedAgentInfos as N}from"../agents/model-registry/service.js";import{getPersonalSyncIdentity as T}from"../personal-sync/epoch.js";import{readMessages as G,recordSession as K}from"../session/store.js";import{deriveWorkerTitle as p,formatWorkerMarkdown as h,normalizeWorkerTitle as L}from"./worker-markdown.js";class g extends Error{code;nextAction;status;constructor(a,r,i,w=400){super(r),this.code=a,this.nextAction=i,this.status=w}}function b(e){return q.resolve(e||process.cwd())}function B(e){const a=b(e);let r;try{r=I.statSync(a)}catch{throw new g("worker_workdir_invalid","The Worker working directory does not exist.","Choose an existing local project directory and retry worker start.")}if(!r.isDirectory())throw new g("worker_workdir_invalid","The Worker working directory is not a directory.","Choose an existing local project directory and retry worker start.");const i=I.realpathSync.native(a);try{I.accessSync(i,I.constants.R_OK|I.constants.X_OK)}catch{throw new g("worker_workdir_inaccessible","The Worker working directory is not readable and searchable by this user.","Choose a project directory this Shennian daemon user can access and retry worker start.")}return i}function x(e){return P(e)&&e!=="manager"&&e!=="pi"&&e!=="openclaw"}function z(e){return e==="worker.final"?"completed":e==="worker.error"?"failed":e==="worker.aborted"?"stopped":"progress"}function F(e,a){const r=[];for(const i of e)if(!C(i.payload))if(U(i.payload)){let w="tool",u="completed";try{const c=JSON.parse(i.payload);w=c.name||w,u=c.status||u}catch{}r.push({...i,payload:`[\u5DE5\u5177] ${w} \xB7 ${u}`})}else r.push({...i,payload:$(i.payload)});return r.slice(-Math.max(1,Math.min(a,500)))}async function M(e,a,r,i,w){if(!a.startsWith("/sessions/")&&a!=="/memory/path")return null;const u=String(r.managerSessionId||i||"");if(!u)throw new Error("managerSessionId is required");const c=e.registry.getManager(u);if(!c)throw new Error("Manager runtime is not registered");if(a==="/sessions/list"){const s=new Set([...e.getRuntime().sessions.entries()].filter(([,d])=>!!d.currentRunId).map(([d])=>d));return{status:200,body:{ok:!0,sessions:e.registry.listWorkers(u,{runningSessionIds:s})}}}if(a==="/memory/path")return{status:200,body:{ok:!0,path:q.join(c.workDir,".shennian")}};if(a==="/sessions/start"){const s=String(r.agentType||r.agent||c.defaultWorkerAgentType||"codex");if(!x(s))throw new Error(`Unsupported manager worker agent: ${s}`);const o=b(String(r.workDir||c.workDir));if(o!==c.workDir)throw new Error("Manager can only start workers in the same workDir");const d=String(r.message||"");if(!d)throw new Error("message is required");const m=r.title?L(String(r.title)):p(d),n=e.registry.addWorker({managerSessionId:u,agentType:s,workDir:o,title:m,summary:d.slice(0,120)}),k=String(r.modelId||(s===c.defaultWorkerAgentType?c.defaultWorkerModelId??"":""));K({sessionId:n.sessionId,agentType:s,workDir:o,agentSessionId:null,modelId:k||null,title:m,titleSource:"shennian_generated"}),await e.dispatchChatEnqueue(n.sessionId,s,o,d,null,k||void 0,u,w?.roomOrigin);const f=h({kind:"created",sessionId:n.sessionId,title:m,body:d});return e.appendManagerVisibleMarkdown(u,f,`worker-created:${n.sessionId}`),{status:200,body:{ok:!0,session:n,markdown:f}}}const l=String(r.sessionId||""),t=e.registry.getWorkerForManager(u,l);if(!t||t.role!=="managed_worker"||t.managedBy!==u)throw new Error("Worker not found in this manager scope");if(a==="/sessions/send"){const s=String(r.message||"");if(!s.trim())throw new Error("message is required");const o=r.enqueue===void 0?!0:!!r.enqueue,m=e.getRuntime().sessions.get(l)?.currentRunId?"worker_busy":t.status==="final"||t.status==="error"||t.status==="aborted"?"worker_terminal":void 0,n=String(r.modelId||(t.agentType===c.defaultWorkerAgentType?c.defaultWorkerModelId??"":""));await e.dispatchChatEnqueue(t.sessionId,t.agentType,t.workDir,s,t.agentSessionId??null,n||void 0,u,w?.roomOrigin);const k=h({kind:"message",sessionId:l,title:t.title||p(t.userGoal||t.summary||s),body:s});return e.appendManagerVisibleMarkdown(u,k,`worker-message:${_()}`),{status:200,body:{ok:!0,markdown:k,queued:!0,directRequested:!o,...m?{code:m}:{}}}}if(a==="/sessions/queue")return{status:200,body:{ok:!0,queue:e.getRuntime().chatQueue?.getSnapshot(l)}};if(a==="/sessions/queue/edit"||a==="/sessions/queue/delete"){const s=a.endsWith("/edit");return await e.dispatchReq({type:"req",id:`manager-queue-${s?"edit":"delete"}-${_()}`,method:s?"chat.queue.edit":"chat.queue.delete",params:{...T(),sessionId:l,queueMessageId:String(r.queueMessageId||r.messageId||""),...s?{text:String(r.message||r.text||"")}:{}}}),{status:200,body:{ok:!0,queue:e.getRuntime().chatQueue?.getSnapshot(l)}}}if(a==="/sessions/stop"||a==="/sessions/terminate"){if(t.status==="final"||t.status==="error"||t.status==="aborted"){const s=t.status==="final"?"completed":t.status==="error"?"failed":"stopped";return{status:200,body:{ok:!0,alreadyTerminal:!0,code:"worker_terminal",status:t.status,markdown:h({kind:s,sessionId:l,title:t.title||p(t.userGoal||t.summary||""),body:t.summary||"\u672C\u6B21\u6267\u884C\u5DF2\u7ECF\u7ED3\u675F\u3002"})}}}return await e.dispatchReq({type:"req",id:`manager-abort-${_()}`,method:"chat.abort",params:{...T(),sessionId:l}}),e.registry.updateWorker(l,{status:"aborted"}),{status:200,body:{ok:!0,markdown:h({kind:"stopped",sessionId:l,title:t.title||p(t.userGoal||t.summary||""),body:"\u5DF2\u505C\u6B62\u672C\u6B21\u6267\u884C\u3002"})}}}if(a==="/sessions/read"){const s=Number(r.limit||200),o=G(l,{limit:Math.max(s*20,s)});return{status:200,body:{ok:!0,messages:F(o,s),rawMessageCount:o.length}}}if(a==="/sessions/wait"){const s=Math.max(1e3,Math.min(Number(r.timeoutMs||6e5),6e5)),o=Date.now()+s;let d;for(;Date.now()<o&&(d=e.registry.listInboxEvents(u).find(n=>n.workerSessionId===l),!d);)await new Promise(n=>setTimeout(n,1e3));if(!d){const n=e.registry.getWorkerForManager(u,l);if(n&&(n.status==="final"||n.status==="error"||n.status==="aborted")){const k=n.status==="final"?"completed":n.status==="error"?"failed":"stopped";return{status:200,body:{ok:!0,timedOut:!1,alreadyTerminal:!0,status:n.status,markdown:h({kind:k,sessionId:l,title:n.title||p(n.userGoal||n.summary||""),body:n.summary||"\u672C\u6B21\u6267\u884C\u5DF2\u7ECF\u7ED3\u675F\u3002"})}}}return{status:200,body:{ok:!0,timedOut:!0,code:"wait_timed_out",status:n?.status??"running",nextAction:"Call worker read or worker wait again later with the same workerRef."}}}const m=h({kind:z(d.kind),sessionId:l,title:t.title||p(t.userGoal||t.summary||d.summary),body:d.summary});return e.registry.removeInboxEvents(u,[d.id]),{status:200,body:{ok:!0,timedOut:!1,markdown:m,event:d}}}return null}const H={"/workers/read":{action:"read",managerPath:"/sessions/read"},"/workers/send":{action:"send",managerPath:"/sessions/send"},"/workers/wait":{action:"wait",managerPath:"/sessions/wait"},"/workers/stop":{action:"stop",managerPath:"/sessions/stop"}};async function oe(e,a,r){if(!a.startsWith("/workers/"))return null;const i=O();if(!i.machineId||!i.machineToken&&!i.accessToken)throw new g("auth_required","Shennian CLI must be paired and logged in.","Pair this installation with shennian pair, then retry the same worker command.",401);if(a==="/workers/start"){const s=String(r.message||"").trim();if(!s)throw new g("worker_message_required","A Worker task message is required.","Provide --message or --message-file and retry worker start.");const o=String(r.agentType||r.agent||"");if(!o||!x(o))throw new g("runtime_unavailable",`Unsupported Worker Agent: ${o||"(empty)"}`,"Run shennian agent discover --compact --json and select a Worker-capable Agent type.");const d=await j({serverUrl:i.serverUrl,authToken:i.machineToken??i.accessToken}),m=N(d).find(y=>y.type===o);if(!m||m.supportsExternalWorkerTarget!==!0)throw new g("runtime_unavailable",`Worker Agent is not available on this machine: ${o}`,"Run shennian agent discover --compact --json and select a discovered Agent with supportsExternalWorkerTarget=true.");if(m.authState==="required"||m.authState==="error")throw new g("auth_required",`Worker Agent requires provider sign-in: ${o}`,"Sign in with the provider\u2019s own CLI on this computer, refresh discovery, and retry worker start.");const n=typeof r.modelId=="string"?r.modelId.trim():"";if(n&&m.models.length>0&&!m.models.some(y=>y.id===n))throw new g("model_unavailable",`The selected model was not returned by discovery for ${o}.`,`Run shennian agent discover --agent ${o} --json and choose a returned model id.`);const k=B(String(r.workDir||process.cwd())),f=`ephemeral_controller_${_()}`;e.registry.upsertManager({sessionId:f,agentSessionId:null,workDir:k,machineId:i.machineId,modelId:"manager",agentType:"manager",controllerKind:"ephemeral",status:"idle"});let v;try{v=await M(e,"/sessions/start",{managerSessionId:f,agentType:o,workDir:k,title:r.title,modelId:n||void 0,message:s},"")}catch(y){const A=e.registry.retireManager(f);for(const D of A)e.registry.completeRetiredWorker(f,D.sessionId);throw y}const S=v.body.session,W=typeof S?.sessionId=="string"?S.sessionId:"";if(!W)throw new Error("worker_start_invalid_response");const{workerRef:R,grant:E}=e.registry.issueWorkerRef({managerSessionId:f,workerSessionId:W,workDir:k,machineId:i.machineId,accountNamespaceId:i.accountNamespaceId??null});return{status:200,body:{ok:!0,workerId:W,workerRef:R,resolvedWorkDir:k,agentType:o,status:typeof S?.status=="string"?S.status:"running",expiresAt:E.expiresAt}}}const w=H[a];if(!w)throw new g("worker_command_unavailable","Unknown external Worker command.","Use worker start, read, send, wait, or stop. External Worker history cannot be listed.",404);const u=String(r.workerRef||r.ref||""),c=e.registry.resolveWorkerRefDetailed({workerRef:u,action:w.action,machineId:i.machineId,accountNamespaceId:i.accountNamespaceId??null});if(!c.ok){const s=c.reason==="expired"?"worker_ref_expired":c.reason==="revoked"?"worker_ref_revoked":"worker_ref_invalid";throw new g(s,`The Worker reference is ${c.reason} or belongs to another local scope.`,c.reason==="invalid"?"Use the exact workerRef returned by worker start. Lost references cannot be rediscovered.":"Start a new Worker if the task still needs to continue.",403)}const l=c.grant;let t;try{t=await M(e,w.managerPath,{...r,managerSessionId:l.managerSessionId,sessionId:l.workerSessionId},"")}catch(s){const o=s instanceof Error?s.message:String(s);throw o==="message is required"?new g("worker_message_required","A non-empty Worker message is required.","Provide --message or --message-file and retry worker send."):o.includes("not found")||o.includes("not registered")?new g("worker_ref_revoked","The Worker is no longer available in this controller scope.","Start a new Worker if the task still needs to continue.",410):new g("worker_command_failed",o,"Read the Worker state, correct the request, and retry once.")}if(!t)throw new g("worker_command_unavailable","The requested Worker operation is unavailable.","Use worker start, read, send, wait, or stop.",404);return{status:t.status,body:{...t.body,workerId:l.workerSessionId}}}export{g as EphemeralWorkerError,oe as handleEphemeralWorkerIpcRoute,M as handleWorkerIpcRoute};
@@ -1 +1 @@
1
- import{registerPiValidationSession as I}from"./pi-validation-session.js";import y from"node:fs/promises";import b from"node:path";import{randomUUID as C}from"node:crypto";import{isAvailableAgentType as E}from"@shennian/wire";import{createAgent as N}from"../../agents/adapter.js";import{buildManagedAgentEnv as S}from"../../agents/config-status.js";import{ManagerModeAdapter as T}from"../../agents/manager.js";import{NativeSessionLifecycleRegistry as O}from"../native-session-lifecycle.js";import{validateManagedAgentWorkDir as M}from"../../security/managed-agent-policy.js";const x="\u8BF7\u56DE\u590D hello\uFF0C\u7528\u4E8E\u786E\u8BA4\u4F60\u5DF2\u51C6\u5907\u597D\u3002",R=6e4,W=new O;async function L(e,r={}){try{return await P(e,r)}catch(t){const a=t instanceof Error?t.message:"";return{ok:!1,errorCode:a==="agent_validation_cleanup_failed"?a:v(t,"start")}}}async function P(e,r){if(!E(e.runtimeAdapter)||e.runtimeAdapter==="manager"||e.runtimeAdapter==="pi"||e.runtimeAdapter==="openclaw")return{ok:!1,errorCode:"agent_validation_runtime_unavailable"};if(e.sessionMode==="manager"&&!["codex","claude","pi-coding-agent"].includes(e.runtimeAdapter))return{ok:!1,errorCode:"agent_validation_manager_unavailable"};if(!b.isAbsolute(e.workDir.trim()))return{ok:!1,errorCode:"agent_validation_workdir_invalid"};let t;try{t=await(r.resolveWorkDir??U)(e.workDir)}catch(i){return{ok:!1,errorCode:V(i)}}if(!(r.validateWorkDir?await r.validateWorkDir(t):(await M(t,async()=>t)).ok))return{ok:!1,errorCode:"agent_validation_workdir_invalid"};const s=(r.createAdapter??N)(e.runtimeAdapter);if(!s)return{ok:!1,errorCode:"agent_validation_runtime_unavailable"};const o=e.sessionMode==="manager"?new T(s,e.runtimeAdapter):s;let f=`agent-validation-${C()}`,l,u="",m="",_=null,c=null;const D=new Promise(i=>{c=i}),p=i=>{i.agentSessionId&&(u=i.agentSessionId,r.ignoreNativeSession?.(e.runtimeAdapter,u)),i.state==="delta"&&!i.thinking&&i.text&&(m+=i.text),i.state==="final"?(i.text&&(m+=i.text),c?.({kind:"final"})):(i.state==="error"||i.state==="aborted")&&c?.({kind:"error",error:i.message??i.state})},w=i=>c?.({kind:"error",error:i});o.on("agentEvent",p),o.on("error",w);const h=async()=>{e.runtimeAdapter==="pi-coding-agent"&&(l=await(r.registerPiSession??I)(),f=l.sessionId),o.configure?.({sessionId:f,env:S(e.runtimeAdapter)});try{await o.start(f,t,null,{persistNativeSession:!1})}catch(n){return{ok:!1,errorCode:v(n,"start")}}const i=new Promise(n=>{_=setTimeout(()=>n({kind:"timeout"}),r.timeoutMs??R)}),k=o.send(x,e.modelId??void 0).then(()=>D).catch(n=>({kind:"error",error:n})),d=await Promise.race([k,i]);return d.kind==="timeout"?{ok:!1,errorCode:"agent_validation_response_timeout"}:d.kind==="error"?{ok:!1,errorCode:v(d.error,"response")}:m.toLowerCase().includes("hello")?{ok:!0}:{ok:!1,errorCode:"agent_validation_response_mismatch"}};let A,g=!1;try{A=await h()}finally{_&&clearTimeout(_),o.removeListener("agentEvent",p),o.removeListener("error",w);try{await o.stop()}catch{l&&(g=!0)}finally{await l?.dispose().catch(()=>{g=!0})}u&&(r.cleanupNativeSession??((k,d,n)=>{W.execute(k,d,{workDir:n}).catch(()=>{})}))(e.runtimeAdapter,u,t)}return g?{ok:!1,errorCode:"agent_validation_cleanup_failed"}:A}async function Y(e,r){const t=j(r.params);if(!t){e.client.sendRes({type:"res",id:r.id,ok:!1,error:"agent_validation_invalid_configuration"});return}const a=await L(t,{ignoreNativeSession:(s,o)=>{e.nativeFusion?.ignoreNativeSource(s,o)}});e.client.sendRes(a.ok?{type:"res",id:r.id,ok:!0,payload:{ready:!0}}:{type:"res",id:r.id,ok:!1,error:a.errorCode})}async function U(e){const r=e.trim();if(!r)throw Object.assign(new Error("workdir_required"),{code:"EINVAL"});const t=await y.realpath(r);if(!(await y.stat(t)).isDirectory())throw Object.assign(new Error("workdir_not_directory"),{code:"ENOTDIR"});return t}function V(e){const r=F(e);return r==="ENOENT"?"agent_validation_workdir_not_found":r==="ENOTDIR"?"agent_validation_workdir_not_directory":r==="EACCES"||r==="EPERM"?"agent_validation_workdir_unavailable":"agent_validation_workdir_invalid"}function v(e,r){const t=e instanceof Error?e.message:String(e);return t==="agent_validation_server_upgrade_required"?t:/model.*(?:not found|unavailable|unsupported|invalid)|unknown model/i.test(t)?"agent_validation_model_unavailable":/quota|insufficient.*(?:credit|balance)|billing|rate.?limit|\b429\b/i.test(t)?"agent_validation_quota_exceeded":/not found|enoent|command .* missing|is .* installed/i.test(t)?"agent_validation_runtime_unavailable":/unauth|authentication|not logged|login required|api key|token.*missing|forbidden|401|403/i.test(t)?"agent_validation_runtime_auth_required":/ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT|network|fetch failed|connection.*(?:failed|closed|refused)/i.test(t)?"agent_validation_network_failed":r==="start"?"agent_validation_start_failed":"agent_validation_response_failed"}function F(e){return typeof e=="object"&&e&&"code"in e?String(e.code):""}function j(e){const r=typeof e.runtimeAdapter=="string"?e.runtimeAdapter:"",t=typeof e.workDir=="string"?e.workDir:"",a=e.sessionMode==="manager"?"manager":"standard";return!E(r)||!t.trim()?null:{runtimeAdapter:r,workDir:t,sessionMode:a,modelId:typeof e.modelId=="string"?e.modelId:null}}export{Y as handleAgentWorkspaceValidation,L as validateAgentWorkspaceConfiguration};
1
+ import{registerPiValidationSession as I}from"./pi-validation-session.js";import y from"node:fs/promises";import b from"node:path";import{randomUUID as C}from"node:crypto";import{isAvailableAgentType as E}from"@shennian/wire";import{createAgent as N}from"../../agents/adapter.js";import{buildManagedAgentEnv as S}from"../../agents/config-status.js";import{ManagerModeAdapter as T}from"../../agents/manager.js";import{NativeSessionLifecycleRegistry as O}from"../native-session-lifecycle.js";import{validateManagedAgentWorkDir as M}from"../../security/managed-agent-policy.js";const x="\u8BF7\u56DE\u590D hello\uFF0C\u7528\u4E8E\u786E\u8BA4\u4F60\u5DF2\u51C6\u5907\u597D\u3002",R=6e4,W=new O;async function L(e,r={}){try{return await P(e,r)}catch(t){const a=t instanceof Error?t.message:"";return{ok:!1,errorCode:a==="agent_validation_cleanup_failed"?a:v(t,"start")}}}async function P(e,r){if(!E(e.runtimeAdapter)||e.runtimeAdapter==="manager"||e.runtimeAdapter==="pi"||e.runtimeAdapter==="openclaw")return{ok:!1,errorCode:"agent_validation_runtime_unavailable"};if(!b.isAbsolute(e.workDir.trim()))return{ok:!1,errorCode:"agent_validation_workdir_invalid"};let t;try{t=await(r.resolveWorkDir??U)(e.workDir)}catch(i){return{ok:!1,errorCode:V(i)}}if(!(r.validateWorkDir?await r.validateWorkDir(t):(await M(t,async()=>t)).ok))return{ok:!1,errorCode:"agent_validation_workdir_invalid"};const s=(r.createAdapter??N)(e.runtimeAdapter);if(!s)return{ok:!1,errorCode:"agent_validation_runtime_unavailable"};const o=e.sessionMode==="manager"?new T(s,e.runtimeAdapter):s;let f=`agent-validation-${C()}`,l,u="",m="",_=null,c=null;const D=new Promise(i=>{c=i}),p=i=>{i.agentSessionId&&(u=i.agentSessionId,r.ignoreNativeSession?.(e.runtimeAdapter,u)),i.state==="delta"&&!i.thinking&&i.text&&(m+=i.text),i.state==="final"?(i.text&&(m+=i.text),c?.({kind:"final"})):(i.state==="error"||i.state==="aborted")&&c?.({kind:"error",error:i.message??i.state})},w=i=>c?.({kind:"error",error:i});o.on("agentEvent",p),o.on("error",w);const h=async()=>{e.runtimeAdapter==="pi-coding-agent"&&(l=await(r.registerPiSession??I)(),f=l.sessionId),o.configure?.({sessionId:f,env:S(e.runtimeAdapter)});try{await o.start(f,t,null,{persistNativeSession:!1})}catch(n){return{ok:!1,errorCode:v(n,"start")}}const i=new Promise(n=>{_=setTimeout(()=>n({kind:"timeout"}),r.timeoutMs??R)}),k=o.send(x,e.modelId??void 0).then(()=>D).catch(n=>({kind:"error",error:n})),d=await Promise.race([k,i]);return d.kind==="timeout"?{ok:!1,errorCode:"agent_validation_response_timeout"}:d.kind==="error"?{ok:!1,errorCode:v(d.error,"response")}:m.toLowerCase().includes("hello")?{ok:!0}:{ok:!1,errorCode:"agent_validation_response_mismatch"}};let A,g=!1;try{A=await h()}finally{_&&clearTimeout(_),o.removeListener("agentEvent",p),o.removeListener("error",w);try{await o.stop()}catch{l&&(g=!0)}finally{await l?.dispose().catch(()=>{g=!0})}u&&(r.cleanupNativeSession??((k,d,n)=>{W.execute(k,d,{workDir:n}).catch(()=>{})}))(e.runtimeAdapter,u,t)}return g?{ok:!1,errorCode:"agent_validation_cleanup_failed"}:A}async function Y(e,r){const t=j(r.params);if(!t){e.client.sendRes({type:"res",id:r.id,ok:!1,error:"agent_validation_invalid_configuration"});return}const a=await L(t,{ignoreNativeSession:(s,o)=>{e.nativeFusion?.ignoreNativeSource(s,o)}});e.client.sendRes(a.ok?{type:"res",id:r.id,ok:!0,payload:{ready:!0}}:{type:"res",id:r.id,ok:!1,error:a.errorCode})}async function U(e){const r=e.trim();if(!r)throw Object.assign(new Error("workdir_required"),{code:"EINVAL"});const t=await y.realpath(r);if(!(await y.stat(t)).isDirectory())throw Object.assign(new Error("workdir_not_directory"),{code:"ENOTDIR"});return t}function V(e){const r=F(e);return r==="ENOENT"?"agent_validation_workdir_not_found":r==="ENOTDIR"?"agent_validation_workdir_not_directory":r==="EACCES"||r==="EPERM"?"agent_validation_workdir_unavailable":"agent_validation_workdir_invalid"}function v(e,r){const t=e instanceof Error?e.message:String(e);return t==="agent_validation_server_upgrade_required"?t:/model.*(?:not found|unavailable|unsupported|invalid)|unknown model/i.test(t)?"agent_validation_model_unavailable":/quota|insufficient.*(?:credit|balance)|billing|rate.?limit|\b429\b/i.test(t)?"agent_validation_quota_exceeded":/not found|enoent|command .* missing|is .* installed/i.test(t)?"agent_validation_runtime_unavailable":/unauth|authentication|not logged|login required|api key|token.*missing|forbidden|401|403/i.test(t)?"agent_validation_runtime_auth_required":/ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT|network|fetch failed|connection.*(?:failed|closed|refused)/i.test(t)?"agent_validation_network_failed":r==="start"?"agent_validation_start_failed":"agent_validation_response_failed"}function F(e){return typeof e=="object"&&e&&"code"in e?String(e.code):""}function j(e){const r=typeof e.runtimeAdapter=="string"?e.runtimeAdapter:"",t=typeof e.workDir=="string"?e.workDir:"",a=e.sessionMode==="manager"?"manager":"standard";return!E(r)||!t.trim()?null:{runtimeAdapter:r,workDir:t,sessionMode:a,modelId:typeof e.modelId=="string"?e.modelId:null}}export{Y as handleAgentWorkspaceValidation,L as validateAgentWorkspaceConfiguration};
@@ -1,2 +1,2 @@
1
- import{buildApprovalPendingPayload as b}from"@shennian/wire";import{createHash as x}from"node:crypto";import{reportLog as $}from"../../log-reporter.js";import{registerInternalNativeSource as k}from"../../native-fusion/source-visibility.js";import{requestSessionIndexPublish as f}from"../index-publisher.js";import{appendMessage as g,recordSession as p,upsertMessage as M}from"../store.js";import{writeToolDetail as w}from"../tool-detail-store.js";import{refreshMachineAgents as y}from"./agents.js";function S(a,e){const n=e?.managedRoomContextToken;!e||!n||(e.managedRoomContextToken=null,a.revokeManagedRoomContextToken?.(n))}function D(a){const e=a.indexOf(`
2
- `),n=e>0?Math.min(e,80):Math.min(a.length,80);return a.slice(0,n)}function q(a,e,n){return`${a}:${x("sha256").update(`${e}\0${n??""}`).digest("hex")}`}function _(a,e,n={}){if(a.state==="tool-call"||a.state==="tool-result"){const u={runId:a.runId,sourceSeq:a.seq};return{state:a.state,runId:a.runId,seq:a.seq,sessionId:e,detailRef:u,status:a.state==="tool-call"?"running":"completed",detailAvailable:!0,...a.durationMs!==void 0?{durationMs:a.durationMs}:{},...a.name?{name:a.name}:{},...a.source?{source:a.source}:{},...a.agentSessionId?{agentSessionId:a.agentSessionId}:{},...n}}return{...a,sessionId:e,...n}}const P=3e4;function C(a){return a.state==="heartbeat"?a.runPhase??null:a.state==="tool-call"||a.state==="tool-result"?"tool_running":a.state==="approval-pending"?"waiting_approval":a.state==="delta"?a.thinking?"thinking":"streaming_text":a.state==="init"||a.state==="start"?"thinking":null}function R(a,e){return a!=="manager"?a:e==="claude"?"claude":"codex"}function Q(a,e,n,u){let I=null;function h(t,r={}){a.client.sendAgentEvent({type:"event",event:"agent",payload:_(t,e,r),seq:t.seq,id:`agent-evt-${t.runId}-${t.seq}`})}function T(t){t?.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null)}function A(t){if(!t.currentRunId||!t.currentRunPhase)return;const r=t.heartbeatSeq++;a.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:t.currentRunId,seq:r,runPhase:t.currentRunPhase},seq:r,id:`agent-heartbeat-${t.currentRunId}-${r}-${Date.now()}`})}function E(t){!t||t.heartbeatTimer||(t.heartbeatTimer=setInterval(()=>{A(t)},P),t.heartbeatTimer.unref?.())}function i(t){const r=t?.pendingTextEvent;!t||!r||!r.text||(g(e,{id:`agent-${r.runId}-${r.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:r.text}),f(a.client),h({state:"delta",runId:r.runId,seq:r.seq,text:r.text,thinking:r.thinking||void 0}),t.pendingTextEvent=null)}u.on("agentEvent",t=>{t.errorCode==="acp_auth_required"&&y(a).catch(()=>{});const r=a.sessions.get(e),c=C(t),d=t.state==="final"||t.state==="error"||t.state==="aborted";if(r&&(r.nextEventSeq=t.seq+1,d||(r.currentRunId=t.runId),!d&&c&&(r.currentRunPhase=c,E(r)),t.agentSessionId&&(r.agentSessionId=t.agentSessionId),r.journalRunId)){const o={agentSessionId:t.agentSessionId??r.agentSessionId,providerRunId:t.runId||null,lastObservedEventSeq:t.seq,...d?{}:{phase:c==="waiting_approval"?"waiting_approval":"running"}};a.runJournal?.update(e,r.journalRunId,o)}if(a.managerRuntime?.noteAgentEvent(e,t),t.state==="delta"&&t.thinking)return;t.state!=="delta"&&$({level:"info",sessionId:e,wsEvent:`agent.${t.state}`,wsDirection:"out",metadata:{runId:t.runId,seq:t.seq,agentType:n}});let s=!1;if(t.state==="tool-call"||t.state==="tool-result"){const o=t.toolCallId?`agent-tool-${x("sha256").update(`${t.runId}\0${t.toolCallId}`).digest("hex")}`:`agent-${t.runId}-${t.seq}`;w({sessionId:e,toolCallId:o,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?M:g)(e,{id:o,sessionId:e,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:t.state==="tool-call"?"tool_use":"tool_result",name:t.name,toolCallId:o,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"})}),s=!0}else t.state==="approval-pending"?(g(e,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:b({...t.approval,permission:t.permission})}),s=!0,r&&p({sessionId:e,agentType:n,workDir:r.workDir,agentSessionId:r.agentSessionId,attentionId:q("approval",t.runId,t.seq)})):(t.state==="error"||t.state==="aborted")&&t.message&&(g(e,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:t.message}),s=!0);s&&f(a.client);const l=`${e}:${t.runId}`;if(t.state==="delta"&&!t.thinking&&t.text&&a.runTextAcc.set(l,(a.runTextAcc.get(l)??"")+t.text),t.agentSessionId&&t.agentSessionId!==I&&(r?.nativeSessionVisibility==="internal"&&k(e,R(n,t.source)??n,t.agentSessionId),I=t.agentSessionId,a.nativeFusion?.noteManagedSourceSession(e,R(n,t.source),t.agentSessionId),r&&p({sessionId:e,agentType:n,workDir:r.workDir,agentSessionId:t.agentSessionId}),a.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:n,agentSessionId:t.agentSessionId}}})),t.state==="delta"){const o=t.text??"";if(!o)return;r?.pendingTextEvent&&r.pendingTextEvent.runId!==t.runId&&i(r),r&&!r.pendingTextEvent&&(r.pendingTextEvent={runId:t.runId,seq:t.seq,text:"",thinking:!1}),r?.pendingTextEvent&&(r.pendingTextEvent.text+=o,r.pendingTextEvent.seq=t.seq);return}let m={};if(t.state==="final"){i(r);const o=a.runTextAcc.get(l)??"";o&&(m={messageSummary:D(o)}),a.runTextAcc.delete(l)}else t.state==="error"||t.state==="aborted"?(i(r),a.runTextAcc.delete(l)):(t.state==="tool-call"||t.state==="tool-result"||t.state==="approval-pending")&&i(r);d&&r?.currentRunId===t.runId&&(S(a,r),r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,T(r),a.chatQueue?.noteTerminal(e),p({sessionId:e,agentType:n,workDir:r.workDir,agentSessionId:r.agentSessionId,status:t.state==="final"?"completed":"failed",attentionId:q("terminal",t.runId),lastActivityAt:new Date().toISOString()}),f(a.client),r.journalRunId&&(a.runJournal?.remove(e,r.journalRunId),a.recoveryBarrier?.end(e),r.journalRunId=null)),h(t,m)}),u.on("error",t=>{console.error(`[chat.send] adapter error sessionId=${e} agentType=${n}: ${t.message}`);const r=a.sessions.get(e);S(a,r),r?.journalRunId&&(a.runJournal?.update(e,r.journalRunId,{phase:"terminal"}),a.runJournal?.remove(e,r.journalRunId),a.recoveryBarrier?.end(e),r.journalRunId=null),a.sessions.delete(e),a.chatQueue?.noteTerminal(e),a.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:e,message:t.message,runId:"",seq:0}})})}export{Q as bindAdapterEvents,R as getNativeSourceAgentType};
1
+ import{buildApprovalPendingPayload as $}from"@shennian/wire";import{createHash as x}from"node:crypto";import{AgentEventTerminalGate as k}from"../../agents/event-terminal-gate.js";import{reportLog as w}from"../../log-reporter.js";import{registerInternalNativeSource as M}from"../../native-fusion/source-visibility.js";import{requestSessionIndexPublish as f}from"../index-publisher.js";import{appendMessage as g,recordSession as p,upsertMessage as y}from"../store.js";import{writeToolDetail as D}from"../tool-detail-store.js";import{refreshMachineAgents as _}from"./agents.js";function S(a,e){const n=e?.managedRoomContextToken;!e||!n||(e.managedRoomContextToken=null,a.revokeManagedRoomContextToken?.(n))}function P(a){const e=a.indexOf(`
2
+ `),n=e>0?Math.min(e,80):Math.min(a.length,80);return a.slice(0,n)}function q(a,e,n){return`${a}:${x("sha256").update(`${e}\0${n??""}`).digest("hex")}`}function C(a,e,n={}){if(a.state==="tool-call"||a.state==="tool-result"){const u={runId:a.runId,sourceSeq:a.seq};return{state:a.state,runId:a.runId,seq:a.seq,sessionId:e,detailRef:u,status:a.state==="tool-call"?"running":"completed",detailAvailable:!0,...a.durationMs!==void 0?{durationMs:a.durationMs}:{},...a.name?{name:a.name}:{},...a.source?{source:a.source}:{},...a.agentSessionId?{agentSessionId:a.agentSessionId}:{},...n}}return{...a,sessionId:e,...n}}const j=3e4;function O(a){return a.state==="heartbeat"?a.runPhase??null:a.state==="tool-call"||a.state==="tool-result"?"tool_running":a.state==="approval-pending"?"waiting_approval":a.state==="delta"?a.thinking?"thinking":"streaming_text":a.state==="init"||a.state==="start"?"thinking":null}function R(a,e){return a!=="manager"?a:e==="claude"?"claude":"codex"}function Y(a,e,n,u){let I=null;const T=new k;function m(t,r={}){a.client.sendAgentEvent({type:"event",event:"agent",payload:C(t,e,r),seq:t.seq,id:`agent-evt-${t.runId}-${t.seq}`})}function A(t){t?.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null)}function E(t){if(!t.currentRunId||!t.currentRunPhase)return;const r=t.heartbeatSeq++;a.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:t.currentRunId,seq:r,runPhase:t.currentRunPhase},seq:r,id:`agent-heartbeat-${t.currentRunId}-${r}-${Date.now()}`})}function b(t){!t||t.heartbeatTimer||(t.heartbeatTimer=setInterval(()=>{E(t)},j),t.heartbeatTimer.unref?.())}function i(t){const r=t?.pendingTextEvent;!t||!r||!r.text||(g(e,{id:`agent-${r.runId}-${r.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:r.text}),f(a.client),m({state:"delta",runId:r.runId,seq:r.seq,text:r.text,thinking:r.thinking||void 0}),t.pendingTextEvent=null)}u.on("agentEvent",t=>{if(!T.accept(t))return;t.errorCode==="acp_auth_required"&&_(a).catch(()=>{});const r=a.sessions.get(e),c=O(t),d=t.state==="final"||t.state==="error"||t.state==="aborted";if(r&&(r.nextEventSeq=t.seq+1,d||(r.currentRunId=t.runId),!d&&c&&(r.currentRunPhase=c,b(r)),t.agentSessionId&&(r.agentSessionId=t.agentSessionId),r.journalRunId)){const o={agentSessionId:t.agentSessionId??r.agentSessionId,providerRunId:t.runId||null,lastObservedEventSeq:t.seq,...d?{}:{phase:c==="waiting_approval"?"waiting_approval":"running"}};a.runJournal?.update(e,r.journalRunId,o)}if(a.managerRuntime?.noteAgentEvent(e,t),t.state==="delta"&&t.thinking)return;t.state!=="delta"&&w({level:"info",sessionId:e,wsEvent:`agent.${t.state}`,wsDirection:"out",metadata:{runId:t.runId,seq:t.seq,agentType:n}});let s=!1;if(t.state==="tool-call"||t.state==="tool-result"){const o=t.toolCallId?`agent-tool-${x("sha256").update(`${t.runId}\0${t.toolCallId}`).digest("hex")}`:`agent-${t.runId}-${t.seq}`;D({sessionId:e,toolCallId:o,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?y:g)(e,{id:o,sessionId:e,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:t.state==="tool-call"?"tool_use":"tool_result",name:t.name,toolCallId:o,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"})}),s=!0}else t.state==="approval-pending"?(g(e,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:$({...t.approval,permission:t.permission})}),s=!0,r&&p({sessionId:e,agentType:n,workDir:r.workDir,agentSessionId:r.agentSessionId,attentionId:q("approval",t.runId,t.seq)})):(t.state==="error"||t.state==="aborted")&&t.message&&(g(e,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:t.message}),s=!0);s&&f(a.client);const l=`${e}:${t.runId}`;if(t.state==="delta"&&!t.thinking&&t.text&&a.runTextAcc.set(l,(a.runTextAcc.get(l)??"")+t.text),t.agentSessionId&&t.agentSessionId!==I&&(r?.nativeSessionVisibility==="internal"&&M(e,R(n,t.source)??n,t.agentSessionId),I=t.agentSessionId,a.nativeFusion?.noteManagedSourceSession(e,R(n,t.source),t.agentSessionId),r&&p({sessionId:e,agentType:n,workDir:r.workDir,agentSessionId:t.agentSessionId}),a.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:n,agentSessionId:t.agentSessionId}}})),t.state==="delta"){const o=t.text??"";if(!o)return;r?.pendingTextEvent&&r.pendingTextEvent.runId!==t.runId&&i(r),r&&!r.pendingTextEvent&&(r.pendingTextEvent={runId:t.runId,seq:t.seq,text:"",thinking:!1}),r?.pendingTextEvent&&(r.pendingTextEvent.text+=o,r.pendingTextEvent.seq=t.seq);return}let h={};if(t.state==="final"){i(r);const o=a.runTextAcc.get(l)??"";o&&(h={messageSummary:P(o)}),a.runTextAcc.delete(l)}else t.state==="error"||t.state==="aborted"?(i(r),a.runTextAcc.delete(l)):(t.state==="tool-call"||t.state==="tool-result"||t.state==="approval-pending")&&i(r);d&&r?.currentRunId===t.runId&&(S(a,r),r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,A(r),a.chatQueue?.noteTerminal(e),p({sessionId:e,agentType:n,workDir:r.workDir,agentSessionId:r.agentSessionId,status:t.state==="final"?"completed":"failed",attentionId:q("terminal",t.runId),lastActivityAt:new Date().toISOString()}),f(a.client),r.journalRunId&&(a.runJournal?.remove(e,r.journalRunId),a.recoveryBarrier?.end(e),r.journalRunId=null)),m(t,h)}),u.on("error",t=>{console.error(`[chat.send] adapter error sessionId=${e} agentType=${n}: ${t.message}`);const r=a.sessions.get(e);S(a,r),r?.journalRunId&&(a.runJournal?.update(e,r.journalRunId,{phase:"terminal"}),a.runJournal?.remove(e,r.journalRunId),a.recoveryBarrier?.end(e),r.journalRunId=null),a.sessions.delete(e),a.chatQueue?.noteTerminal(e),a.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:e,message:t.message,runId:"",seq:0}})})}export{Y as bindAdapterEvents,R as getNativeSourceAgentType};
@@ -1 +1 @@
1
- import{NativeSourceDeletionStore as U}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as V}from"./queue-retry.js";import{findDuplicateQueuedMessage as Q,stripLocalQueueFields as _,takeCompatibleBatch as X,formatQueuedBatch as P,dedupeQueuedAttachments as B,classifyQueueError as F,isControllableQueueMessage as v,takeControllableMessage as Y,normalizeQueuedModelId as W,resolveManagedRoomNativeSessionId as T,managedDirectTurnOptions as L}from"./queue-helpers.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as Z}from"@shennian/wire";import{isCodexThreadTailInterrupted as ee}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as te}from"./projection.js";import{assertFinishedAttachmentReferences as j,markAttachmentReferences as O}from"./attachment-transfer-store.js";import{appendMessage as N,getMessageDeliveryStatus as x,listSessionRecords as z,recordSession as $,tombstoneMessage as J,upsertMessage as C}from"./store.js";import{requestSessionIndexPublish as w}from"./index-publisher.js";import{clearPersistedManagerRoomOrigins as se,purgeQueuedSessionData as ne,readQueue as m,writeQueue as y}from"./queue-storage.js";import{getNativeSourceAgentType as ie}from"./handlers/chat-events.js";import{parsePersonalAttachmentReferences as K}from"./attachment-reference.js";import{getPersonalSyncIdentity as b}from"../personal-sync/epoch.js";import{getDaemonInstallationId as A}from"../personal-sync/identity.js";import{isTrustedManagerEvent as ae}from"./manager-event-authority.js";import{TerminalQueuedChatDispatchError as D,nowIso as E,queueEnvelope as R,queueMessageFromParams as G}from"./queue-message.js";import{purgeQueuedSessionData as xe}from"./queue-storage.js";import{TerminalQueuedChatDispatchError as qe}from"./queue-message.js";class Re{opts;validateScheduledTarget;async enqueueScheduled(e){return this.handleEnqueue(e,!0)}draining=new Set;promoting=new Set;disposed=!1;retries=new V(e=>{this.wakeSession(e)});deletions=new U;inFlightMessageIds=new Map;deferredLogState=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,se(),setTimeout(()=>this.drainIdleQueues(),0).unref?.()}stop(){this.disposed=!0,this.retries.stop()}getSnapshot(e){const s=m().sessions[e]??[],t=this.inFlightMessageIds.get(e),i=this.isImmediateSendAvailable(e),n=s.filter(a=>v(a)).map(a=>({..._(a),pendingState:a.externalQueuedSubmissionId?"provider_queued":t?.has(a.id)?"submitting":"waiting",canDelete:!a.externalQueuedSubmissionId&&!t?.has(a.id),canPromote:!a.externalQueuedSubmissionId&&!t?.has(a.id)&&i}));return{...b(A()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(_),controllablePending:s.filter(a=>v(a)&&!a.externalQueuedSubmissionId&&!t?.has(a.id)).map(_),visiblePending:n}}isImmediateSendAvailable(e){const s=this.opts.getRuntime(),t=s.sessions.get(e);return t?.currentRunId?t.journalRunId?s.runJournal?.get(e,t.journalRunId)?.deliveryMode==="direct":!1:!0}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const s=this.opts.getRuntime(),t=m(),i=new Set,n=new Map;let a=!1;for(const d of e){const r=d.params;if(this.deletions.hasSession(r.sessionId)){delete t.sessions[r.sessionId],a=!0;continue}if(!r.sessionId||!r.text||!r.agentType||!r.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=G({...b(A()),...r});u.origin="room",u.managedRoom=d.context,u.trigger={kind:"room_activation",...d.context};const l=t.sessions[r.sessionId]??[],g=Q(l,u),f=u.clientMessageId??u.id,k=x(r.sessionId,f);if(!g&&k.state==="not_found")l.push(u),t.sessions[r.sessionId]=l,n.set(`${u.sessionId}\0${f}`,u),i.add(r.sessionId),a=!0;else if(g&&k.state==="not_found")n.set(`${g.sessionId}\0${f}`,g),i.add(r.sessionId);else if(g&&k.state==="queued")i.add(r.sessionId);else if(g){const M=l.filter(S=>S.id!==g.id);M.length?t.sessions[r.sessionId]=M:delete t.sessions[r.sessionId],a=!0}}a&&y(t);let o;for(const d of i)try{for(const r of n.values())r.sessionId===d&&(N(d,R(r),"queued"),$({sessionId:d,agentType:r.agentType,sessionMode:r.sessionMode,managerConfig:r.managerConfig,workDir:r.workDir,agentSessionId:T(s,r),modelId:r.modelId}));this.logLifecycle("enqueue.persisted",d,{origin:"room",pendingCount:m().sessions[d]?.length??0}),this.broadcast(d),this.wakeSession(d)}catch(r){o=r,this.logLifecycle("enqueue.commit-failed",d,{failureClass:F(r instanceof Error?r.message:"","canonical_commit_failed")},"error"),this.retries.schedule(d,!0)}if(i.size&&w(s.client),o)throw o}async handleEnqueue(e,s=!1){const t=this.opts.getRuntime(),i=e.params;if(i.scheduledTask&&!s)throw new Error("schedule_source_untrusted");if((i.managerEventId||i.managerEventRoomOrigin||i.managedWorkerParentSessionId||i.managerWorkerRoomOrigin||e.params.managerWorkerNativeFull||e.params.managedRecoverySource)&&!ae(e))throw new Error("manager_event_source_untrusted");const n=!!(s&&i.scheduledTask?.bindingId);if(te(i.sessionListProjection),!i.sessionId||!i.text||!i.agentType||!i.workDir)return t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const a=L(i.managerEventId?{roomOrigin:i.managerEventRoomOrigin}:null,!!i.managedAgentDirect),o=i.managedAgentDirect||n?await this.prepareManagedAgentDirectTurn?.(i.sessionId,a):null;if((i.managedAgentDirect||n)&&!o)throw new Error("managed_agent_context_unavailable");const d=o?z().find(H=>H.sessionId===i.sessionId):null,r=o?{...i,...o,agentType:o.agentType,sessionMode:o.sessionMode,managerConfig:o.managerConfig,workDir:o.workDir,agentSessionId:o.agentSessionId?.startsWith("managed:")?d?.agentSessionId&&!d.agentSessionId.startsWith("managed:")?d.agentSessionId:null:o.agentSessionId,modelId:d?.modelId??void 0,systemPrompt:o.systemPrompt,externalChannel:i.managedAgentDirect?null:i.externalChannel,replyTarget:i.managedAgentDirect?null:i.replyTarget,managerDefaultWorkerAgentType:o.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:o.managerDefaultWorkerModelId}:i,u=t.resolvePath(r.workDir),l=K(r.attachments);l?.length&&j(u,l);const g={text:r.text,attachments:l,localized:!1},f=t.sessions.get(r.sessionId);if(r.agentType==="codex"&&r.agentSessionId&&f?.currentRunId?.startsWith("dispatch:")&&ee(r.agentSessionId))return t.client.sendRes({type:"res",id:e.id,ok:!1,error:Z}),!1;o?.managedRoomContextToken&&t.revokeManagedRoomContextToken?.(o.managedRoomContextToken);const k=await this.isSessionBusy(r.sessionId),M=m(),S=M.sessions[r.sessionId]??[],h=G({...r,text:g.text,attachments:g.attachments,systemPrompt:o?null:r.systemPrompt});o&&(i.managedAgentDirect&&(h.managedAgentDirect=!0),h.trigger?.kind!=="manager_event"&&h.trigger?.kind!=="scheduled"&&(h.trigger={kind:"managed_direct"}));const q=h.clientMessageId??h.id,c=x(r.sessionId,q);if(c.state!=="not_found")return t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:c.state==="queued",queueMessageId:h.id,...c,queue:this.getSnapshot(r.sessionId)}}),!0;const I=Q(S,h);I||(M.sessions[r.sessionId]=[...S,h],y(M));const p=I??h;return $({sessionId:p.sessionId,agentType:p.agentType,sessionMode:p.sessionMode,managerConfig:p.managerConfig,workDir:p.workDir,agentSessionId:p.agentSessionId,modelId:p.modelId}),N(p.sessionId,R(p),"queued"),O(t.resolvePath(p.workDir),p.attachments??[]),w(t.client),this.logLifecycle("enqueue.persisted",r.sessionId,{origin:r.origin??"app",queueMessageId:p.id,pendingCount:(m().sessions[r.sessionId]??[]).length}),this.broadcast(r.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:I?.id??h.id,...x(r.sessionId,I?.clientMessageId??I?.id??h.clientMessageId??h.id),queue:this.getSnapshot(r.sessionId),...g.localized?{localizedAttachments:!0}:{}}}),k?this.retries.schedule(r.sessionId):this.wakeSession(r.sessionId),!0}async handleGet(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleEdit(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],a=n.findIndex(r=>r.id===t.queueMessageId);if(a<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!v(n[a])||n[a].externalQueuedSubmissionId||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId)){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=K(t.attachments),d=s.resolvePath(n[a].workDir);o?.length&&j(d,o),n[a]={...n[a],text:t.text,attachments:o,updatedAt:E()},i.sessions[t.sessionId]=n,y(i),C(t.sessionId,R(n[a]),"queued"),n[a].attachments?.length&&O(d,n[a].attachments??[]),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],a=n.find(d=>d.id===t.queueMessageId);if(a&&(!v(a)||a.externalQueuedSubmissionId||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId))){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=n.filter(d=>d.id!==t.queueMessageId);if(o.length===n.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}o.length?i.sessions[t.sessionId]=o:delete i.sessions[t.sessionId],y(i),J(t.sessionId,a?.clientMessageId??a?.id??t.queueMessageId),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handlePromote(e){const s=this.opts.getRuntime(),t=e.params,i=a=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:a});if(!t.sessionId||!t.queueMessageId){i("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){i("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let n=[];try{for(;this.draining.has(t.sessionId);)await new Promise(d=>setTimeout(d,5));if(n=Y(m().sessions[t.sessionId]??[],t.queueMessageId),!n.length){i("Queued message not found");return}if(n[0].externalQueuedSubmissionId||!this.isImmediateSendAvailable(t.sessionId)){i("queue_immediate_send_unavailable");return}this.inFlightMessageIds.set(t.sessionId,new Set(n.map(d=>d.id)));const a=s.sessions.get(t.sessionId);let o="idle";if(a?.currentRunId)if(this.canInjectActiveBatch(a,n)){const d=n.length===1?n[0].text:P(n),r=n[0].clientMessageId??n[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:n[0].agentType,sourceAgentType:ie(n[0].agentType,n[0].modelId),canonicalMessageId:r,sourceSessionKey:a.agentSessionId,text:d,managedEchoPolicy:"suppress_following"});const u=Date.now();try{await a.adapter.injectActiveInput({text:d,attachments:B(n.flatMap(l=>l.attachments??[])),clientMessageId:r})}catch(l){throw s.nativeFusion?.cancelManagedSend(r),l}for(const l of n)C(t.sessionId,{...R(l),ts:u},"executing");this.removeBatch(t.sessionId,n),o="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId);const d=await this.dispatchQueuedMessages(n);d.deliveryMode==="external_owner_queue"?this.markExternalQueued(n,d.queuedSubmissionId):(this.removeBatch(t.sessionId,n),o="restarted")}else{const d=await this.dispatchQueuedMessages(n);d.deliveryMode==="external_owner_queue"?this.markExternalQueued(n,d.queuedSubmissionId):this.removeBatch(t.sessionId,n)}w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId),promotedMessageIds:n.map(d=>d.id),mode:o}})}catch(a){i(a instanceof Error?a.message:"queue_promotion_failed"),this.broadcast(t.sessionId)}finally{this.inFlightMessageIds.delete(t.sessionId),this.promoting.delete(t.sessionId),m().sessions[t.sessionId]?.length&&this.wakeSession(t.sessionId)}}noteTerminal(e){const s=m().sessions[e]?.find(t=>!!t.externalQueuedSubmissionId);s&&this.noteExternalClaim(e,s.clientMessageId??s.id),this.wakeSession(e)}purgeSession(e){ne(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const o=m().sessions[e]?.length??0;return this.purgeSession(e),o}const s=m(),t=s.sessions[e]??[],i=this.inFlightMessageIds.get(e)??new Set,n=t.filter(o=>o.managedRoom&&!i.has(o.id));if(n.length===0)return 0;const a=t.filter(o=>!n.includes(o));a.length?s.sessions[e]=a:delete s.sessions[e],y(s);for(const o of n)J(e,o.clientMessageId??o.id);return w(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:n.length,pendingCount:a.length}),this.broadcast(e),n.length}wakeSession(e){this.drainNext(e).catch(()=>{this.logLifecycle("drain.retry-required",e,{},"error"),this.retries.schedule(e,!0)})}drainIdleQueues(){if(this.disposed)return;const e=m();for(const s of Object.keys(e.sessions))this.wakeSession(s)}async drainNext(e){if(this.disposed||this.draining.has(e)||this.promoting.has(e))return;this.draining.add(e);let s=!1,t=!1,i=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const l=m().sessions[e]?.length??0,g=this.deferredLogState.get(e),f=Date.now();(!g||g.pendingCount!==l||f-g.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:l}),this.deferredLogState.set(e,{at:f,pendingCount:l}));return}this.deferredLogState.delete(e);const n=m(),a=n.sessions[e]??[],o=a.filter(l=>{const g=l.clientMessageId??l.id,f=x(e,g).state;return f==="queued"||f==="not_found"});if(o.length!==a.length&&(o.length?n.sessions[e]=o:delete n.sessions[e],y(n),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:a.length-o.length,pendingCount:o.length})),o.some(l=>!!l.externalQueuedSubmissionId)){this.broadcast(e);return}const d=X(o);if(d.length===0){this.broadcast(e);return}i=d,this.inFlightMessageIds.set(e,new Set(d.map(l=>l.id))),this.logLifecycle("dispatch.start",e,{batchCount:d.length,queueMessageId:d[0].id,origin:d[0].origin??"app"}),this.broadcast(e);for(const l of d)C(e,R(l),"queued");const r=await this.dispatchQueuedMessages(d);if(r.deliveryMode==="external_owner_queue"){this.markExternalQueued(d,r.queuedSubmissionId),this.logLifecycle("dispatch.external-queued",e,{batchCount:d.length,queueMessageId:d[0].id}),this.broadcast(e);return}const u=this.removeBatch(e,d);this.logLifecycle("dispatch.accepted",e,{batchCount:d.length,queueMessageId:d[0].id,pendingCount:u}),this.deferredLogState.delete(e),s=u>0,this.broadcast(e)}catch(n){const a=F(n instanceof Error?n.message:String(n),n instanceof D?"terminal_dispatch_failed":"transient_dispatch_failed"),o=i[0],d=!!(o&&!o.trigger&&!o.clientMessageId&&Date.now()-Date.parse(o.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(n instanceof D||a==="managed_claim_source_binding_conflict"||d){const u=this.removeBatch(e,i);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:i.length,queueMessageId:i[0]?.id,pendingCount:u,failureClass:d?"stale_legacy_managed_worker_dispatch_failure":a},"error"),s=u>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:a},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e);const n=m().sessions[e]??[];n.length?(this.broadcast(e),n.some(a=>!!a.externalQueuedSubmissionId)?this.retries.cancel(e):s?queueMicrotask(()=>this.wakeSession(e)):this.retries.schedule(e,t)):this.retries.cancel(e)}}noteExternalClaim(e,s){const t=m(),i=t.sessions[e]??[],n=i.filter(a=>(a.clientMessageId??a.id)!==s);n.length!==i.length&&(n.length?t.sessions[e]=n:delete t.sessions[e],y(t),this.broadcast(e))}canInjectActiveBatch(e,s){if(!e.adapter.supportsActiveInput||!e.adapter.injectActiveInput||!e.journalRunId)return!1;const t=this.opts.getRuntime().runJournal?.get(s[0].sessionId,e.journalRunId),i=s[0].trigger?.kind==="managed_direct"||s[0].managedAgentDirect?null:"personal";return!!(i&&s[0].systemPrompt==null&&t&&t.source===i&&t.deliveryMode==="direct"&&t.agentType===s[0].agentType&&t.workDir===e.workDir&&t.agentSessionId===e.agentSessionId&&t.modelId===(s[0].modelId??null)&&t.reasoningEffort===(s[0].reasoningEffort??null))}removeBatch(e,s){if(s.length===0)return m().sessions[e]?.length??0;const t=m(),i=new Set(s.map(a=>a.id)),n=(t.sessions[e]??[]).filter(a=>!i.has(a.id));return n.length?t.sessions[e]=n:delete t.sessions[e],y(t),n.length}logLifecycle(e,s,t,i="info"){const n=`[chat.queue] ${JSON.stringify({at:E(),event:e,sessionId:s,...t})}`;i==="error"?console.error(n):console.info(n)}async dispatchQueuedMessages(e){const s=e[0],t=s.trigger?.kind==="scheduled"?s.trigger:null;if(t&&await this.validateScheduledTarget?.({sessionId:s.sessionId,...t})!=="ready")throw new D("schedule_target_unavailable");const i=e.flatMap(c=>{if(c.trigger?.kind==="room_activation"){const{kind:I,...p}=c.trigger;return[p]}return!c.trigger&&c.managedRoom?[c.managedRoom]:[]}),n=e.find(c=>c.trigger?.kind==="manager_event")?.trigger,a=e.some(c=>c.trigger?.kind==="managed_direct"||!!c.managedAgentDirect),o=a||!!t?.bindingId,d=e.find(c=>c.trigger?.kind==="manager_worker")?.trigger,r=n?.kind==="manager_event"||a,u=i.length>0?await this.prepareManagedRoomTurn?.(i):o||n?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(s.sessionId,L(n?.kind==="manager_event"?n:null,a)):void 0;if(i.length>0&&!u)throw new Error("managed_room_turn_preparer_unavailable");if(o&&!u)throw new Error("managed_agent_context_unavailable");if(n?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(s.sessionId)&&!u)throw new Error("managed_agent_context_unavailable");const l=new Set((m().sessions[s.sessionId]??[]).map(c=>c.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(c=>!l.has(c.id)))throw u?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(u.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new D("queued_dispatch_cancelled");const g=u??null,f=g?.sessionMode==="manager"?g.managerConfig:null,k=e.length===1?s.text:P(e),M=B(e.flatMap(c=>c.attachments??[])),S=o||n?.kind==="manager_event"&&!!u,h=S?u.agentSessionId?.startsWith("managed:")?T(this.opts.getRuntime(),s):u.agentSessionId:i.length>0?T(this.opts.getRuntime(),s):s.agentSessionId??null,q=u?z().find(c=>c.sessionId===s.sessionId):null;return await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...b(A()),sessionId:s.sessionId,text:k,agentType:S?u.agentType:s.agentType,sessionMode:g?.sessionMode??s.sessionMode,managerConfig:g?f:s.managerConfig,workDir:S?u.workDir:s.workDir,agentSessionId:h,modelId:W(q?.modelId)??W(s.modelId),systemPrompt:u?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:g?g.managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:g?g.managerDefaultWorkerModelId:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:M,externalChannel:r?void 0:s.externalChannel,replyTarget:r?void 0:s.replyTarget,roomContextRegistryKey:r?void 0:u?.roomContextRegistryKey,activationCauseMessageId:r?void 0:i.at(-1)?.causeMessageId,rootCauseMessageId:r?void 0:i.at(-1)?.rootCauseMessageId,activationSequence:r?void 0:i.at(-1)?.sequence,activationHopCount:r?void 0:i.at(-1)?.hopCount,managedRecoverySource:n?.kind==="manager_event"?"manager_event":o?"managed_direct":i.length>0?"managed_room":void 0,managerEventId:n?.kind==="manager_event"?n.eventId:void 0,managedWorkerParentSessionId:d?.kind==="manager_worker"?d.parentSessionId:void 0,managerWorkerRoomOrigin:d?.kind==="manager_worker"?d.roomOrigin:void 0,managerWorkerNativeFull:d?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isNativeSkillManager(d.parentSessionId,s.workDir)||void 0,managedRoomContextToken:u?.managedRoomContextToken,scheduledDelivery:!!t,suppressExternalOwnerEcho:i.length>0||!!t||void 0,queuedBatchItems:e.map(c=>{const I=_(c);return r?{...I,systemPrompt:u?.systemPrompt??null,externalChannel:null,replyTarget:null}:I}),waitForDispatch:!0}},u??void 0)??{}}markExternalQueued(e,s){const t=m(),i=new Set(e.map(o=>o.id)),n=t.sessions[e[0].sessionId]??[];let a=!1;for(const o of n)i.has(o.id)&&(o.externalQueuedSubmissionId=s||o.id,o.updatedAt=E(),a=!0);a&&y(t)}async isSessionBusy(e){const s=this.opts.getRuntime().recoveryBarrier;if(s?.isDispatchFrozen()||s?.isRecovering(e))return!0;const t=this.opts.getRuntime().sessions.get(e);if(!t)return!1;if(t.currentRunId){if(!(t.currentRunId.startsWith("dispatch:")&&!!this.opts.getRuntime().runJournal&&!t.journalRunId)||(t.adapter.getStatus?await t.adapter.getStatus().catch(()=>null):null)?.active)return!0;this.logLifecycle("drain.recovered-stale-dispatch",e,{staleRunId:t.currentRunId}),t.currentRunId=null,t.currentRunPhase=null}if(!t.adapter.getStatus)return!1;const i=await t.adapter.getStatus().catch(()=>null);return i?.active?(i.runId&&(t.currentRunId=i.runId,t.currentRunPhase=i.runPhase??t.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}export{Re as ChatQueueManager,qe as TerminalQueuedChatDispatchError,xe as purgeQueuedSessionData};
1
+ import{NativeSourceDeletionStore as U}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as V}from"./queue-retry.js";import{findDuplicateQueuedMessage as Q,stripLocalQueueFields as _,takeCompatibleBatch as X,formatQueuedBatch as P,dedupeQueuedAttachments as B,classifyQueueError as W,isControllableQueueMessage as v,takeControllableMessage as Y,normalizeQueuedModelId as F,resolveManagedRoomNativeSessionId as T,managedDirectTurnOptions as L}from"./queue-helpers.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as Z}from"@shennian/wire";import{isCodexThreadTailInterrupted as ee}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as te}from"./projection.js";import{assertFinishedAttachmentReferences as j,markAttachmentReferences as O}from"./attachment-transfer-store.js";import{appendMessage as N,getMessageDeliveryStatus as x,listSessionRecords as z,recordSession as $,tombstoneMessage as J,upsertMessage as C}from"./store.js";import{requestSessionIndexPublish as w}from"./index-publisher.js";import{clearPersistedManagerRoomOrigins as se,purgeQueuedSessionData as ne,readQueue as m,writeQueue as y}from"./queue-storage.js";import{getNativeSourceAgentType as ie}from"./handlers/chat-events.js";import{parsePersonalAttachmentReferences as K}from"./attachment-reference.js";import{getPersonalSyncIdentity as b}from"../personal-sync/epoch.js";import{getDaemonInstallationId as E}from"../personal-sync/identity.js";import{isTrustedManagerEvent as ae}from"./manager-event-authority.js";import{TerminalQueuedChatDispatchError as D,nowIso as A,queueEnvelope as R,queueMessageFromParams as G}from"./queue-message.js";import{purgeQueuedSessionData as xe}from"./queue-storage.js";import{TerminalQueuedChatDispatchError as qe}from"./queue-message.js";class Re{opts;validateScheduledTarget;async enqueueScheduled(e){return this.handleEnqueue(e,!0)}draining=new Set;promoting=new Set;disposed=!1;retries=new V(e=>{this.wakeSession(e)});deletions=new U;inFlightMessageIds=new Map;deferredLogState=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,se(),setTimeout(()=>this.drainIdleQueues(),0).unref?.()}stop(){this.disposed=!0,this.retries.stop()}getSnapshot(e){const s=m().sessions[e]??[],t=this.inFlightMessageIds.get(e),i=this.isImmediateSendAvailable(e),n=s.filter(a=>v(a)).map(a=>({..._(a),pendingState:a.externalQueuedSubmissionId?"provider_queued":t?.has(a.id)?"submitting":"waiting",canDelete:!a.externalQueuedSubmissionId&&!t?.has(a.id),canPromote:!a.externalQueuedSubmissionId&&!t?.has(a.id)&&i}));return{...b(E()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(_),controllablePending:s.filter(a=>v(a)&&!a.externalQueuedSubmissionId&&!t?.has(a.id)).map(_),visiblePending:n}}isImmediateSendAvailable(e){const s=this.opts.getRuntime(),t=s.sessions.get(e);return t?.currentRunId?t.journalRunId?s.runJournal?.get(e,t.journalRunId)?.deliveryMode==="direct":!1:!0}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const s=this.opts.getRuntime(),t=m(),i=new Set,n=new Map;let a=!1;for(const d of e){const r=d.params;if(this.deletions.hasSession(r.sessionId)){delete t.sessions[r.sessionId],a=!0;continue}if(!r.sessionId||!r.text||!r.agentType||!r.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=G({...b(E()),...r});u.origin="room",u.managedRoom=d.context,u.trigger={kind:"room_activation",...d.context};const l=t.sessions[r.sessionId]??[],g=Q(l,u),f=u.clientMessageId??u.id,k=x(r.sessionId,f);if(!g&&k.state==="not_found")l.push(u),t.sessions[r.sessionId]=l,n.set(`${u.sessionId}\0${f}`,u),i.add(r.sessionId),a=!0;else if(g&&k.state==="not_found")n.set(`${g.sessionId}\0${f}`,g),i.add(r.sessionId);else if(g&&k.state==="queued")i.add(r.sessionId);else if(g){const M=l.filter(S=>S.id!==g.id);M.length?t.sessions[r.sessionId]=M:delete t.sessions[r.sessionId],a=!0}}a&&y(t);let o;for(const d of i)try{for(const r of n.values())r.sessionId===d&&(N(d,R(r),"queued"),$({sessionId:d,agentType:r.agentType,sessionMode:r.sessionMode,managerConfig:r.managerConfig,workDir:r.workDir,agentSessionId:T(s,r),modelId:r.modelId}));this.logLifecycle("enqueue.persisted",d,{origin:"room",pendingCount:m().sessions[d]?.length??0}),this.broadcast(d),this.wakeSession(d)}catch(r){o=r,this.logLifecycle("enqueue.commit-failed",d,{failureClass:W(r instanceof Error?r.message:"","canonical_commit_failed")},"error"),this.retries.schedule(d,!0)}if(i.size&&w(s.client),o)throw o}async handleEnqueue(e,s=!1){const t=this.opts.getRuntime(),i=e.params;if(i.scheduledTask&&!s)throw new Error("schedule_source_untrusted");if((i.managerEventId||i.managerEventRoomOrigin||i.managedWorkerParentSessionId||i.managerWorkerRoomOrigin||e.params.managerWorkerNativeFull||e.params.managedRecoverySource)&&!ae(e))throw new Error("manager_event_source_untrusted");const n=!!(s&&i.scheduledTask?.bindingId);if(te(i.sessionListProjection),!i.sessionId||!i.text||!i.agentType||!i.workDir)return t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const a=L(i.managerEventId?{roomOrigin:i.managerEventRoomOrigin}:null,!!i.managedAgentDirect),o=i.managedAgentDirect||n?await this.prepareManagedAgentDirectTurn?.(i.sessionId,a):null;if((i.managedAgentDirect||n)&&!o)throw new Error("managed_agent_context_unavailable");const d=o?z().find(H=>H.sessionId===i.sessionId):null,r=o?{...i,...o,agentType:o.agentType,sessionMode:o.sessionMode,managerConfig:o.managerConfig,workDir:o.workDir,agentSessionId:o.agentSessionId?.startsWith("managed:")?d?.agentSessionId&&!d.agentSessionId.startsWith("managed:")?d.agentSessionId:null:o.agentSessionId,modelId:d?.modelId??void 0,systemPrompt:o.systemPrompt,externalChannel:i.managedAgentDirect?null:i.externalChannel,replyTarget:i.managedAgentDirect?null:i.replyTarget,managerDefaultWorkerAgentType:o.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:o.managerDefaultWorkerModelId}:i,u=t.resolvePath(r.workDir),l=K(r.attachments);l?.length&&j(u,l);const g={text:r.text,attachments:l,localized:!1},f=t.sessions.get(r.sessionId);if(r.agentType==="codex"&&r.agentSessionId&&f?.currentRunId?.startsWith("dispatch:")&&ee(r.agentSessionId))return t.client.sendRes({type:"res",id:e.id,ok:!1,error:Z}),!1;o?.managedRoomContextToken&&t.revokeManagedRoomContextToken?.(o.managedRoomContextToken);const k=await this.isSessionBusy(r.sessionId),M=m(),S=M.sessions[r.sessionId]??[],h=G({...r,text:g.text,attachments:g.attachments,systemPrompt:o?null:r.systemPrompt});o&&(i.managedAgentDirect&&(h.managedAgentDirect=!0),h.trigger?.kind!=="manager_event"&&h.trigger?.kind!=="scheduled"&&(h.trigger={kind:"managed_direct"}));const q=h.clientMessageId??h.id,c=x(r.sessionId,q);if(c.state!=="not_found")return t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:c.state==="queued",queueMessageId:h.id,...c,queue:this.getSnapshot(r.sessionId)}}),!0;const I=Q(S,h);I||(M.sessions[r.sessionId]=[...S,h],y(M));const p=I??h;return $({sessionId:p.sessionId,agentType:p.agentType,sessionMode:p.sessionMode,managerConfig:p.managerConfig,workDir:p.workDir,agentSessionId:p.agentSessionId,modelId:p.modelId}),N(p.sessionId,R(p),"queued"),O(t.resolvePath(p.workDir),p.attachments??[]),w(t.client),this.logLifecycle("enqueue.persisted",r.sessionId,{origin:r.origin??"app",queueMessageId:p.id,pendingCount:(m().sessions[r.sessionId]??[]).length}),this.broadcast(r.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:I?.id??h.id,...x(r.sessionId,I?.clientMessageId??I?.id??h.clientMessageId??h.id),queue:this.getSnapshot(r.sessionId),...g.localized?{localizedAttachments:!0}:{}}}),k?this.retries.schedule(r.sessionId):this.wakeSession(r.sessionId),!0}async handleGet(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleEdit(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],a=n.findIndex(r=>r.id===t.queueMessageId);if(a<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!v(n[a])||n[a].externalQueuedSubmissionId||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId)){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=K(t.attachments),d=s.resolvePath(n[a].workDir);o?.length&&j(d,o),n[a]={...n[a],text:t.text,attachments:o,updatedAt:A()},i.sessions[t.sessionId]=n,y(i),C(t.sessionId,R(n[a]),"queued"),n[a].attachments?.length&&O(d,n[a].attachments??[]),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],a=n.find(d=>d.id===t.queueMessageId);if(a&&(!v(a)||a.externalQueuedSubmissionId||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId))){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=n.filter(d=>d.id!==t.queueMessageId);if(o.length===n.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}o.length?i.sessions[t.sessionId]=o:delete i.sessions[t.sessionId],y(i),J(t.sessionId,a?.clientMessageId??a?.id??t.queueMessageId),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handlePromote(e){const s=this.opts.getRuntime(),t=e.params,i=a=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:a});if(!t.sessionId||!t.queueMessageId){i("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){i("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let n=[];try{for(;this.draining.has(t.sessionId);)await new Promise(d=>setTimeout(d,5));if(n=Y(m().sessions[t.sessionId]??[],t.queueMessageId),!n.length){i("Queued message not found");return}if(n[0].externalQueuedSubmissionId||!this.isImmediateSendAvailable(t.sessionId)){i("queue_immediate_send_unavailable");return}this.inFlightMessageIds.set(t.sessionId,new Set(n.map(d=>d.id)));const a=s.sessions.get(t.sessionId);let o="idle";if(a?.currentRunId)if(this.canInjectActiveBatch(a,n)){const d=n.length===1?n[0].text:P(n),r=n[0].clientMessageId??n[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:n[0].agentType,sourceAgentType:ie(n[0].agentType,n[0].modelId),canonicalMessageId:r,sourceSessionKey:a.agentSessionId,text:d,managedEchoPolicy:"suppress_following"});const u=Date.now();try{await a.adapter.injectActiveInput({text:d,attachments:B(n.flatMap(l=>l.attachments??[])),clientMessageId:r})}catch(l){throw s.nativeFusion?.cancelManagedSend(r),l}for(const l of n)C(t.sessionId,{...R(l),ts:u},"executing");this.removeBatch(t.sessionId,n),o="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId);const d=await this.dispatchQueuedMessages(n);d.deliveryMode==="external_owner_queue"?this.markExternalQueued(n,d.queuedSubmissionId):(this.removeBatch(t.sessionId,n),o="restarted")}else{const d=await this.dispatchQueuedMessages(n);d.deliveryMode==="external_owner_queue"?this.markExternalQueued(n,d.queuedSubmissionId):this.removeBatch(t.sessionId,n)}w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId),promotedMessageIds:n.map(d=>d.id),mode:o}})}catch(a){i(a instanceof Error?a.message:"queue_promotion_failed"),this.broadcast(t.sessionId)}finally{this.inFlightMessageIds.delete(t.sessionId),this.promoting.delete(t.sessionId),m().sessions[t.sessionId]?.length&&this.wakeSession(t.sessionId)}}noteTerminal(e){const s=m().sessions[e]?.find(t=>!!t.externalQueuedSubmissionId);s&&this.noteExternalClaim(e,s.clientMessageId??s.id),this.wakeSession(e)}purgeSession(e){ne(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const o=m().sessions[e]?.length??0;return this.purgeSession(e),o}const s=m(),t=s.sessions[e]??[],i=this.inFlightMessageIds.get(e)??new Set,n=t.filter(o=>o.managedRoom&&!i.has(o.id));if(n.length===0)return 0;const a=t.filter(o=>!n.includes(o));a.length?s.sessions[e]=a:delete s.sessions[e],y(s);for(const o of n)J(e,o.clientMessageId??o.id);return w(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:n.length,pendingCount:a.length}),this.broadcast(e),n.length}wakeSession(e){this.drainNext(e).catch(()=>{this.logLifecycle("drain.retry-required",e,{},"error"),this.retries.schedule(e,!0)})}drainIdleQueues(){if(this.disposed)return;const e=m();for(const s of Object.keys(e.sessions))this.wakeSession(s)}async drainNext(e){if(this.disposed||this.draining.has(e)||this.promoting.has(e))return;this.draining.add(e);let s=!1,t=!1,i=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const l=m().sessions[e]?.length??0,g=this.deferredLogState.get(e),f=Date.now();(!g||g.pendingCount!==l||f-g.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:l}),this.deferredLogState.set(e,{at:f,pendingCount:l}));return}this.deferredLogState.delete(e);const n=m(),a=n.sessions[e]??[],o=a.filter(l=>{const g=l.clientMessageId??l.id,f=x(e,g).state;return f==="queued"||f==="not_found"});if(o.length!==a.length&&(o.length?n.sessions[e]=o:delete n.sessions[e],y(n),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:a.length-o.length,pendingCount:o.length})),o.some(l=>!!l.externalQueuedSubmissionId)){this.broadcast(e);return}const d=X(o);if(d.length===0){this.broadcast(e);return}i=d,this.inFlightMessageIds.set(e,new Set(d.map(l=>l.id))),this.logLifecycle("dispatch.start",e,{batchCount:d.length,queueMessageId:d[0].id,origin:d[0].origin??"app"}),this.broadcast(e);for(const l of d)C(e,R(l),"queued");const r=await this.dispatchQueuedMessages(d);if(r.deliveryMode==="external_owner_queue"){this.markExternalQueued(d,r.queuedSubmissionId),this.logLifecycle("dispatch.external-queued",e,{batchCount:d.length,queueMessageId:d[0].id}),this.broadcast(e);return}const u=this.removeBatch(e,d);this.logLifecycle("dispatch.accepted",e,{batchCount:d.length,queueMessageId:d[0].id,pendingCount:u}),this.deferredLogState.delete(e),s=u>0,this.broadcast(e)}catch(n){const a=W(n instanceof Error?n.message:String(n),n instanceof D?"terminal_dispatch_failed":"transient_dispatch_failed"),o=i[0],d=!!(o&&!o.trigger&&!o.clientMessageId&&Date.now()-Date.parse(o.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(n instanceof D||a==="managed_claim_source_binding_conflict"||d){const u=this.removeBatch(e,i);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:i.length,queueMessageId:i[0]?.id,pendingCount:u,failureClass:d?"stale_legacy_managed_worker_dispatch_failure":a},"error"),s=u>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:a},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e);const n=m().sessions[e]??[];n.length?(this.broadcast(e),n.some(a=>!!a.externalQueuedSubmissionId)?this.retries.cancel(e):s?queueMicrotask(()=>this.wakeSession(e)):this.retries.schedule(e,t)):this.retries.cancel(e)}}noteExternalClaim(e,s){const t=m(),i=t.sessions[e]??[],n=i.filter(a=>(a.clientMessageId??a.id)!==s);n.length!==i.length&&(n.length?t.sessions[e]=n:delete t.sessions[e],y(t),this.broadcast(e))}canInjectActiveBatch(e,s){if(!e.adapter.supportsActiveInput||!e.adapter.injectActiveInput||!e.journalRunId)return!1;const t=this.opts.getRuntime().runJournal?.get(s[0].sessionId,e.journalRunId),i=s[0].trigger?.kind==="managed_direct"||s[0].managedAgentDirect?null:"personal";return!!(i&&s[0].systemPrompt==null&&t&&t.source===i&&t.deliveryMode==="direct"&&t.agentType===s[0].agentType&&t.workDir===e.workDir&&t.agentSessionId===e.agentSessionId&&t.modelId===(s[0].modelId??null)&&t.reasoningEffort===(s[0].reasoningEffort??null))}removeBatch(e,s){if(s.length===0)return m().sessions[e]?.length??0;const t=m(),i=new Set(s.map(a=>a.id)),n=(t.sessions[e]??[]).filter(a=>!i.has(a.id));return n.length?t.sessions[e]=n:delete t.sessions[e],y(t),n.length}logLifecycle(e,s,t,i="info"){const n=`[chat.queue] ${JSON.stringify({at:A(),event:e,sessionId:s,...t})}`;i==="error"?console.error(n):console.info(n)}async dispatchQueuedMessages(e){const s=e[0],t=s.trigger?.kind==="scheduled"?s.trigger:null;if(t&&await this.validateScheduledTarget?.({sessionId:s.sessionId,...t})!=="ready")throw new D("schedule_target_unavailable");const i=e.flatMap(c=>{if(c.trigger?.kind==="room_activation"){const{kind:I,...p}=c.trigger;return[p]}return!c.trigger&&c.managedRoom?[c.managedRoom]:[]}),n=e.find(c=>c.trigger?.kind==="manager_event")?.trigger,a=e.some(c=>c.trigger?.kind==="managed_direct"||!!c.managedAgentDirect),o=a||!!t?.bindingId,d=e.find(c=>c.trigger?.kind==="manager_worker")?.trigger,r=n?.kind==="manager_event"||a,u=i.length>0?await this.prepareManagedRoomTurn?.(i):o||n?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(s.sessionId,L(n?.kind==="manager_event"?n:null,a)):void 0;if(i.length>0&&!u)throw new Error("managed_room_turn_preparer_unavailable");if(o&&!u)throw new Error("managed_agent_context_unavailable");if(n?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(s.sessionId)&&!u)throw new Error("managed_agent_context_unavailable");const l=new Set((m().sessions[s.sessionId]??[]).map(c=>c.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(c=>!l.has(c.id)))throw u?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(u.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new D("queued_dispatch_cancelled");const g=u??null,f=g?.sessionMode==="manager"?g.managerConfig:null,k=e.length===1?s.text:P(e),M=B(e.flatMap(c=>c.attachments??[])),S=o||n?.kind==="manager_event"&&!!u,h=S?u.agentSessionId?.startsWith("managed:")?T(this.opts.getRuntime(),s):u.agentSessionId:i.length>0?T(this.opts.getRuntime(),s):s.agentSessionId??null,q=u?z().find(c=>c.sessionId===s.sessionId):null;return await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...b(E()),sessionId:s.sessionId,text:k,agentType:S?u.agentType:s.agentType,sessionMode:g?.sessionMode??s.sessionMode,managerConfig:g?f:s.managerConfig,workDir:S?u.workDir:s.workDir,agentSessionId:h,modelId:F(q?.modelId)??F(s.modelId),systemPrompt:u?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:g?g.managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:g?g.managerDefaultWorkerModelId:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:M,externalChannel:r?void 0:s.externalChannel,replyTarget:r?void 0:s.replyTarget,roomContextRegistryKey:r?void 0:u?.roomContextRegistryKey,activationCauseMessageId:r?void 0:i.at(-1)?.causeMessageId,rootCauseMessageId:r?void 0:i.at(-1)?.rootCauseMessageId,activationSequence:r?void 0:i.at(-1)?.sequence,activationHopCount:r?void 0:i.at(-1)?.hopCount,managedRecoverySource:n?.kind==="manager_event"?"manager_event":o?"managed_direct":i.length>0?"managed_room":void 0,managerEventId:n?.kind==="manager_event"?n.eventId:void 0,managedWorkerParentSessionId:d?.kind==="manager_worker"?d.parentSessionId:void 0,managerWorkerRoomOrigin:d?.kind==="manager_worker"?d.roomOrigin:void 0,managerWorkerNativeFull:d?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isExternalWorkerController(d.parentSessionId,s.workDir)||void 0,managedRoomContextToken:u?.managedRoomContextToken,scheduledDelivery:!!t,suppressExternalOwnerEcho:i.length>0||!!t||void 0,queuedBatchItems:e.map(c=>{const I=_(c);return r?{...I,systemPrompt:u?.systemPrompt??null,externalChannel:null,replyTarget:null}:I}),waitForDispatch:!0}},u??void 0)??{}}markExternalQueued(e,s){const t=m(),i=new Set(e.map(o=>o.id)),n=t.sessions[e[0].sessionId]??[];let a=!1;for(const o of n)i.has(o.id)&&(o.externalQueuedSubmissionId=s||o.id,o.updatedAt=A(),a=!0);a&&y(t)}async isSessionBusy(e){const s=this.opts.getRuntime().recoveryBarrier;if(s?.isDispatchFrozen()||s?.isRecovering(e))return!0;const t=this.opts.getRuntime().sessions.get(e);if(!t)return!1;if(t.currentRunId){if(!(t.currentRunId.startsWith("dispatch:")&&!!this.opts.getRuntime().runJournal&&!t.journalRunId)||(t.adapter.getStatus?await t.adapter.getStatus().catch(()=>null):null)?.active)return!0;this.logLifecycle("drain.recovered-stale-dispatch",e,{staleRunId:t.currentRunId}),t.currentRunId=null,t.currentRunPhase=null}if(!t.adapter.getStatus)return!1;const i=await t.adapter.getStatus().catch(()=>null);return i?.active?(i.runId&&(t.currentRunId=i.runId,t.currentRunPhase=i.runPhase??t.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}export{Re as ChatQueueManager,qe as TerminalQueuedChatDispatchError,xe as purgeQueuedSessionData};
@@ -117,6 +117,8 @@ export type AcpExternalSessionIdentity = {
117
117
  export type AgentInfo = {
118
118
  type: AgentType;
119
119
  models: ModelInfo[];
120
+ /** True when this runtime can back an ordinary Shennian session. */
121
+ supportsStandardSession?: boolean;
120
122
  /** User-facing grouping. Omitted by older daemons and interpreted as deep. */
121
123
  integration?: AgentIntegration;
122
124
  /** Non-secret metadata used for dynamic ACP display fallbacks. */
@@ -137,6 +139,10 @@ export type AgentInfo = {
137
139
  supportsWorker?: boolean;
138
140
  /** True only after this real provider passed Manager-mode runtime validation. */
139
141
  supportsManagerMode?: boolean;
142
+ /** True when a managed Room turn can receive explicit Room tools. */
143
+ supportsRoomTools?: boolean;
144
+ /** True when the public temporary Worker controller may target this runtime. */
145
+ supportsExternalWorkerTarget?: boolean;
140
146
  /** Per-machine Agent provider config summary (additive, non-secret). */
141
147
  config?: AgentConfigSummary;
142
148
  /** Non-secret runtime version and compatibility summary. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.4.71",
3
+ "version": "0.4.73",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {