shennian 0.4.59 → 0.4.61

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.
@@ -471,8 +471,8 @@
471
471
  },
472
472
  {
473
473
  "file": "src/manager/runtime.js",
474
- "beforeBytes": 28570,
475
- "afterBytes": 14555
474
+ "beforeBytes": 28698,
475
+ "afterBytes": 14642
476
476
  },
477
477
  {
478
478
  "file": "src/manager/worker-ipc.js",
@@ -786,8 +786,8 @@
786
786
  },
787
787
  {
788
788
  "file": "src/session/handlers/chat-recovery.js",
789
- "beforeBytes": 6002,
790
- "afterBytes": 3291
789
+ "beforeBytes": 6752,
790
+ "afterBytes": 3526
791
791
  },
792
792
  {
793
793
  "file": "src/session/handlers/chat-session-lifecycle.js",
@@ -796,8 +796,8 @@
796
796
  },
797
797
  {
798
798
  "file": "src/session/handlers/chat.js",
799
- "beforeBytes": 32820,
800
- "afterBytes": 13929
799
+ "beforeBytes": 33565,
800
+ "afterBytes": 14399
801
801
  },
802
802
  {
803
803
  "file": "src/session/handlers/control.js",
@@ -906,13 +906,13 @@
906
906
  },
907
907
  {
908
908
  "file": "src/session/queue.js",
909
- "beforeBytes": 42777,
910
- "afterBytes": 19219
909
+ "beforeBytes": 45091,
910
+ "afterBytes": 20004
911
911
  },
912
912
  {
913
913
  "file": "src/session/queued-chat-dispatch.js",
914
- "beforeBytes": 2356,
915
- "afterBytes": 1264
914
+ "beforeBytes": 2850,
915
+ "afterBytes": 1594
916
916
  },
917
917
  {
918
918
  "file": "src/session/run-recovery/barrier.js",
@@ -55,6 +55,7 @@ export declare class ManagerRuntimeService {
55
55
  getManagedAgentPolicyForWorker(managerSessionId: string, workDir: string): CompiledManagedAgentPolicy | null;
56
56
  isNativeSkillManager(managerSessionId: string, workDir: string): boolean;
57
57
  isManagedWorkerSession(sessionId: string): boolean;
58
+ isManagedRoomBoundManager(sessionId: string): boolean;
58
59
  setManagerWorkerDefaults(sessionId: string, agentType?: AgentType | null, modelId?: string | null): void;
59
60
  getManagerWorkerDefaults(sessionId: string): {
60
61
  agentType: AgentType | null;
@@ -1,13 +1,13 @@
1
- import _ from"node:http";import{createHash as N,randomBytes as x,randomUUID as f}from"node:crypto";import h from"node:fs";import v from"node:os";import y from"node:path";import{ManagerRegistry as T}from"./registry.js";import{appendMessage as D}from"../session/store.js";import{requestSessionIndexPublish as W}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 O}from"../agents/external-channel-instructions.js";import{getPersonalSyncIdentity as I}from"../personal-sync/epoch.js";import{handleWorkerIpcRoute as E}from"./worker-ipc.js";import{deriveWorkerTitle as C,formatWorkerMarkdown as B}from"./worker-markdown.js";const H=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024),m=12*6e4;let R=null;function ae(o){R=o}function oe(){return R}function c(o){return y.resolve(o||v.homedir())}function g(o,e,t){o.writeHead(e,{"content-type":"application/json; charset=utf-8"}),o.end(JSON.stringify(t))}function M(o){return o.replace(/\r\n/g,`
2
- `).trim()}function $(o){return o==="worker.final"?"completed":o==="worker.error"?"failed":o==="worker.aborted"?"stopped":"progress"}function U(o,e){return`manager-inbox:${N("sha256").update(o).update("\0").update(e.map(r=>r.id).join("\0")).digest("hex").slice(0,32)}`}async function j(o){const e=[];let t=0;for await(const r of o){const n=Buffer.isBuffer(r)?r:Buffer.from(r);if(t+=n.length,t>H)throw new Error("Request body too large");e.push(n)}return e.length===0?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}function G(o,e,t,r){o.client.sendRes({type:"res",id:e,ok:t,...t?{payload:r}:{error:String(r.error||"unknown error")}})}function S(){return F()?y.join(v.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):P("runtime","manager-ipc.json")}function F(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class ue{opts;managedPolicies=new Map;registry=new T;channelRuntime;server=null;ipcUrl=null;ipcToken=x(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;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.server=_.createServer((t,r)=>{this.handleIpc(t,r)}),this.server.requestTimeout=m,this.server.timeout=m,this.server.keepAliveTimeout=m,this.server.headersTimeout=m+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=S();h.mkdirSync(y.dirname(e),{recursive:!0}),h.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),h.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=S(),t=JSON.parse(h.readFileSync(e,"utf-8"));(t.url===this.ipcUrl||t.token===this.ipcToken)&&h.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:c(r),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:c(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||c(t)!==r.workDir?null:n}isNativeSkillManager(e,t){const r=this.registry.getManager(e);return r?.controllerKind==="native_skill"&&c(t)===r.workDir}isManagedWorkerSession(e){return!!this.findWorker(e)}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 s=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:t,workDir:c(r),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n,agentType:s?.agentType,providerModelId:s?.providerModelId})}noteAgentEvent(e,t){const r=this.findWorker(e);if(!r)return;const n={lastActivityAt:new Date().toISOString(),runId:t.runId};t.agentSessionId&&(n.agentSessionId=t.agentSessionId);const s=`${e}:${t.runId}`;let i=t.message||r.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)";if(t.state==="delta"&&t.text&&!t.thinking){const u=(this.workerTextAcc.get(s)??"")+t.text;this.workerTextAcc.set(s,u);const a=M(u);a&&(n.summary=a.length>160?`${a.slice(0,160)}...`:a)}if(t.state==="final"||t.state==="error"||t.state==="aborted"){n.status=t.state;const u=M(this.workerTextAcc.get(s)??"");u&&(n.summary=u.length>240?`${u.slice(0,240)}...`:u,i=t.message||u),this.workerTextAcc.delete(s)}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,i)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){G(this.opts.getRuntime(),e.id,!1,{error:"feature_retired"})}async handleIpc(e,t){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){g(t,401,{ok:!1,error:"Unauthorized"});return}try{const r=new URL(e.url??"/","http://127.0.0.1"),n=await j(e);if(r.pathname.startsWith("/channel/")||r.pathname.startsWith("/external/")||r.pathname.startsWith("/wechat-rpa/")){g(t,410,{ok:!1,error:"feature_retired"});return}const s=await E({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(s){g(t,s.status,s.body);return}g(t,404,{ok:!1,error:`Unknown manager IPC path: ${r.pathname}`})}catch(r){g(t,400,{ok:!1,error:r instanceof Error?r.message:String(r)})}}async executeScopedWorkerAction(e,t,r){const n={"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 E({registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},n[t],r,e);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,s,i,d){if((await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${f()}`,method:"chat.enqueue",params:{...I(),sessionId:e,text:n,agentType:t,workDir:r,agentSessionId:s,modelId:i,managedWorkerParentSessionId:d}}))?.durableQueueHandoff===!1)throw new Error("manager_worker_queue_handoff_unconfirmed")}appendManagerVisibleMarkdown(e,t,r){this.registry.getManager(e)?.controllerKind!=="native_skill"&&(D(e,{id:r,sessionId:e,role:"agent",ts:Date.now(),payload:t},"committed"),W(this.opts.getRuntime().client))}enqueueWorkerEvent(e,t,r,n,s){const i=this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:t.sessionId,kind:r==="final"?"worker.final":r==="error"?"worker.error":"worker.aborted",priority:"normal",runId:n??null,summary:s||t.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}),d=this.registry.getManager(e);i&&d?.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 s=e-Date.parse(n.createdAt);if(s<10*6e4)continue;const i=e-Date.parse(n.lastActivityAt),d=r.sessions.get(n.sessionId)?.currentRunId,u=!!r.chatQueue?.getSnapshot(n.sessionId).pending.length;if(!d){if(!u&&i>=10*6e4){const w="\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",p=this.registry.updateWorker(n.sessionId,{status:"error",lastActivityAt:new Date(e).toISOString(),summary:w});p&&this.enqueueWorkerEvent(p.managedBy,p,"error",n.runId??void 0,w)}continue}const a=n.healthNotifiedAt?Date.parse(n.healthNotifiedAt):0;if(a&&e-a<10*6e4)continue;const l=t.managers[n.managedBy];if(!l)continue;const A=Math.max(0,Math.floor(i/6e4)),b=M(n.runId?this.workerTextAcc.get(`${n.sessionId}:${n.runId}`)??"":""),k=`\u5DF2\u8FD0\u884C ${Math.floor(s/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
1
+ import _ from"node:http";import{createHash as N,randomBytes as x,randomUUID as f}from"node:crypto";import g from"node:fs";import v from"node:os";import y from"node:path";import{ManagerRegistry as T}from"./registry.js";import{appendMessage as D}from"../session/store.js";import{requestSessionIndexPublish as W}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 B}from"../agents/external-channel-instructions.js";import{getPersonalSyncIdentity as I}from"../personal-sync/epoch.js";import{handleWorkerIpcRoute as E}from"./worker-ipc.js";import{deriveWorkerTitle as O,formatWorkerMarkdown as C}from"./worker-markdown.js";const H=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024),m=12*6e4;let R=null;function ae(o){R=o}function oe(){return R}function h(o){return y.resolve(o||v.homedir())}function c(o,e,t){o.writeHead(e,{"content-type":"application/json; charset=utf-8"}),o.end(JSON.stringify(t))}function M(o){return o.replace(/\r\n/g,`
2
+ `).trim()}function $(o){return o==="worker.final"?"completed":o==="worker.error"?"failed":o==="worker.aborted"?"stopped":"progress"}function U(o,e){return`manager-inbox:${N("sha256").update(o).update("\0").update(e.map(r=>r.id).join("\0")).digest("hex").slice(0,32)}`}async function j(o){const e=[];let t=0;for await(const r of o){const n=Buffer.isBuffer(r)?r:Buffer.from(r);if(t+=n.length,t>H)throw new Error("Request body too large");e.push(n)}return e.length===0?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}function G(o,e,t,r){o.client.sendRes({type:"res",id:e,ok:t,...t?{payload:r}:{error:String(r.error||"unknown error")}})}function S(){return F()?y.join(v.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):P("runtime","manager-ipc.json")}function F(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class ue{opts;managedPolicies=new Map;registry=new T;channelRuntime;server=null;ipcUrl=null;ipcToken=x(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;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.server=_.createServer((t,r)=>{this.handleIpc(t,r)}),this.server.requestTimeout=m,this.server.timeout=m,this.server.keepAliveTimeout=m,this.server.headersTimeout=m+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=S();g.mkdirSync(y.dirname(e),{recursive:!0}),g.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),g.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=S(),t=JSON.parse(g.readFileSync(e,"utf-8"));(t.url===this.ipcUrl||t.token===this.ipcToken)&&g.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:h(r),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:h(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||h(t)!==r.workDir?null:n}isNativeSkillManager(e,t){const r=this.registry.getManager(e);return r?.controllerKind==="native_skill"&&h(t)===r.workDir}isManagedWorkerSession(e){return!!this.findWorker(e)}isManagedRoomBoundManager(e){return this.registry.getManager(e)?.managedRoomBound===!0}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 s=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:t,workDir:h(r),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n,agentType:s?.agentType,providerModelId:s?.providerModelId})}noteAgentEvent(e,t){const r=this.findWorker(e);if(!r)return;const n={lastActivityAt:new Date().toISOString(),runId:t.runId};t.agentSessionId&&(n.agentSessionId=t.agentSessionId);const s=`${e}:${t.runId}`;let i=t.message||r.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)";if(t.state==="delta"&&t.text&&!t.thinking){const u=(this.workerTextAcc.get(s)??"")+t.text;this.workerTextAcc.set(s,u);const a=M(u);a&&(n.summary=a.length>160?`${a.slice(0,160)}...`:a)}if(t.state==="final"||t.state==="error"||t.state==="aborted"){n.status=t.state;const u=M(this.workerTextAcc.get(s)??"");u&&(n.summary=u.length>240?`${u.slice(0,240)}...`:u,i=t.message||u),this.workerTextAcc.delete(s)}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,i)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){G(this.opts.getRuntime(),e.id,!1,{error:"feature_retired"})}async handleIpc(e,t){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){c(t,401,{ok:!1,error:"Unauthorized"});return}try{const r=new URL(e.url??"/","http://127.0.0.1"),n=await j(e);if(r.pathname.startsWith("/channel/")||r.pathname.startsWith("/external/")||r.pathname.startsWith("/wechat-rpa/")){c(t,410,{ok:!1,error:"feature_retired"});return}const s=await E({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(s){c(t,s.status,s.body);return}c(t,404,{ok:!1,error:`Unknown manager IPC path: ${r.pathname}`})}catch(r){c(t,400,{ok:!1,error:r instanceof Error?r.message:String(r)})}}async executeScopedWorkerAction(e,t,r){const n={"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 E({registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},n[t],r,e);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,s,i,d){if((await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${f()}`,method:"chat.enqueue",params:{...I(),sessionId:e,text:n,agentType:t,workDir:r,agentSessionId:s,modelId:i,managedWorkerParentSessionId:d}}))?.durableQueueHandoff===!1)throw new Error("manager_worker_queue_handoff_unconfirmed")}appendManagerVisibleMarkdown(e,t,r){this.registry.getManager(e)?.controllerKind!=="native_skill"&&(D(e,{id:r,sessionId:e,role:"agent",ts:Date.now(),payload:t},"committed"),W(this.opts.getRuntime().client))}enqueueWorkerEvent(e,t,r,n,s){const i=this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:t.sessionId,kind:r==="final"?"worker.final":r==="error"?"worker.error":"worker.aborted",priority:"normal",runId:n??null,summary:s||t.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}),d=this.registry.getManager(e);i&&d?.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 s=e-Date.parse(n.createdAt);if(s<10*6e4)continue;const i=e-Date.parse(n.lastActivityAt),d=r.sessions.get(n.sessionId)?.currentRunId,u=!!r.chatQueue?.getSnapshot(n.sessionId).pending.length;if(!d){if(!u&&i>=10*6e4){const w="\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",p=this.registry.updateWorker(n.sessionId,{status:"error",lastActivityAt:new Date(e).toISOString(),summary:w});p&&this.enqueueWorkerEvent(p.managedBy,p,"error",n.runId??void 0,w)}continue}const a=n.healthNotifiedAt?Date.parse(n.healthNotifiedAt):0;if(a&&e-a<10*6e4)continue;const l=t.managers[n.managedBy];if(!l)continue;const A=Math.max(0,Math.floor(i/6e4)),b=M(n.runId?this.workerTextAcc.get(`${n.sessionId}:${n.runId}`)??"":""),k=`\u5DF2\u8FD0\u884C ${Math.floor(s/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${b||n.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)"}
6
6
  - \u6700\u8FD1\u6D3B\u52A8\u65F6\u95F4\uFF1A${n.lastActivityAt}
7
- - \u6700\u8FD1 ${A} \u5206\u949F\u6CA1\u6709\u65B0\u6D3B\u52A8\u3002`;this.registry.updateWorker(n.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:k}),this.registry.enqueueInboxEvent({managerSessionId:l.sessionId,workerSessionId:n.sessionId,kind:"worker.health",priority:"normal",runId:n.runId??null,summary:k})&&l.controllerKind!=="native_skill"&&this.drainManagerInbox(l.sessionId)}}async interruptAndResumeManager(e,t,r){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${f()}`,method:"chat.abort",params:{...I(),sessionId:e.sessionId}}),await this.dispatchManagerContinuation(e,t,!1,r)}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(a=>a.priority==="high"),s=this.opts.getRuntime().sessions.get(e);if(t.status!=="idle"&&!n||s?.currentRunId&&!n)return;const i=n?[n]:r.slice(0,20),d=i.map(a=>{const l=this.registry.getWorkerForManager(e,a.workerSessionId);return B({kind:$(a.kind),sessionId:a.workerSessionId,title:l?.title||C(l?.userGoal||l?.summary||a.summary),body:a.summary})}).join(`
7
+ - \u6700\u8FD1 ${A} \u5206\u949F\u6CA1\u6709\u65B0\u6D3B\u52A8\u3002`;this.registry.updateWorker(n.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:k}),this.registry.enqueueInboxEvent({managerSessionId:l.sessionId,workerSessionId:n.sessionId,kind:"worker.health",priority:"normal",runId:n.runId??null,summary:k})&&l.controllerKind!=="native_skill"&&this.drainManagerInbox(l.sessionId)}}async interruptAndResumeManager(e,t,r){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${f()}`,method:"chat.abort",params:{...I(),sessionId:e.sessionId}}),await this.dispatchManagerContinuation(e,t,!1,r)}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(a=>a.priority==="high"),s=this.opts.getRuntime().sessions.get(e);if(t.status!=="idle"&&!n||s?.currentRunId&&!n)return;const i=n?[n]:r.slice(0,20),d=i.map(a=>{const l=this.registry.getWorkerForManager(e,a.workerSessionId);return C({kind:$(a.kind),sessionId:a.workerSessionId,title:l?.title||O(l?.userGoal||l?.summary||a.summary),body:a.summary})}).join(`
8
8
 
9
9
  ---
10
10
 
11
- `),u=U(e,i);try{n&&s?.currentRunId?(this.registry.upsertManager({...t,status:"interrupting",deliveringInboxEventIds:i.map(l=>l.id)}),await this.interruptAndResumeManager(t,d,u)):(this.registry.upsertManager({...t,status:"running",deliveringInboxEventIds:i.map(l=>l.id)}),await this.dispatchManagerContinuation(t,d,!0,u)),this.registry.removeInboxEvents(e,i.map(l=>l.id));const a=this.registry.getManager(e);a&&this.registry.upsertManager({...a,deliveringInboxEventIds:[]})}catch(a){this.registry.upsertManager({...t,status:"idle",deliveringInboxEventIds:[]}),console.error(`[manager-inbox] delivery failed sessionId=${e}: ${a instanceof Error?a.message:String(a)}`)}}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 s=this.registry.listInboxEvents(r.sessionId).filter(i=>i.kind==="worker.health"&&!t.sessions.get(i.workerSessionId)?.currentRunId).map(i=>i.id);this.registry.removeInboxEvents(r.sessionId,s),this.registry.listInboxEvents(r.sessionId).length>0&&queueMicrotask(()=>{this.drainManagerInbox(r.sessionId)})}}async dispatchManagerContinuation(e,t,r=!1,n){const s=e.agentType??"manager",i=s==="manager"?e.modelId:e.providerModelId??void 0,d={...I(),sessionId:e.sessionId,text:t,agentType:s,sessionMode:"manager",workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:i,managerDefaultWorkerAgentType:e.defaultWorkerAgentType??null,managerDefaultWorkerModelId:e.defaultWorkerModelId??null,...e.managedRoomBound?{managedAgentDirect:!0}:{},clientMessageId:n,managerEventId:n};if((await this.opts.dispatchReq({type:"req",id:`manager-${r?"enqueue":"send"}-${f()}`,method:"chat.enqueue",params:d}))?.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=>O(t,void 0,e,"manager")).join(`
11
+ `),u=U(e,i);try{n&&s?.currentRunId?(this.registry.upsertManager({...t,status:"interrupting",deliveringInboxEventIds:i.map(l=>l.id)}),await this.interruptAndResumeManager(t,d,u)):(this.registry.upsertManager({...t,status:"running",deliveringInboxEventIds:i.map(l=>l.id)}),await this.dispatchManagerContinuation(t,d,!0,u)),this.registry.removeInboxEvents(e,i.map(l=>l.id));const a=this.registry.getManager(e);a&&this.registry.upsertManager({...a,deliveringInboxEventIds:[]})}catch(a){this.registry.upsertManager({...t,status:"idle",deliveringInboxEventIds:[]}),console.error(`[manager-inbox] delivery failed sessionId=${e}: ${a instanceof Error?a.message:String(a)}`)}}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 s=this.registry.listInboxEvents(r.sessionId).filter(i=>i.kind==="worker.health"&&!t.sessions.get(i.workerSessionId)?.currentRunId).map(i=>i.id);this.registry.removeInboxEvents(r.sessionId,s),this.registry.listInboxEvents(r.sessionId).length>0&&queueMicrotask(()=>{this.drainManagerInbox(r.sessionId)})}}async dispatchManagerContinuation(e,t,r=!1,n){const s=e.agentType??"manager",i=s==="manager"?e.modelId:e.providerModelId??void 0,d={...I(),sessionId:e.sessionId,text:t,agentType:s,sessionMode:"manager",workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:i,managerDefaultWorkerAgentType:e.defaultWorkerAgentType??null,managerDefaultWorkerModelId:e.defaultWorkerModelId??null,...e.managedRoomBound?{managedAgentDirect:!0}:{},clientMessageId:n,managerEventId:n};if((await this.opts.dispatchReq({type:"req",id:`manager-${r?"enqueue":"send"}-${f()}`,method:"chat.enqueue",params:d}))?.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=>B(t,void 0,e,"manager")).join(`
12
12
 
13
13
  `).trim()}}export{ue as ManagerRuntimeService,oe as getManagerRuntimeService,ae as setManagerRuntimeService};
@@ -1 +1 @@
1
- import{createHash as u}from"node:crypto";import{buildManagedAgentPermissionPrompt as g}from"../../room/managed-prompt-composer.js";import{configureAdapterForSession as d,replaceManagedRoomContextToken as p}from"./chat-adapter-config.js";import{createActiveSession as y}from"./chat-session-lifecycle.js";import{validateManagedRoomExecutionContext as c}from"./chat.js";async function h(a,e,m=()=>{}){let o=null,s=null,i=null,r;if(e.source==="managed_room"||e.source==="managed_direct"||e.source==="manager_event"&&e.managedPolicyVersion){const n=await a.prepareManagedRecovery?.(e);if(!n)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"managed recovery requires a fresh permission context"};if(n.agentType!==e.agentType||n.sessionMode!==e.sessionMode||a.resolvePath(n.workDir)!==a.resolvePath(e.workDir)||n.managedRoomPromptSnapshot.managedRoomPolicyVersion!==e.managedPolicyVersion||n.managedRoomPromptSnapshot.runtimePermissionPolicyHash!==e.managedRuntimePolicyHash)return a.revokeManagedRoomContextToken?.(n.managedRoomContextToken),{result:"recovery_failed",reason:"permission_context_unavailable",message:"managed recovery authority no longer matches the interrupted run"};const l={source:"room_activation",managedAgentPolicy:n.managedAgentPolicy,managedRoomPromptSnapshot:n.managedRoomPromptSnapshot};c(l,n.systemPrompt,n.managedRoomContextToken),o=n.managedAgentPolicy,s=n.managedRoomContextToken,i=n.systemPrompt,r={...l,recovery:"upgrade"}}else if(e.source==="manager_worker"&&!e.managerWorkerNativeFull){const n=e.parentSessionId;if(o=n?a.managerRuntime?.getManagedAgentPolicyForWorker(n,e.workDir)??null:null,!o)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker recovery requires the current parent policy"};if(u("sha256").update(JSON.stringify({requestedPolicy:o.requestedPolicy,enforcement:o.enforcement})).digest("hex")!==e.managedRuntimePolicyHash)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker policy changed after interruption"};i=g({requestedPolicy:o.requestedPolicy,enforcement:o.enforcement,resolvedWorkDir:o.adapterPolicy.workDir})}r??={source:"upgrade_recovery",recovery:"upgrade"};let t=a.sessions.get(e.sessionId);return t||(t=await y(a,e.sessionId,e.agentType,e.sessionMode,e.workDir,e.agentSessionId,null,null,o,e.nativeSessionVisibility)),s&&(p(a,t,s),d(t.adapter,e.sessionId,e.agentType,null,null,s,o,a.managedRoomExecutor)),t.journalRunId=e.runId,t.currentRunId=e.providerRunId??`recovery:${e.runId}`,t.currentRunPhase="thinking",e.agentType==="codex"&&e.deliveryMode==="external_owner_queue"&&e.agentSessionId&&e.externalRunId&&a.nativeFusion?.restoreExternalObservation({sessionId:e.sessionId,sourceSessionKey:e.agentSessionId,runId:e.externalRunId,canonicalMessageId:e.clientMessageId,createdAt:Date.parse(e.createdAt)})?{result:"reattached"}:t.adapter.recoverInterruptedRun({sessionId:e.sessionId,shennianRunId:e.runId,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,reasoningEffort:e.reasoningEffort,providerRunId:e.providerRunId,lastObservedEventSeq:e.lastObservedEventSeq,continueDispatchState:e.continueDispatchState,systemPrompt:i,sendContext:r,beforeContinueDispatch:m})}export{h as recoverJournaledRun};
1
+ import{createHash as g}from"node:crypto";import{buildManagedAgentPermissionPrompt as d}from"../../room/managed-prompt-composer.js";import{configureAdapterForSession as m,replaceManagedRoomContextToken as p}from"./chat-adapter-config.js";import{createActiveSession as y}from"./chat-session-lifecycle.js";import{validateManagedRoomExecutionContext as v}from"./chat.js";async function h(o,e,u=()=>{}){let a=null,s=null,r=null,i;if(e.source==="managed_room"||e.source==="managed_direct"||e.source==="manager_event"&&e.managedPolicyVersion){const n=await o.prepareManagedRecovery?.(e);if(!n)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"managed recovery requires a fresh permission context"};if(n.agentType!==e.agentType||n.sessionMode!==e.sessionMode||o.resolvePath(n.workDir)!==o.resolvePath(e.workDir)||n.managedRoomPromptSnapshot.managedRoomPolicyVersion!==e.managedPolicyVersion||n.managedRoomPromptSnapshot.runtimePermissionPolicyHash!==e.managedRuntimePolicyHash)return o.revokeManagedRoomContextToken?.(n.managedRoomContextToken),{result:"recovery_failed",reason:"permission_context_unavailable",message:"managed recovery authority no longer matches the interrupted run"};if(a=n.managedAgentPolicy,e.source==="manager_event")o.revokeManagedRoomContextToken?.(n.managedRoomContextToken),i={source:"upgrade_recovery",recovery:"upgrade"};else{const l={source:"room_activation",managedAgentPolicy:n.managedAgentPolicy,managedRoomPromptSnapshot:n.managedRoomPromptSnapshot};v(l,n.systemPrompt,n.managedRoomContextToken),s=n.managedRoomContextToken,r=n.systemPrompt,i={...l,recovery:"upgrade"}}}else if(e.source==="manager_worker"&&!e.managerWorkerNativeFull){const n=e.parentSessionId;if(a=n?o.managerRuntime?.getManagedAgentPolicyForWorker(n,e.workDir)??null:null,!a)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker recovery requires the current parent policy"};if(g("sha256").update(JSON.stringify({requestedPolicy:a.requestedPolicy,enforcement:a.enforcement})).digest("hex")!==e.managedRuntimePolicyHash)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker policy changed after interruption"};r=d({requestedPolicy:a.requestedPolicy,enforcement:a.enforcement,resolvedWorkDir:a.adapterPolicy.workDir})}i??={source:"upgrade_recovery",recovery:"upgrade"};let t=o.sessions.get(e.sessionId);return t||(t=await y(o,e.sessionId,e.agentType,e.sessionMode,e.workDir,e.agentSessionId,null,null,a,e.nativeSessionVisibility)),a&&e.source==="manager_event"?m(t.adapter,e.sessionId,e.agentType,null,null,null,a,null):s&&(p(o,t,s),m(t.adapter,e.sessionId,e.agentType,null,null,s,a,o.managedRoomExecutor)),t.journalRunId=e.runId,t.currentRunId=e.providerRunId??`recovery:${e.runId}`,t.currentRunPhase="thinking",e.agentType==="codex"&&e.deliveryMode==="external_owner_queue"&&e.agentSessionId&&e.externalRunId&&o.nativeFusion?.restoreExternalObservation({sessionId:e.sessionId,sourceSessionKey:e.agentSessionId,runId:e.externalRunId,canonicalMessageId:e.clientMessageId,createdAt:Date.parse(e.createdAt)})?{result:"reattached"}:t.adapter.recoverInterruptedRun({sessionId:e.sessionId,shennianRunId:e.runId,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,reasoningEffort:e.reasoningEffort,providerRunId:e.providerRunId,lastObservedEventSeq:e.lastObservedEventSeq,continueDispatchState:e.continueDispatchState,systemPrompt:r,sendContext:i,beforeContinueDispatch:u})}export{h as recoverJournaledRun};
@@ -3,10 +3,18 @@ import { type ReqFrame, type AgentType, type SessionMode } from '@shennian/wire'
3
3
  import type { SessionManagerRuntime } from '../types.js';
4
4
  import type { CompiledManagedAgentPolicy } from '../../security/managed-agent-policy.js';
5
5
  import { type ManagedRoomPromptSnapshot } from '../../room/managed-prompt-composer.js';
6
- export type ChatSendExecutionContext = AgentSendContext & {
6
+ export type ChatSendExecutionContext = (AgentSendContext & {
7
+ source: 'room_activation';
7
8
  /** Local-only snapshot produced by RoomActivationService; never accepted from Relay input. */
8
9
  managedAgentPolicy: CompiledManagedAgentPolicy;
9
10
  managedRoomPromptSnapshot: ManagedRoomPromptSnapshot;
11
+ }) | {
12
+ /** A Worker event may reuse the Room's current permission ceiling, but it
13
+ * is a private Manager continuation and must never receive Room tools. */
14
+ source: 'manager_event';
15
+ managedAgentPolicy: CompiledManagedAgentPolicy;
16
+ managedRoomPolicyVersion: string;
17
+ managedRuntimePolicyHash: string | null;
10
18
  };
11
19
  export declare function validateManagedRoomExecutionContext(context: ChatSendExecutionContext, systemPrompt: string | null | undefined, managedRoomContextToken: string | null | undefined): void;
12
20
  export declare function sendSessionUpdateEvent(runtime: SessionManagerRuntime, input: {
@@ -1 +1 @@
1
- import se from"node:os";import{createHash as V,randomUUID as _e}from"node:crypto";import{buildUserMessagePayload as ie}from"@shennian/wire";import{reportLog as E}from"../../log-reporter.js";import{appendMessage as de,getDaemonInstallationId as le,getMessageDeliveryStatus as Ae,recordSession as Me,upsertMessage as I}from"../store.js";import{mergeProjectedSessions as Pe}from"../projection.js";import{parsePersonalAttachmentReferences as De}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as Ee,markAttachmentReferences as xe}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as R}from"../index-publisher.js";import{getPersonalSyncIdentity as be}from"../../personal-sync/epoch.js";import{buildManagedAgentPermissionPrompt as Te}from"../../room/managed-prompt-composer.js";import{getNativeSourceAgentType as Ce}from"./chat-events.js";import{configureAdapterForSession as ce,maybeResolveClaudeImportedWorkDir as $e,replaceManagedRoomContextToken as ge,revokeManagedRoomContextToken as ue}from"./chat-adapter-config.js";import{createActiveSession as qe,disposeSession as $,prepareAdapterTurn as We,rememberProcessedReqId as je,sendAdapterTurn as me}from"./chat-session-lifecycle.js";function Fe(e,n,t){if(!e.managedAgentPolicy)throw new Error("managed_agent_policy_missing");const l=e.managedRoomPromptSnapshot;if(!l)throw new Error("managed_room_prompt_snapshot_missing");if(!n?.trim())throw new Error("managed_room_prompt_incomplete");if(!t||!/^mrc_[A-Za-z0-9_-]{43}$/.test(t))throw new Error("managed_room_context_unavailable");const o=V("sha256").update(JSON.stringify({requestedPolicy:e.managedAgentPolicy.requestedPolicy,enforcement:e.managedAgentPolicy.enforcement})).digest("hex"),x=V("sha256").update(e.managedAgentPolicy.adapterPolicy.workDir).digest("hex");if(l.runtimePermissionPolicyHash!==o||l.resolvedWorkDirHash!==x||l.enforcementLevel!==e.managedAgentPolicy.enforcement.level)throw new Error("managed_room_prompt_snapshot_mismatch")}function pe(e,n){return`Agent send failed: ${n instanceof Error?n.message:String(n)}`}function Be(e,n){Me({sessionId:n.sessionId,agentType:n.agentType,sessionMode:n.sessionMode,managerConfig:n.managerConfig,workDir:n.workDir,agentSessionId:n.agentSessionId??null,modelId:n.modelId??null,managerDefaultWorkerAgentType:n.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:n.managerDefaultWorkerModelId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:n.sessionId,agentType:n.agentType,sessionMode:n.sessionMode,managerConfig:n.managerConfig,agentSessionId:n.agentSessionId??null,modelId:n.modelId??null,workDir:n.workDir,status:"active",externalChannel:null}}})}function Je(e,n){const t=e.sessions.get(n),l=t?.currentRunId;if(!t||!l)return;const o=t.nextEventSeq;e.runTextAcc.delete(`${n}:${l}`),ue(e,t),t.pendingTextEvent=null,t.currentRunId=null,t.currentRunPhase=null,t.nextEventSeq=0,t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:n,runId:l,seq:o},seq:o,id:`agent-evt-${l}-${o}`}),t.journalRunId&&(e.runJournal?.remove(n,t.journalRunId),e.recoveryBarrier?.end(n),t.journalRunId=null),e.chatQueue?.noteTerminal(n)}async function on(e,n,t){const l=t?n.params.managedRoomContextToken??null:null;let o=!1;try{await He(e,n,t,()=>{o=!0})}finally{l&&!o&&e.revokeManagedRoomContextToken?.(l)}}async function He(e,n,t,l){if(e.processedReqIds.has(n.id)){e.client.sendRes({type:"res",id:n.id,ok:!0});return}const{sessionId:o,text:x,agentType:A,sessionMode:fe,managerConfig:L,workDir:ye,agentSessionId:u,modelId:v,systemPrompt:Q,managerDefaultWorkerAgentType:q,managerDefaultWorkerModelId:W,reasoningEffort:j,clientMessageId:ve,sessionListProjection:he,waitForDispatch:Z,responseId:Ie,managedRoomContextToken:M,suppressExternalOwnerEcho:Re,queuedBatchItems:F,managedWorkerParentSessionId:h,managerWorkerNativeFull:G,managedRecoverySource:b}=n.params,c=Ie||n.id;Pe(he);const T=null,B="";if(t?.source==="room_activation"&&(Fe(t,Q,M),A==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(je(e,n.id),!o||!x){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:"sessionId and text are required"});return}if(e.recoveryBarrier?.isDispatchFrozen()){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:"upgrade_in_progress"});return}e.recoveryBarrier?.isRecovering(o)&&await e.recoveryBarrier.wait(o);const d=A,S=d==="manager"||fe==="manager"?"manager":"standard";S==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(o,q??null,W??null);const P=(d==="claude"||d==="codex"||d==="workbuddy"||d==="pi-coding-agent"||d.startsWith("acp:"))&&typeof j=="string"&&j.trim()?j.trim():void 0,p=e.resolvePath($e(d,ye||se.homedir(),u)||se.homedir()),f=t?.managedAgentPolicy??(h?e.managerRuntime?.getManagedAgentPolicyForWorker(h,p)??null:null);if(h&&!G&&!f)throw new Error("managed_worker_permission_context_unavailable");const J=d==="codex"&&(t?.source==="room_activation"||h)?"internal":"user",H=h&&f?Te({requestedPolicy:f.requestedPolicy,enforcement:f.enforcement,resolvedWorkDir:f.adapterPolicy.workDir}):Q,Se=p,C=De(n.params.attachments);C?.length&&Ee(p,C);const m={text:x,attachments:C,localized:!1};let r=e.sessions.get(o);if(r){if(r.lastActiveAt=Date.now(),r.agentType!==d||(r.sessionMode??(r.agentType==="manager"?"manager":"standard"))!==S||r.workDir!==p||(r.nativeSessionVisibility??"user")!==J||JSON.stringify(r.externalChannel??null)!==JSON.stringify(T??null)){e.sessions.delete(o);try{await $(e,r)}catch{e.processedReqIds.delete(n.id)}r=void 0}else if(u&&r.agentSessionId!==u)try{await r.adapter.resume(u),r.agentSessionId=u}catch{e.sessions.delete(o);try{await $(e,r)}catch{e.processedReqIds.delete(n.id)}r=void 0}}if(!r)try{r=await qe(e,o,d,S,p,u,T,B,f,J)}catch(a){const i=a instanceof Error&&a.message.startsWith("Unsupported agent:")?a.message:`Failed to start ${A}: ${a instanceof Error?a.message:String(a)}`;console.error(`[chat.send] start failed reqId=${n.id} sessionId=${o} agentType=${A} workDir=${p} agentSessionId=${u??""}: ${i}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:i,runId:"",seq:0}}),e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:i});return}const g={id:ve??`user-${n.id}`,sessionId:o,role:"user",ts:Date.now(),payload:ie(m.text,m.attachments)},X=Array.isArray(F)&&F.length>0?F.map(a=>({id:a.clientMessageId??a.id,sessionId:o,role:b==="manager_event"?"agent":"user",ts:b==="manager_event"?Date.parse(a.createdAt):g.ts,payload:ie(a.text,a.attachments)})):null,w=X??[g],z=Ae(o,g.id),Y=z.state==="queued";if(z.state!=="not_found"&&!Y){e.client.sendRes({type:"res",id:c,ok:!0,payload:z});return}if(Z){t?.source==="room_activation"&&ce(r.adapter,o,d,T,B,M,t.managedAgentPolicy,e.managedRoomExecutor);try{await We(r.adapter,m.text,v,P,m.attachments,H,t?{source:t.source}:void 0)}catch(a){e.processedReqIds.delete(n.id),e.sessions.delete(o),await $(e,r),e.client.sendRes({type:"res",id:c,ok:!1,error:a instanceof Error?a.message:String(a)});return}}E({level:"info",sessionId:o,wsEvent:"chat.send.start",metadata:{reqId:n.id,agentType:d,sessionMode:S,managerConfig:L??null,modelId:v,managerDefaultWorkerAgentType:q??null,managerDefaultWorkerModelId:W??null,reasoningEffort:P,...t?.source==="room_activation"?{managedRoomPromptSnapshot:t.managedRoomPromptSnapshot}:{}}});const k=(a,i)=>({...be(le()),sessionId:o,clientMessageId:g.id,messageId:g.id,deliveryState:a,bodyRevision:i,committedAt:new Date(g.ts).toISOString()}),N=a=>{e.nativeFusion?.registerManagedSend({sessionId:o,agentType:d,sourceAgentType:Ce(d,v),canonicalMessageId:g.id,sourceSessionKey:r.agentSessionId??u??null,text:m.text,managedEchoPolicy:a?.deliveryMode==="external_owner_queue"&&!Re?"import_following":"suppress_following",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??g.id}`:null})},ee=()=>{r.currentRunId||(r.currentRunId=`dispatch:${n.id}`,r.currentRunPhase="thinking",r.nextEventSeq=0)},ne=()=>{if(!e.runJournal||r.journalRunId)return;const a=`run:${_e()}`;e.runJournal.begin({daemonInstallationId:le(),sessionId:o,runId:a,clientMessageId:g.id,agentType:d,sessionMode:S,workDir:p,agentSessionId:r.agentSessionId??u??null,modelId:v??null,reasoningEffort:P??null,nativeSessionVisibility:J,providerRunId:null,source:b==="manager_event"?"manager_event":t?.source==="room_activation"?b==="managed_direct"?"managed_direct":"managed_room":h?"manager_worker":"personal",parentSessionId:h??null,managerWorkerNativeFull:G===!0,roomContextRegistryKey:typeof n.params.roomContextRegistryKey=="string"?n.params.roomContextRegistryKey:null,activationCauseMessageId:typeof n.params.activationCauseMessageId=="string"?n.params.activationCauseMessageId:null,rootCauseMessageId:typeof n.params.rootCauseMessageId=="string"?n.params.rootCauseMessageId:null,activationSequence:typeof n.params.activationSequence=="string"?n.params.activationSequence:null,activationHopCount:typeof n.params.activationHopCount=="number"?n.params.activationHopCount:null,managedPolicyVersion:t?.managedRoomPromptSnapshot.managedRoomPolicyVersion??null,managedRuntimePolicyHash:t?.managedRoomPromptSnapshot.runtimePermissionPolicyHash??(f?V("sha256").update(JSON.stringify({requestedPolicy:f.requestedPolicy,enforcement:f.enforcement})).digest("hex"):null)}),r.journalRunId=a},ae=()=>{if(N(),Y){let a=0;for(const[i,s]of w.entries()){const y=I(o,s,"queued");i===0&&(a=y)}return k("queued",a)}try{Be(e,{sessionId:o,agentType:d,sessionMode:S,managerConfig:L??null,workDir:Se,agentSessionId:r.agentSessionId??u??null,modelId:v,managerDefaultWorkerAgentType:q??null,managerDefaultWorkerModelId:W??null});let a=0;for(const[i,s]of w.entries()){const y=X?I(o,s,"committed"):de(o,s,"committed");i===0&&(a=y)}return xe(p,C??[]),R(e.client),k("committed",a)}catch(a){throw e.nativeFusion?.cancelManagedSend?.(g.id),a}},oe=a=>{r.journalRunId&&e.runJournal?.update(o,r.journalRunId,{deliveryMode:a?.deliveryMode==="external_owner_queue"?"external_owner_queue":"direct",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??g.id}`:null})},te=a=>{r.currentRunId||(r.nextEventSeq=0),oe(a),N(a);const i=a?.deliveryMode==="external_owner_queue"?"queued":"executing";let s=0;for(const[y,D]of w.entries()){const _=I(o,D,i);y===0&&(s=_)}return R(e.client),k(i,s)},U=async(a,i)=>{e.nativeFusion?.cancelManagedSend?.(g.id);const s=pe(d,a);if(console.error(`[chat.send] send failed reqId=${n.id} sessionId=${o} agentType=${A} workDir=${p} agentSessionId=${r.agentSessionId??u??""}: ${s}`),r.adapter.hasPendingAuthenticationRetry?.()===!0){i&&e.client.sendRes({type:"res",id:c,ok:!0,payload:k("queued",0)});return}e.sessions.delete(o),r.journalRunId&&(e.runJournal?.update(o,r.journalRunId,{phase:"terminal"}),e.runJournal?.remove(o,r.journalRunId),e.recoveryBarrier?.end(o),r.journalRunId=null);try{await $(e,r)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:s,runId:"",seq:0}}),!i){const D={id:`agent-error-${n.id}-${Date.now()}`,sessionId:o,role:"agent",ts:Date.now(),payload:s};de(o,D),R(e.client)}i&&(e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:s}))};if(Z){try{ae()}catch(s){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:`Durable message commit failed: ${s instanceof Error?s.message:String(s)}`});return}ne(),ee();let a;try{t?.source==="room_activation"&&(ge(e,r,M),l());const s=m.attachments;a=await me(r.adapter,m.text,v,P,s,H,t?{source:t.source}:void 0),E({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}catch(s){if(r.adapter.hasPendingAuthenticationRetry?.()){let _=0;for(const[O,K]of w.entries()){const ke=I(o,K,"queued");O===0&&(_=ke)}R(e.client),await U(s,!1),e.client.sendRes({type:"res",id:c,ok:!0,payload:k("queued",_)});return}let y=0;for(const[_,O]of w.entries()){const K=I(o,O,"failed");_===0&&(y=K)}R(e.client);const D=pe(d,s);await U(s,!1),e.client.sendRes({type:"res",id:c,ok:!0,payload:{...k("failed",y),failureMessage:D}});return}const i=te(a??void 0);e.client.sendRes({type:"res",id:c,ok:!0,payload:{...i,...m.localized?{localizedAttachments:!0}:{},...a?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:a.deliveryMode,queuedSubmissionId:a.queuedSubmissionId}:{}}}),E({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});return}let re;try{re=ae()}catch(a){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:`Durable message commit failed: ${a instanceof Error?a.message:String(a)}`});return}ne(),ee(),t?.source==="room_activation"&&(ce(r.adapter,o,d,T,B,M,t.managedAgentPolicy,e.managedRoomExecutor),ge(e,r,M),l()),te(),e.client.sendRes({type:"res",id:c,ok:!0,payload:{...re,...m.localized?{localizedAttachments:!0}:{}}}),E({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});const we=m.attachments;me(r.adapter,m.text,v,P,we,H,t?{source:t.source}:void 0).then(a=>{if(a?.deliveryMode==="external_owner_queue"){oe(a),N(a);for(const i of w)I(o,i,"queued");R(e.client)}E({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}).catch(a=>{I(o,g,r.adapter.hasPendingAuthenticationRetry?.()?"queued":"failed"),R(e.client),U(a,!1)})}async function tn(e,n){const{sessionId:t,discardPending:l}=n.params,o=e.sessions.get(t);if(o){o.journalRunId&&e.runJournal?.markAborted(t,o.journalRunId),ue(e,o);try{await o.adapter.stop()}catch{e.client.sendRes({type:"res",id:n.id,ok:!1,error:"agent_stop_failed"});return}Je(e,t),e.activityPublisher?.publish(t,null)}l===!0&&e.chatQueue?.purgeSession(t),e.client.sendRes({type:"res",id:n.id,ok:!0})}export{tn as handleChatAbort,on as handleChatSend,Be as sendSessionUpdateEvent,Fe as validateManagedRoomExecutionContext};
1
+ import se from"node:os";import{createHash as K,randomUUID as ke}from"node:crypto";import{buildUserMessagePayload as ie}from"@shennian/wire";import{reportLog as E}from"../../log-reporter.js";import{appendMessage as de,getDaemonInstallationId as le,getMessageDeliveryStatus as Pe,recordSession as Ae,upsertMessage as I}from"../store.js";import{mergeProjectedSessions as Me}from"../projection.js";import{parsePersonalAttachmentReferences as De}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as Ee,markAttachmentReferences as be}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as R}from"../index-publisher.js";import{getPersonalSyncIdentity as xe}from"../../personal-sync/epoch.js";import{buildManagedAgentPermissionPrompt as Te}from"../../room/managed-prompt-composer.js";import{getNativeSourceAgentType as Ce}from"./chat-events.js";import{configureAdapterForSession as ce,maybeResolveClaudeImportedWorkDir as $e,replaceManagedRoomContextToken as ue,revokeManagedRoomContextToken as ge}from"./chat-adapter-config.js";import{createActiveSession as qe,disposeSession as $,prepareAdapterTurn as We,rememberProcessedReqId as je,sendAdapterTurn as me}from"./chat-session-lifecycle.js";function Fe(e,n,t){if(e.source!=="room_activation")throw new Error("managed_room_execution_context_required");if(!e.managedAgentPolicy)throw new Error("managed_agent_policy_missing");const l=e.managedRoomPromptSnapshot;if(!l)throw new Error("managed_room_prompt_snapshot_missing");if(!n?.trim())throw new Error("managed_room_prompt_incomplete");if(!t||!/^mrc_[A-Za-z0-9_-]{43}$/.test(t))throw new Error("managed_room_context_unavailable");const o=K("sha256").update(JSON.stringify({requestedPolicy:e.managedAgentPolicy.requestedPolicy,enforcement:e.managedAgentPolicy.enforcement})).digest("hex"),b=K("sha256").update(e.managedAgentPolicy.adapterPolicy.workDir).digest("hex");if(l.runtimePermissionPolicyHash!==o||l.resolvedWorkDirHash!==b||l.enforcementLevel!==e.managedAgentPolicy.enforcement.level)throw new Error("managed_room_prompt_snapshot_mismatch")}function pe(e,n){return`Agent send failed: ${n instanceof Error?n.message:String(n)}`}function Be(e,n){Ae({sessionId:n.sessionId,agentType:n.agentType,sessionMode:n.sessionMode,managerConfig:n.managerConfig,workDir:n.workDir,agentSessionId:n.agentSessionId??null,modelId:n.modelId??null,managerDefaultWorkerAgentType:n.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:n.managerDefaultWorkerModelId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:n.sessionId,agentType:n.agentType,sessionMode:n.sessionMode,managerConfig:n.managerConfig,agentSessionId:n.agentSessionId??null,modelId:n.modelId??null,workDir:n.workDir,status:"active",externalChannel:null}}})}function Je(e,n){const t=e.sessions.get(n),l=t?.currentRunId;if(!t||!l)return;const o=t.nextEventSeq;e.runTextAcc.delete(`${n}:${l}`),ge(e,t),t.pendingTextEvent=null,t.currentRunId=null,t.currentRunPhase=null,t.nextEventSeq=0,t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:n,runId:l,seq:o},seq:o,id:`agent-evt-${l}-${o}`}),t.journalRunId&&(e.runJournal?.remove(n,t.journalRunId),e.recoveryBarrier?.end(n),t.journalRunId=null),e.chatQueue?.noteTerminal(n)}async function on(e,n,t){const l=t?n.params.managedRoomContextToken??null:null;let o=!1;try{await He(e,n,t,()=>{o=!0})}finally{l&&!o&&e.revokeManagedRoomContextToken?.(l)}}async function He(e,n,t,l){if(e.processedReqIds.has(n.id)){e.client.sendRes({type:"res",id:n.id,ok:!0});return}const{sessionId:o,text:b,agentType:P,sessionMode:fe,managerConfig:L,workDir:ye,agentSessionId:g,modelId:v,systemPrompt:Q,managerDefaultWorkerAgentType:q,managerDefaultWorkerModelId:W,reasoningEffort:j,clientMessageId:ve,sessionListProjection:he,waitForDispatch:Z,responseId:Ie,managedRoomContextToken:A,suppressExternalOwnerEcho:Re,queuedBatchItems:F,managedWorkerParentSessionId:h,managerWorkerNativeFull:G,managedRecoverySource:x}=n.params,c=Ie||n.id;Me(he);const T=null,B="";if(t?.source==="room_activation"&&(Fe(t,Q,A),P==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(je(e,n.id),!o||!b){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:"sessionId and text are required"});return}if(e.recoveryBarrier?.isDispatchFrozen()){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:"upgrade_in_progress"});return}e.recoveryBarrier?.isRecovering(o)&&await e.recoveryBarrier.wait(o);const d=P,_=d==="manager"||fe==="manager"?"manager":"standard";_==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(o,q??null,W??null);const M=(d==="claude"||d==="codex"||d==="workbuddy"||d==="pi-coding-agent"||d.startsWith("acp:"))&&typeof j=="string"&&j.trim()?j.trim():void 0,p=e.resolvePath($e(d,ye||se.homedir(),g)||se.homedir()),f=t?.managedAgentPolicy??(h?e.managerRuntime?.getManagedAgentPolicyForWorker(h,p)??null:null);if(h&&!G&&!f)throw new Error("managed_worker_permission_context_unavailable");const J=d==="codex"&&(t?.source==="room_activation"||t?.source==="manager_event"||h)?"internal":"user",H=h&&f?Te({requestedPolicy:f.requestedPolicy,enforcement:f.enforcement,resolvedWorkDir:f.adapterPolicy.workDir}):Q,_e=p,C=De(n.params.attachments);C?.length&&Ee(p,C);const m={text:b,attachments:C,localized:!1};let r=e.sessions.get(o);if(r){if(r.lastActiveAt=Date.now(),r.agentType!==d||(r.sessionMode??(r.agentType==="manager"?"manager":"standard"))!==_||r.workDir!==p||(r.nativeSessionVisibility??"user")!==J||JSON.stringify(r.externalChannel??null)!==JSON.stringify(T??null)){e.sessions.delete(o);try{await $(e,r)}catch{e.processedReqIds.delete(n.id)}r=void 0}else if(g&&r.agentSessionId!==g)try{await r.adapter.resume(g),r.agentSessionId=g}catch{e.sessions.delete(o);try{await $(e,r)}catch{e.processedReqIds.delete(n.id)}r=void 0}}if(!r)try{r=await qe(e,o,d,_,p,g,T,B,f,J)}catch(a){const i=a instanceof Error&&a.message.startsWith("Unsupported agent:")?a.message:`Failed to start ${P}: ${a instanceof Error?a.message:String(a)}`;console.error(`[chat.send] start failed reqId=${n.id} sessionId=${o} agentType=${P} workDir=${p} agentSessionId=${g??""}: ${i}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:i,runId:"",seq:0}}),e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:i});return}const u={id:ve??`user-${n.id}`,sessionId:o,role:"user",ts:Date.now(),payload:ie(m.text,m.attachments)},X=Array.isArray(F)&&F.length>0?F.map(a=>({id:a.clientMessageId??a.id,sessionId:o,role:x==="manager_event"?"agent":"user",ts:x==="manager_event"?Date.parse(a.createdAt):u.ts,payload:ie(a.text,a.attachments)})):null,w=X??[u],z=Pe(o,u.id),Y=z.state==="queued";if(z.state!=="not_found"&&!Y){e.client.sendRes({type:"res",id:c,ok:!0,payload:z});return}if(Z){(t?.source==="room_activation"||t?.source==="manager_event")&&ce(r.adapter,o,d,T,B,t.source==="room_activation"?A:null,t.managedAgentPolicy,e.managedRoomExecutor);try{await We(r.adapter,m.text,v,M,m.attachments,H,t?.source==="room_activation"?{source:"room_activation"}:void 0)}catch(a){e.processedReqIds.delete(n.id),e.sessions.delete(o),await $(e,r),e.client.sendRes({type:"res",id:c,ok:!1,error:a instanceof Error?a.message:String(a)});return}}E({level:"info",sessionId:o,wsEvent:"chat.send.start",metadata:{reqId:n.id,agentType:d,sessionMode:_,managerConfig:L??null,modelId:v,managerDefaultWorkerAgentType:q??null,managerDefaultWorkerModelId:W??null,reasoningEffort:M,...t?.source==="room_activation"?{managedRoomPromptSnapshot:t.managedRoomPromptSnapshot}:{}}});const S=(a,i)=>({...xe(le()),sessionId:o,clientMessageId:u.id,messageId:u.id,deliveryState:a,bodyRevision:i,committedAt:new Date(u.ts).toISOString()}),N=a=>{e.nativeFusion?.registerManagedSend({sessionId:o,agentType:d,sourceAgentType:Ce(d,v),canonicalMessageId:u.id,sourceSessionKey:r.agentSessionId??g??null,text:m.text,managedEchoPolicy:a?.deliveryMode==="external_owner_queue"&&!Re?"import_following":"suppress_following",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??u.id}`:null})},ee=()=>{r.currentRunId||(r.currentRunId=`dispatch:${n.id}`,r.currentRunPhase="thinking",r.nextEventSeq=0)},ne=()=>{if(!e.runJournal||r.journalRunId)return;const a=`run:${ke()}`;e.runJournal.begin({daemonInstallationId:le(),sessionId:o,runId:a,clientMessageId:u.id,agentType:d,sessionMode:_,workDir:p,agentSessionId:r.agentSessionId??g??null,modelId:v??null,reasoningEffort:M??null,nativeSessionVisibility:J,providerRunId:null,source:x==="manager_event"?"manager_event":t?.source==="room_activation"?x==="managed_direct"?"managed_direct":"managed_room":h?"manager_worker":"personal",parentSessionId:h??null,managerWorkerNativeFull:G===!0,roomContextRegistryKey:typeof n.params.roomContextRegistryKey=="string"?n.params.roomContextRegistryKey:null,activationCauseMessageId:typeof n.params.activationCauseMessageId=="string"?n.params.activationCauseMessageId:null,rootCauseMessageId:typeof n.params.rootCauseMessageId=="string"?n.params.rootCauseMessageId:null,activationSequence:typeof n.params.activationSequence=="string"?n.params.activationSequence:null,activationHopCount:typeof n.params.activationHopCount=="number"?n.params.activationHopCount:null,managedPolicyVersion:t?.source==="room_activation"?t.managedRoomPromptSnapshot.managedRoomPolicyVersion:t?.source==="manager_event"?t.managedRoomPolicyVersion:null,managedRuntimePolicyHash:(t?.source==="room_activation"?t.managedRoomPromptSnapshot.runtimePermissionPolicyHash:t?.source==="manager_event"?t.managedRuntimePolicyHash:null)??(f?K("sha256").update(JSON.stringify({requestedPolicy:f.requestedPolicy,enforcement:f.enforcement})).digest("hex"):null)}),r.journalRunId=a},ae=()=>{if(N(),Y){let a=0;for(const[i,s]of w.entries()){const y=I(o,s,"queued");i===0&&(a=y)}return S("queued",a)}try{Be(e,{sessionId:o,agentType:d,sessionMode:_,managerConfig:L??null,workDir:_e,agentSessionId:r.agentSessionId??g??null,modelId:v,managerDefaultWorkerAgentType:q??null,managerDefaultWorkerModelId:W??null});let a=0;for(const[i,s]of w.entries()){const y=X?I(o,s,"committed"):de(o,s,"committed");i===0&&(a=y)}return be(p,C??[]),R(e.client),S("committed",a)}catch(a){throw e.nativeFusion?.cancelManagedSend?.(u.id),a}},oe=a=>{r.journalRunId&&e.runJournal?.update(o,r.journalRunId,{deliveryMode:a?.deliveryMode==="external_owner_queue"?"external_owner_queue":"direct",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??u.id}`:null})},te=a=>{r.currentRunId||(r.nextEventSeq=0),oe(a),N(a);const i=a?.deliveryMode==="external_owner_queue"?"queued":"executing";let s=0;for(const[y,D]of w.entries()){const k=I(o,D,i);y===0&&(s=k)}return R(e.client),S(i,s)},U=async(a,i)=>{e.nativeFusion?.cancelManagedSend?.(u.id);const s=pe(d,a);if(console.error(`[chat.send] send failed reqId=${n.id} sessionId=${o} agentType=${P} workDir=${p} agentSessionId=${r.agentSessionId??g??""}: ${s}`),r.adapter.hasPendingAuthenticationRetry?.()===!0){i&&e.client.sendRes({type:"res",id:c,ok:!0,payload:S("queued",0)});return}e.sessions.delete(o),r.journalRunId&&(e.runJournal?.update(o,r.journalRunId,{phase:"terminal"}),e.runJournal?.remove(o,r.journalRunId),e.recoveryBarrier?.end(o),r.journalRunId=null);try{await $(e,r)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:s,runId:"",seq:0}}),!i){const D={id:`agent-error-${n.id}-${Date.now()}`,sessionId:o,role:"agent",ts:Date.now(),payload:s};de(o,D),R(e.client)}i&&(e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:s}))};if(Z){try{ae()}catch(s){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:`Durable message commit failed: ${s instanceof Error?s.message:String(s)}`});return}ne(),ee();let a;try{t?.source==="room_activation"&&(ue(e,r,A),l());const s=m.attachments;a=await me(r.adapter,m.text,v,M,s,H,t?.source==="room_activation"?{source:"room_activation"}:void 0),E({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}catch(s){if(r.adapter.hasPendingAuthenticationRetry?.()){let k=0;for(const[O,V]of w.entries()){const Se=I(o,V,"queued");O===0&&(k=Se)}R(e.client),await U(s,!1),e.client.sendRes({type:"res",id:c,ok:!0,payload:S("queued",k)});return}let y=0;for(const[k,O]of w.entries()){const V=I(o,O,"failed");k===0&&(y=V)}R(e.client);const D=pe(d,s);await U(s,!1),e.client.sendRes({type:"res",id:c,ok:!0,payload:{...S("failed",y),failureMessage:D}});return}const i=te(a??void 0);e.client.sendRes({type:"res",id:c,ok:!0,payload:{...i,...m.localized?{localizedAttachments:!0}:{},...a?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:a.deliveryMode,queuedSubmissionId:a.queuedSubmissionId}:{}}}),E({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});return}let re;try{re=ae()}catch(a){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:`Durable message commit failed: ${a instanceof Error?a.message:String(a)}`});return}ne(),ee(),t?.source==="room_activation"&&(ce(r.adapter,o,d,T,B,A,t.managedAgentPolicy,e.managedRoomExecutor),ue(e,r,A),l()),te(),e.client.sendRes({type:"res",id:c,ok:!0,payload:{...re,...m.localized?{localizedAttachments:!0}:{}}}),E({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});const we=m.attachments;me(r.adapter,m.text,v,M,we,H,t?.source==="room_activation"?{source:"room_activation"}:void 0).then(a=>{if(a?.deliveryMode==="external_owner_queue"){oe(a),N(a);for(const i of w)I(o,i,"queued");R(e.client)}E({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}).catch(a=>{I(o,u,r.adapter.hasPendingAuthenticationRetry?.()?"queued":"failed"),R(e.client),U(a,!1)})}async function tn(e,n){const{sessionId:t,discardPending:l}=n.params,o=e.sessions.get(t);if(o){o.journalRunId&&e.runJournal?.markAborted(t,o.journalRunId),ge(e,o);try{await o.adapter.stop()}catch{e.client.sendRes({type:"res",id:n.id,ok:!1,error:"agent_stop_failed"});return}Je(e,t),e.activityPublisher?.publish(t,null)}l===!0&&e.chatQueue?.purgeSession(t),e.client.sendRes({type:"res",id:n.id,ok:!0})}export{tn as handleChatAbort,on as handleChatSend,Be as sendSessionUpdateEvent,Fe as validateManagedRoomExecutionContext};
@@ -1 +1 @@
1
- import{NativeSourceDeletionStore as U}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as J}from"./queue-retry.js";import{findDuplicateQueuedMessage as E,stripLocalQueueFields as v,takeCompatibleBatch as G,formatQueuedBatch as Q,dedupeQueuedAttachments as B,classifyQueueError as F,isControllableQueueMessage as T,takeControllableMessage as H,normalizeQueuedModelId as q,resolveManagedRoomNativeSessionId as W}from"./queue-helpers.js";import{randomUUID as V}from"node:crypto";import{CODEX_DESKTOP_SEND_UNAVAILABLE as X,buildUserMessagePayload as Y}from"@shennian/wire";import{isCodexThreadTailInterrupted as Z}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as ee}from"./projection.js";import{parsePersonalAttachmentReferences as C}from"./attachment-reference.js";import{assertFinishedAttachmentReferences as L,markAttachmentReferences as b}from"./attachment-transfer-store.js";import{getPersonalSyncIdentity as R}from"../personal-sync/epoch.js";import{getDaemonInstallationId as D}from"../personal-sync/identity.js";import{appendMessage as j,getMessageDeliveryStatus as _,listSessionRecords as N,recordSession as z,tombstoneMessage as $,upsertMessage as x}from"./store.js";import{requestSessionIndexPublish as S}from"./index-publisher.js";import{purgeQueuedSessionData as te,readQueue as p,writeQueue as M}from"./queue-storage.js";import{getNativeSourceAgentType as se}from"./handlers/chat-events.js";import{purgeQueuedSessionData as Se}from"./queue-storage.js";class A extends Error{constructor(e){super(e),this.name="TerminalQueuedChatDispatchError"}}function P(){return new Date().toISOString()}function O(u){const e=P(),s=u.managedWorkerParentSessionId?{kind:"manager_worker",parentSessionId:u.managedWorkerParentSessionId}:u.managerEventId?{kind:"manager_event",eventId:u.managerEventId}:u.managedAgentDirect?{kind:"managed_direct"}:{kind:"personal"};return{...R(D()),id:u.queueMessageId||u.clientMessageId||`queue-${V()}`,sessionId:u.sessionId,text:u.text,agentType:u.agentType,sessionMode:u.sessionMode,managerConfig:u.managerConfig,workDir:u.workDir,agentSessionId:u.agentSessionId??null,modelId:q(u.modelId)??null,systemPrompt:u.systemPrompt??null,managerDefaultWorkerAgentType:u.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:u.managerDefaultWorkerModelId??null,reasoningEffort:u.reasoningEffort??null,clientMessageId:u.clientMessageId??null,attachments:C(u.attachments),externalChannel:u.externalChannel??null,replyTarget:u.replyTarget??null,origin:u.origin,createdAt:e,updatedAt:e,trigger:s}}function w(u){return{id:u.clientMessageId??u.id,sessionId:u.sessionId,role:u.trigger?.kind==="manager_event"?"agent":"user",ts:Date.parse(u.createdAt),payload:Y(u.text,u.attachments)}}class ye{opts;draining=new Set;promoting=new Set;disposed=!1;retries=new J(e=>{this.wakeSession(e)});deletions=new U;inFlightMessageIds=new Map;deferredLogState=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}stop(){this.disposed=!0,this.retries.stop()}getSnapshot(e){const s=p().sessions[e]??[],t=this.inFlightMessageIds.get(e);return{...R(D()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(v),controllablePending:s.filter(o=>T(o)&&!t?.has(o.id)).map(v)}}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const s=this.opts.getRuntime(),t=p(),o=new Set,i=new Map;let n=!1;for(const d of e){const a=d.params;if(this.deletions.hasSession(a.sessionId)){delete t.sessions[a.sessionId],n=!0;continue}if(!a.sessionId||!a.text||!a.agentType||!a.workDir)throw new Error("sessionId, text, agentType and workDir are required");const g=O({...R(D()),...a});g.origin="room",g.managedRoom=d.context,g.trigger={kind:"room_activation",...d.context};const c=t.sessions[a.sessionId]??[],h=E(c,g),I=g.clientMessageId??g.id,m=_(a.sessionId,I);if(!h&&m.state==="not_found")c.push(g),t.sessions[a.sessionId]=c,i.set(`${g.sessionId}\0${I}`,g),o.add(a.sessionId),n=!0;else if(h&&m.state==="not_found")i.set(`${h.sessionId}\0${I}`,h),o.add(a.sessionId);else if(h&&m.state==="queued")o.add(a.sessionId);else if(h){const l=c.filter(k=>k.id!==h.id);l.length?t.sessions[a.sessionId]=l:delete t.sessions[a.sessionId],n=!0}}n&&M(t);let r;for(const d of o)try{for(const a of i.values())a.sessionId===d&&(j(d,w(a),"queued"),z({sessionId:d,agentType:a.agentType,sessionMode:a.sessionMode,managerConfig:a.managerConfig,workDir:a.workDir,agentSessionId:W(s,a),modelId:a.modelId}));this.logLifecycle("enqueue.persisted",d,{origin:"room",pendingCount:p().sessions[d]?.length??0}),this.broadcast(d),this.wakeSession(d)}catch(a){r=a,this.logLifecycle("enqueue.commit-failed",d,{failureClass:F(a instanceof Error?a.message:"","canonical_commit_failed")},"error"),this.retries.schedule(d,!0)}if(o.size&&S(s.client),r)throw r}async handleEnqueue(e){const s=this.opts.getRuntime(),t=e.params;if(ee(t.sessionListProjection),!t.sessionId||!t.text||!t.agentType||!t.workDir)return s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const o=t.managedAgentDirect?await this.prepareManagedAgentDirectTurn?.(t.sessionId):null;if(t.managedAgentDirect&&!o)throw new Error("managed_agent_context_unavailable");const i=o?N().find(K=>K.sessionId===t.sessionId):null,n=o?{...t,...o,agentType:o.agentType,sessionMode:o.sessionMode,managerConfig:o.managerConfig,workDir:o.workDir,agentSessionId:o.agentSessionId,modelId:i?.modelId??void 0,systemPrompt:o.systemPrompt,managerDefaultWorkerAgentType:o.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:o.managerDefaultWorkerModelId}:t,r=s.resolvePath(n.workDir),d=C(n.attachments);d?.length&&L(r,d);const a={text:n.text,attachments:d,localized:!1},g=s.sessions.get(n.sessionId);if(n.agentType==="codex"&&n.agentSessionId&&g?.currentRunId?.startsWith("dispatch:")&&Z(n.agentSessionId))return s.client.sendRes({type:"res",id:e.id,ok:!1,error:X}),!1;o?.managedRoomContextToken&&s.revokeManagedRoomContextToken?.(o.managedRoomContextToken);const c=await this.isSessionBusy(n.sessionId),h=p(),I=h.sessions[n.sessionId]??[],m=O({...n,text:a.text,attachments:a.attachments,systemPrompt:o?null:n.systemPrompt});o&&(m.managedAgentDirect=!0,m.trigger?.kind!=="manager_event"&&(m.trigger={kind:"managed_direct"}));const l=m.clientMessageId??m.id,k=_(n.sessionId,l);if(k.state!=="not_found")return s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:k.state==="queued",queueMessageId:m.id,...k,queue:this.getSnapshot(n.sessionId)}}),!0;const y=E(I,m);y||(h.sessions[n.sessionId]=[...I,m],M(h));const f=y??m;return z({sessionId:f.sessionId,agentType:f.agentType,sessionMode:f.sessionMode,managerConfig:f.managerConfig,workDir:f.workDir,agentSessionId:f.agentSessionId,modelId:f.modelId}),j(f.sessionId,w(f),"queued"),b(s.resolvePath(f.workDir),f.attachments??[]),S(s.client),this.logLifecycle("enqueue.persisted",n.sessionId,{origin:n.origin??"app",queueMessageId:f.id,pendingCount:(p().sessions[n.sessionId]??[]).length}),this.broadcast(n.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:y?.id??m.id,..._(n.sessionId,y?.clientMessageId??y?.id??m.clientMessageId??m.id),queue:this.getSnapshot(n.sessionId),...a.localized?{localizedAttachments:!0}:{}}}),c?this.retries.schedule(n.sessionId):this.wakeSession(n.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 o=p(),i=o.sessions[t.sessionId]??[],n=i.findIndex(a=>a.id===t.queueMessageId);if(n<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!T(i[n])||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 r=C(t.attachments),d=s.resolvePath(i[n].workDir);r?.length&&L(d,r),i[n]={...i[n],text:t.text,attachments:r,updatedAt:P()},o.sessions[t.sessionId]=i,M(o),x(t.sessionId,w(i[n]),"queued"),i[n].attachments?.length&&b(d,i[n].attachments??[]),S(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 o=p(),i=o.sessions[t.sessionId]??[],n=i.find(d=>d.id===t.queueMessageId);if(n&&(!T(n)||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 r=i.filter(d=>d.id!==t.queueMessageId);if(r.length===i.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}r.length?o.sessions[t.sessionId]=r:delete o.sessions[t.sessionId],M(o),$(t.sessionId,n?.clientMessageId??n?.id??t.queueMessageId),S(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,o=n=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:n});if(!t.sessionId||!t.queueMessageId){o("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){o("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let i=[];try{for(;this.draining.has(t.sessionId);)await new Promise(d=>setTimeout(d,5));if(i=H(p().sessions[t.sessionId]??[],t.queueMessageId),!i.length){o("Queued message not found");return}this.inFlightMessageIds.set(t.sessionId,new Set(i.map(d=>d.id)));const n=s.sessions.get(t.sessionId);let r="idle";if(n?.currentRunId)if(this.canInjectActiveBatch(n,i)){const d=i.length===1?i[0].text:Q(i),a=i[0].clientMessageId??i[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:i[0].agentType,sourceAgentType:se(i[0].agentType,i[0].modelId),canonicalMessageId:a,sourceSessionKey:n.agentSessionId,text:d,managedEchoPolicy:"suppress_following"});const g=Date.now();try{await n.adapter.injectActiveInput({text:d,attachments:B(i.flatMap(c=>c.attachments??[])),clientMessageId:a})}catch(c){throw s.nativeFusion?.cancelManagedSend(a),c}for(const c of i)x(t.sessionId,{...w(c),ts:g},"executing");this.removeBatch(t.sessionId,i),r="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId),await this.dispatchQueuedMessages(i),this.removeBatch(t.sessionId,i),r="restarted"}else await this.dispatchQueuedMessages(i),this.removeBatch(t.sessionId,i);S(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId),promotedMessageIds:i.map(d=>d.id),mode:r}})}catch(n){o(n instanceof Error?n.message:"queue_promotion_failed"),this.broadcast(t.sessionId)}finally{this.inFlightMessageIds.delete(t.sessionId),this.promoting.delete(t.sessionId),p().sessions[t.sessionId]?.length&&this.wakeSession(t.sessionId)}}noteTerminal(e){this.wakeSession(e)}purgeSession(e){te(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const r=p().sessions[e]?.length??0;return this.purgeSession(e),r}const s=p(),t=s.sessions[e]??[],o=this.inFlightMessageIds.get(e)??new Set,i=t.filter(r=>r.managedRoom&&!o.has(r.id));if(i.length===0)return 0;const n=t.filter(r=>!i.includes(r));n.length?s.sessions[e]=n:delete s.sessions[e],M(s);for(const r of i)$(e,r.clientMessageId??r.id);return S(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:i.length,pendingCount:n.length}),this.broadcast(e),i.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=p();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,o=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const g=p().sessions[e]?.length??0,c=this.deferredLogState.get(e),h=Date.now();(!c||c.pendingCount!==g||h-c.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:g}),this.deferredLogState.set(e,{at:h,pendingCount:g}));return}this.deferredLogState.delete(e);const i=p(),n=i.sessions[e]??[],r=n.filter(g=>{const c=g.clientMessageId??g.id,h=_(e,c).state;return h==="queued"||h==="not_found"});r.length!==n.length&&(r.length?i.sessions[e]=r:delete i.sessions[e],M(i),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:n.length-r.length,pendingCount:r.length}));const d=G(r);if(d.length===0){this.broadcast(e);return}o=d,this.inFlightMessageIds.set(e,new Set(d.map(g=>g.id))),this.logLifecycle("dispatch.start",e,{batchCount:d.length,queueMessageId:d[0].id,origin:d[0].origin??"app"});for(const g of d)x(e,w(g),"queued");await this.dispatchQueuedMessages(d);const a=this.removeBatch(e,d);this.logLifecycle("dispatch.accepted",e,{batchCount:d.length,queueMessageId:d[0].id,pendingCount:a}),this.deferredLogState.delete(e),s=a>0,this.broadcast(e)}catch(i){const n=F(i instanceof Error?i.message:String(i),i instanceof A?"terminal_dispatch_failed":"transient_dispatch_failed"),r=o[0],d=!!(r&&!r.trigger&&!r.clientMessageId&&Date.now()-Date.parse(r.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(i instanceof A||n==="managed_claim_source_binding_conflict"||d){const g=this.removeBatch(e,o);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:o.length,queueMessageId:o[0]?.id,pendingCount:g,failureClass:d?"stale_legacy_managed_worker_dispatch_failure":n},"error"),s=g>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:n},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e),p().sessions[e]?.length?s?queueMicrotask(()=>this.wakeSession(e)):this.retries.schedule(e,t):this.retries.cancel(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),o=s[0].trigger?.kind==="managed_direct"||s[0].managedAgentDirect?null:"personal";return!!(o&&s[0].systemPrompt==null&&t&&t.source===o&&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 p().sessions[e]?.length??0;const t=p(),o=new Set(s.map(n=>n.id)),i=(t.sessions[e]??[]).filter(n=>!o.has(n.id));return i.length?t.sessions[e]=i:delete t.sessions[e],M(t),i.length}logLifecycle(e,s,t,o="info"){const i=`[chat.queue] ${JSON.stringify({at:P(),event:e,sessionId:s,...t})}`;o==="error"?console.error(i):console.info(i)}async dispatchQueuedMessages(e){const s=e[0],t=e.flatMap(l=>{if(l.trigger?.kind==="room_activation"){const{kind:k,...y}=l.trigger;return[y]}return l.managedRoom?[l.managedRoom]:[]}),o=e.some(l=>l.trigger?.kind==="managed_direct"||!!l.managedAgentDirect),i=e.find(l=>l.trigger?.kind==="manager_event")?.trigger,n=e.find(l=>l.trigger?.kind==="manager_worker")?.trigger,r=t.length>0?await this.prepareManagedRoomTurn?.(t):o?await this.prepareManagedAgentDirectTurn?.(s.sessionId):void 0;if(t.length>0&&!r)throw new Error("managed_room_turn_preparer_unavailable");if(o&&!r)throw new Error("managed_agent_context_unavailable");const d=new Set((p().sessions[s.sessionId]??[]).map(l=>l.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(l=>!d.has(l.id)))throw r?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(r.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new A("queued_dispatch_cancelled");const a=r??null,g=a?.sessionMode==="manager"?a.managerConfig:null,c=e.length===1?s.text:Q(e),h=B(e.flatMap(l=>l.attachments??[])),I=o?r.agentSessionId:t.length>0?W(this.opts.getRuntime(),s):s.agentSessionId??null,m=r?N().find(l=>l.sessionId===s.sessionId):null;await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...R(D()),sessionId:s.sessionId,text:c,agentType:o?r.agentType:s.agentType,sessionMode:a?.sessionMode??s.sessionMode,managerConfig:a?g:s.managerConfig,workDir:o?r.workDir:s.workDir,agentSessionId:I,modelId:q(m?.modelId)??q(s.modelId),systemPrompt:r?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:a?a.managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:a?a.managerDefaultWorkerModelId:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:h,externalChannel:s.externalChannel,replyTarget:s.replyTarget,roomContextRegistryKey:r?.roomContextRegistryKey,activationCauseMessageId:t.at(-1)?.causeMessageId,rootCauseMessageId:t.at(-1)?.rootCauseMessageId,activationSequence:t.at(-1)?.sequence,activationHopCount:t.at(-1)?.hopCount,managedRecoverySource:i?.kind==="manager_event"?"manager_event":o?"managed_direct":t.length>0?"managed_room":void 0,managerEventId:i?.kind==="manager_event"?i.eventId:void 0,managedWorkerParentSessionId:n?.kind==="manager_worker"?n.parentSessionId:void 0,managerWorkerNativeFull:n?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isNativeSkillManager(n.parentSessionId,s.workDir)||void 0,managedRoomContextToken:r?.managedRoomContextToken,suppressExternalOwnerEcho:t.length>0||void 0,queuedBatchItems:e.map(v),waitForDispatch:!0}},r??void 0)}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 o=await t.adapter.getStatus().catch(()=>null);return o?.active?(o.runId&&(t.currentRunId=o.runId,t.currentRunPhase=o.runPhase??t.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}export{ye as ChatQueueManager,A as TerminalQueuedChatDispatchError,Se as purgeQueuedSessionData};
1
+ import{NativeSourceDeletionStore as U}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as J}from"./queue-retry.js";import{findDuplicateQueuedMessage as E,stripLocalQueueFields as D,takeCompatibleBatch as G,formatQueuedBatch as Q,dedupeQueuedAttachments as B,classifyQueueError as F,isControllableQueueMessage as T,takeControllableMessage as H,normalizeQueuedModelId as x,resolveManagedRoomNativeSessionId as W}from"./queue-helpers.js";import{randomUUID as V}from"node:crypto";import{CODEX_DESKTOP_SEND_UNAVAILABLE as X,buildUserMessagePayload as Y}from"@shennian/wire";import{isCodexThreadTailInterrupted as Z}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as ee}from"./projection.js";import{parsePersonalAttachmentReferences as C}from"./attachment-reference.js";import{assertFinishedAttachmentReferences as L,markAttachmentReferences as b}from"./attachment-transfer-store.js";import{getPersonalSyncIdentity as R}from"../personal-sync/epoch.js";import{getDaemonInstallationId as _}from"../personal-sync/identity.js";import{appendMessage as j,getMessageDeliveryStatus as v,listSessionRecords as N,recordSession as z,tombstoneMessage as $,upsertMessage as q}from"./store.js";import{requestSessionIndexPublish as S}from"./index-publisher.js";import{purgeQueuedSessionData as te,readQueue as p,writeQueue as k}from"./queue-storage.js";import{getNativeSourceAgentType as ne}from"./handlers/chat-events.js";import{purgeQueuedSessionData as Se}from"./queue-storage.js";class A extends Error{constructor(e){super(e),this.name="TerminalQueuedChatDispatchError"}}function P(){return new Date().toISOString()}function O(u){const e=P(),n=u.managedWorkerParentSessionId?{kind:"manager_worker",parentSessionId:u.managedWorkerParentSessionId}:u.managerEventId?{kind:"manager_event",eventId:u.managerEventId}:u.managedAgentDirect?{kind:"managed_direct"}:{kind:"personal"};return{...R(_()),id:u.queueMessageId||u.clientMessageId||`queue-${V()}`,sessionId:u.sessionId,text:u.text,agentType:u.agentType,sessionMode:u.sessionMode,managerConfig:u.managerConfig,workDir:u.workDir,agentSessionId:u.agentSessionId??null,modelId:x(u.modelId)??null,systemPrompt:u.systemPrompt??null,managerDefaultWorkerAgentType:u.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:u.managerDefaultWorkerModelId??null,reasoningEffort:u.reasoningEffort??null,clientMessageId:u.clientMessageId??null,attachments:C(u.attachments),externalChannel:u.externalChannel??null,replyTarget:u.replyTarget??null,origin:u.origin,createdAt:e,updatedAt:e,trigger:n}}function w(u){return{id:u.clientMessageId??u.id,sessionId:u.sessionId,role:u.trigger?.kind==="manager_event"?"agent":"user",ts:Date.parse(u.createdAt),payload:Y(u.text,u.attachments)}}class ye{opts;draining=new Set;promoting=new Set;disposed=!1;retries=new J(e=>{this.wakeSession(e)});deletions=new U;inFlightMessageIds=new Map;deferredLogState=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}stop(){this.disposed=!0,this.retries.stop()}getSnapshot(e){const n=p().sessions[e]??[],t=this.inFlightMessageIds.get(e);return{...R(_()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:n.map(D),controllablePending:n.filter(s=>T(s)&&!t?.has(s.id)).map(D)}}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const n=this.opts.getRuntime(),t=p(),s=new Set,a=new Map;let i=!1;for(const d of e){const r=d.params;if(this.deletions.hasSession(r.sessionId)){delete t.sessions[r.sessionId],i=!0;continue}if(!r.sessionId||!r.text||!r.agentType||!r.workDir)throw new Error("sessionId, text, agentType and workDir are required");const g=O({...R(_()),...r});g.origin="room",g.managedRoom=d.context,g.trigger={kind:"room_activation",...d.context};const c=t.sessions[r.sessionId]??[],m=E(c,g),I=g.clientMessageId??g.id,h=v(r.sessionId,I);if(!m&&h.state==="not_found")c.push(g),t.sessions[r.sessionId]=c,a.set(`${g.sessionId}\0${I}`,g),s.add(r.sessionId),i=!0;else if(m&&h.state==="not_found")a.set(`${m.sessionId}\0${I}`,m),s.add(r.sessionId);else if(m&&h.state==="queued")s.add(r.sessionId);else if(m){const M=c.filter(l=>l.id!==m.id);M.length?t.sessions[r.sessionId]=M:delete t.sessions[r.sessionId],i=!0}}i&&k(t);let o;for(const d of s)try{for(const r of a.values())r.sessionId===d&&(j(d,w(r),"queued"),z({sessionId:d,agentType:r.agentType,sessionMode:r.sessionMode,managerConfig:r.managerConfig,workDir:r.workDir,agentSessionId:W(n,r),modelId:r.modelId}));this.logLifecycle("enqueue.persisted",d,{origin:"room",pendingCount:p().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(s.size&&S(n.client),o)throw o}async handleEnqueue(e){const n=this.opts.getRuntime(),t=e.params;if(ee(t.sessionListProjection),!t.sessionId||!t.text||!t.agentType||!t.workDir)return n.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const s=t.managedAgentDirect?await this.prepareManagedAgentDirectTurn?.(t.sessionId):null;if(t.managedAgentDirect&&!s)throw new Error("managed_agent_context_unavailable");const a=s?N().find(K=>K.sessionId===t.sessionId):null,i=s?{...t,...s,agentType:s.agentType,sessionMode:s.sessionMode,managerConfig:s.managerConfig,workDir:s.workDir,agentSessionId:s.agentSessionId,modelId:a?.modelId??void 0,systemPrompt:s.systemPrompt,managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:s.managerDefaultWorkerModelId}:t,o=n.resolvePath(i.workDir),d=C(i.attachments);d?.length&&L(o,d);const r={text:i.text,attachments:d,localized:!1},g=n.sessions.get(i.sessionId);if(i.agentType==="codex"&&i.agentSessionId&&g?.currentRunId?.startsWith("dispatch:")&&Z(i.agentSessionId))return n.client.sendRes({type:"res",id:e.id,ok:!1,error:X}),!1;s?.managedRoomContextToken&&n.revokeManagedRoomContextToken?.(s.managedRoomContextToken);const c=await this.isSessionBusy(i.sessionId),m=p(),I=m.sessions[i.sessionId]??[],h=O({...i,text:r.text,attachments:r.attachments,systemPrompt:s?null:i.systemPrompt});s&&(h.managedAgentDirect=!0,h.trigger?.kind!=="manager_event"&&(h.trigger={kind:"managed_direct"}));const M=h.clientMessageId??h.id,l=v(i.sessionId,M);if(l.state!=="not_found")return n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:l.state==="queued",queueMessageId:h.id,...l,queue:this.getSnapshot(i.sessionId)}}),!0;const y=E(I,h);y||(m.sessions[i.sessionId]=[...I,h],k(m));const f=y??h;return z({sessionId:f.sessionId,agentType:f.agentType,sessionMode:f.sessionMode,managerConfig:f.managerConfig,workDir:f.workDir,agentSessionId:f.agentSessionId,modelId:f.modelId}),j(f.sessionId,w(f),"queued"),b(n.resolvePath(f.workDir),f.attachments??[]),S(n.client),this.logLifecycle("enqueue.persisted",i.sessionId,{origin:i.origin??"app",queueMessageId:f.id,pendingCount:(p().sessions[i.sessionId]??[]).length}),this.broadcast(i.sessionId),n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:y?.id??h.id,...v(i.sessionId,y?.clientMessageId??y?.id??h.clientMessageId??h.id),queue:this.getSnapshot(i.sessionId),...r.localized?{localizedAttachments:!0}:{}}}),c?this.retries.schedule(i.sessionId):this.wakeSession(i.sessionId),!0}async handleGet(e){const n=this.opts.getRuntime(),t=e.params;if(!t.sessionId){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleEdit(e){const n=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const s=p(),a=s.sessions[t.sessionId]??[],i=a.findIndex(r=>r.id===t.queueMessageId);if(i<0){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!T(a[i])||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId)){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=C(t.attachments),d=n.resolvePath(a[i].workDir);o?.length&&L(d,o),a[i]={...a[i],text:t.text,attachments:o,updatedAt:P()},s.sessions[t.sessionId]=a,k(s),q(t.sessionId,w(a[i]),"queued"),a[i].attachments?.length&&b(d,a[i].attachments??[]),S(n.client),this.broadcast(t.sessionId),n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const n=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const s=p(),a=s.sessions[t.sessionId]??[],i=a.find(d=>d.id===t.queueMessageId);if(i&&(!T(i)||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId))){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=a.filter(d=>d.id!==t.queueMessageId);if(o.length===a.length){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}o.length?s.sessions[t.sessionId]=o:delete s.sessions[t.sessionId],k(s),$(t.sessionId,i?.clientMessageId??i?.id??t.queueMessageId),S(n.client),this.broadcast(t.sessionId),n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handlePromote(e){const n=this.opts.getRuntime(),t=e.params,s=i=>n.client.sendRes({type:"res",id:e.id,ok:!1,error:i});if(!t.sessionId||!t.queueMessageId){s("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){s("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let a=[];try{for(;this.draining.has(t.sessionId);)await new Promise(d=>setTimeout(d,5));if(a=H(p().sessions[t.sessionId]??[],t.queueMessageId),!a.length){s("Queued message not found");return}this.inFlightMessageIds.set(t.sessionId,new Set(a.map(d=>d.id)));const i=n.sessions.get(t.sessionId);let o="idle";if(i?.currentRunId)if(this.canInjectActiveBatch(i,a)){const d=a.length===1?a[0].text:Q(a),r=a[0].clientMessageId??a[0].id;n.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:a[0].agentType,sourceAgentType:ne(a[0].agentType,a[0].modelId),canonicalMessageId:r,sourceSessionKey:i.agentSessionId,text:d,managedEchoPolicy:"suppress_following"});const g=Date.now();try{await i.adapter.injectActiveInput({text:d,attachments:B(a.flatMap(c=>c.attachments??[])),clientMessageId:r})}catch(c){throw n.nativeFusion?.cancelManagedSend(r),c}for(const c of a)q(t.sessionId,{...w(c),ts:g},"executing");this.removeBatch(t.sessionId,a),o="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId),await this.dispatchQueuedMessages(a),this.removeBatch(t.sessionId,a),o="restarted"}else await this.dispatchQueuedMessages(a),this.removeBatch(t.sessionId,a);S(n.client),this.broadcast(t.sessionId),n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId),promotedMessageIds:a.map(d=>d.id),mode:o}})}catch(i){s(i instanceof Error?i.message:"queue_promotion_failed"),this.broadcast(t.sessionId)}finally{this.inFlightMessageIds.delete(t.sessionId),this.promoting.delete(t.sessionId),p().sessions[t.sessionId]?.length&&this.wakeSession(t.sessionId)}}noteTerminal(e){this.wakeSession(e)}purgeSession(e){te(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const o=p().sessions[e]?.length??0;return this.purgeSession(e),o}const n=p(),t=n.sessions[e]??[],s=this.inFlightMessageIds.get(e)??new Set,a=t.filter(o=>o.managedRoom&&!s.has(o.id));if(a.length===0)return 0;const i=t.filter(o=>!a.includes(o));i.length?n.sessions[e]=i:delete n.sessions[e],k(n);for(const o of a)$(e,o.clientMessageId??o.id);return S(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:a.length,pendingCount:i.length}),this.broadcast(e),a.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=p();for(const n of Object.keys(e.sessions))this.wakeSession(n)}async drainNext(e){if(this.disposed||this.draining.has(e)||this.promoting.has(e))return;this.draining.add(e);let n=!1,t=!1,s=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const g=p().sessions[e]?.length??0,c=this.deferredLogState.get(e),m=Date.now();(!c||c.pendingCount!==g||m-c.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:g}),this.deferredLogState.set(e,{at:m,pendingCount:g}));return}this.deferredLogState.delete(e);const a=p(),i=a.sessions[e]??[],o=i.filter(g=>{const c=g.clientMessageId??g.id,m=v(e,c).state;return m==="queued"||m==="not_found"});o.length!==i.length&&(o.length?a.sessions[e]=o:delete a.sessions[e],k(a),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:i.length-o.length,pendingCount:o.length}));const d=G(o);if(d.length===0){this.broadcast(e);return}s=d,this.inFlightMessageIds.set(e,new Set(d.map(g=>g.id))),this.logLifecycle("dispatch.start",e,{batchCount:d.length,queueMessageId:d[0].id,origin:d[0].origin??"app"});for(const g of d)q(e,w(g),"queued");await this.dispatchQueuedMessages(d);const r=this.removeBatch(e,d);this.logLifecycle("dispatch.accepted",e,{batchCount:d.length,queueMessageId:d[0].id,pendingCount:r}),this.deferredLogState.delete(e),n=r>0,this.broadcast(e)}catch(a){const i=F(a instanceof Error?a.message:String(a),a instanceof A?"terminal_dispatch_failed":"transient_dispatch_failed"),o=s[0],d=!!(o&&!o.trigger&&!o.clientMessageId&&Date.now()-Date.parse(o.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(a instanceof A||i==="managed_claim_source_binding_conflict"||d){const g=this.removeBatch(e,s);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:s.length,queueMessageId:s[0]?.id,pendingCount:g,failureClass:d?"stale_legacy_managed_worker_dispatch_failure":i},"error"),n=g>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:i},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e),p().sessions[e]?.length?n?queueMicrotask(()=>this.wakeSession(e)):this.retries.schedule(e,t):this.retries.cancel(e)}}canInjectActiveBatch(e,n){if(!e.adapter.supportsActiveInput||!e.adapter.injectActiveInput||!e.journalRunId)return!1;const t=this.opts.getRuntime().runJournal?.get(n[0].sessionId,e.journalRunId),s=n[0].trigger?.kind==="managed_direct"||n[0].managedAgentDirect?null:"personal";return!!(s&&n[0].systemPrompt==null&&t&&t.source===s&&t.deliveryMode==="direct"&&t.agentType===n[0].agentType&&t.workDir===e.workDir&&t.agentSessionId===e.agentSessionId&&t.modelId===(n[0].modelId??null)&&t.reasoningEffort===(n[0].reasoningEffort??null))}removeBatch(e,n){if(n.length===0)return p().sessions[e]?.length??0;const t=p(),s=new Set(n.map(i=>i.id)),a=(t.sessions[e]??[]).filter(i=>!s.has(i.id));return a.length?t.sessions[e]=a:delete t.sessions[e],k(t),a.length}logLifecycle(e,n,t,s="info"){const a=`[chat.queue] ${JSON.stringify({at:P(),event:e,sessionId:n,...t})}`;s==="error"?console.error(a):console.info(a)}async dispatchQueuedMessages(e){const n=e[0],t=e.flatMap(l=>{if(l.trigger?.kind==="room_activation"){const{kind:y,...f}=l.trigger;return[f]}return!l.trigger&&l.managedRoom?[l.managedRoom]:[]}),s=e.find(l=>l.trigger?.kind==="manager_event")?.trigger,a=e.some(l=>l.trigger?.kind==="managed_direct"||!!l.managedAgentDirect),i=e.find(l=>l.trigger?.kind==="manager_worker")?.trigger,o=t.length>0?await this.prepareManagedRoomTurn?.(t):a||s?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(n.sessionId):void 0;if(t.length>0&&!o)throw new Error("managed_room_turn_preparer_unavailable");if(a&&!o)throw new Error("managed_agent_context_unavailable");if(s?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(n.sessionId)&&!o)throw new Error("managed_agent_context_unavailable");const d=new Set((p().sessions[n.sessionId]??[]).map(l=>l.id));if(this.disposed||this.deletions.hasSession(n.sessionId)||e.some(l=>!d.has(l.id)))throw o?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(o.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new A("queued_dispatch_cancelled");s?.kind==="manager_event"&&o?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(o.managedRoomContextToken);const r=o??null,g=r?.sessionMode==="manager"?r.managerConfig:null,c=e.length===1?n.text:Q(e),m=B(e.flatMap(l=>l.attachments??[])),I=a||s?.kind==="manager_event"&&!!o,h=I?o.agentSessionId:t.length>0?W(this.opts.getRuntime(),n):n.agentSessionId??null,M=o?N().find(l=>l.sessionId===n.sessionId):null;await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${n.id}-${Date.now()}`,method:"chat.send",params:{...R(_()),sessionId:n.sessionId,text:c,agentType:I?o.agentType:n.agentType,sessionMode:r?.sessionMode??n.sessionMode,managerConfig:r?g:n.managerConfig,workDir:I?o.workDir:n.workDir,agentSessionId:h,modelId:x(M?.modelId)??x(n.modelId),systemPrompt:s?.kind==="manager_event"?null:o?.systemPrompt??n.systemPrompt??null,managerDefaultWorkerAgentType:r?r.managerDefaultWorkerAgentType:n.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:r?r.managerDefaultWorkerModelId:n.managerDefaultWorkerModelId,reasoningEffort:n.reasoningEffort??void 0,clientMessageId:n.clientMessageId??n.id,attachments:m,externalChannel:s?.kind==="manager_event"?void 0:n.externalChannel,replyTarget:s?.kind==="manager_event"?void 0:n.replyTarget,roomContextRegistryKey:s?.kind==="manager_event"?void 0:o?.roomContextRegistryKey,activationCauseMessageId:s?.kind==="manager_event"?void 0:t.at(-1)?.causeMessageId,rootCauseMessageId:s?.kind==="manager_event"?void 0:t.at(-1)?.rootCauseMessageId,activationSequence:s?.kind==="manager_event"?void 0:t.at(-1)?.sequence,activationHopCount:s?.kind==="manager_event"?void 0:t.at(-1)?.hopCount,managedRecoverySource:s?.kind==="manager_event"?"manager_event":a?"managed_direct":t.length>0?"managed_room":void 0,managerEventId:s?.kind==="manager_event"?s.eventId:void 0,managedWorkerParentSessionId:i?.kind==="manager_worker"?i.parentSessionId:void 0,managerWorkerNativeFull:i?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isNativeSkillManager(i.parentSessionId,n.workDir)||void 0,managedRoomContextToken:s?.kind==="manager_event"?void 0:o?.managedRoomContextToken,suppressExternalOwnerEcho:t.length>0||void 0,queuedBatchItems:e.map(l=>{const y=D(l);return s?.kind==="manager_event"?{...y,systemPrompt:null,externalChannel:null,replyTarget:null}:y}),waitForDispatch:!0}},o??void 0)}async isSessionBusy(e){const n=this.opts.getRuntime().recoveryBarrier;if(n?.isDispatchFrozen()||n?.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 s=await t.adapter.getStatus().catch(()=>null);return s?.active?(s.runId&&(t.currentRunId=s.runId,t.currentRunPhase=s.runPhase??t.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}export{ye as ChatQueueManager,A as TerminalQueuedChatDispatchError,Se as purgeQueuedSessionData};
@@ -3,6 +3,7 @@ import type { SessionManagerRuntime } from './types.js';
3
3
  import { type ChatSendExecutionContext } from './handlers/chat.js';
4
4
  import { type PreparedManagedRoomTurn } from './queue.js';
5
5
  export declare function managedChatContext(prepared: PreparedManagedRoomTurn): ChatSendExecutionContext;
6
+ export declare function managedManagerEventContext(prepared: PreparedManagedRoomTurn): ChatSendExecutionContext;
6
7
  export declare function dispatchManagedChat(runtime: SessionManagerRuntime, req: ReqFrame, prepared: PreparedManagedRoomTurn): Promise<void>;
7
8
  export declare function dispatchQueuedChat(runtime: SessionManagerRuntime, req: ReqFrame, prepared?: PreparedManagedRoomTurn): Promise<void>;
8
9
  export declare function interruptQueuedChat(runtime: SessionManagerRuntime, sessionId: string): Promise<void>;
@@ -1 +1 @@
1
- import{handleChatAbort as s,handleChatSend as u}from"./handlers/chat.js";import{TerminalQueuedChatDispatchError as f}from"./queue.js";function d(e){return{source:"room_activation",managedAgentPolicy:e.managedAgentPolicy,managedRoomPromptSnapshot:e.managedRoomPromptSnapshot}}async function p(e,l,t){await u(e,l,d(t))}async function v(e,l,t){const n={value:null},a=new Proxy(e.client,{get:(o,i)=>{if(i==="sendRes")return r=>{n.value=r};const c=Reflect.get(o,i,o);return typeof c=="function"?c.bind(o):c}});if(await u({...e,client:a},l,t?d(t):void 0),n.value?.payload?.deliveryState==="failed")throw new f(n.value.payload.failureMessage??n.value.error??"queued_chat_dispatch_failed");if(!n.value?.ok)throw new Error(n.value?.error??"queued_chat_dispatch_failed")}async function w(e,l){const t={value:null},n=new Proxy(e.client,{get:(a,o)=>o==="sendRes"?i=>{t.value=i}:typeof Reflect.get(a,o,a)=="function"?Reflect.get(a,o,a).bind(a):Reflect.get(a,o,a)});if(await s({...e,client:n},{type:"req",id:`queue-promote-abort-${Date.now()}`,method:"chat.abort",params:{sessionId:l,discardPending:!1}}),!t.value?.ok)throw new Error(t.value?.error??"agent_stop_failed")}export{p as dispatchManagedChat,v as dispatchQueuedChat,w as interruptQueuedChat,d as managedChatContext};
1
+ import{handleChatAbort as m,handleChatSend as u}from"./handlers/chat.js";import{TerminalQueuedChatDispatchError as s}from"./queue.js";function d(e){return{source:"room_activation",managedAgentPolicy:e.managedAgentPolicy,managedRoomPromptSnapshot:e.managedRoomPromptSnapshot}}function h(e){return{source:"manager_event",managedAgentPolicy:e.managedAgentPolicy,managedRoomPolicyVersion:e.managedRoomPromptSnapshot.managedRoomPolicyVersion,managedRuntimePolicyHash:e.managedRoomPromptSnapshot.runtimePermissionPolicyHash}}async function v(e,i,a){await u(e,i,d(a))}async function y(e,i,a){const o={value:null},n=new Proxy(e.client,{get:(t,l)=>{if(l==="sendRes")return r=>{o.value=r};const c=Reflect.get(t,l,t);return typeof c=="function"?c.bind(t):c}});if(await u({...e,client:n},i,a?i.params.managedRecoverySource==="manager_event"?h(a):d(a):void 0),o.value?.payload?.deliveryState==="failed")throw new s(o.value.payload.failureMessage??o.value.error??"queued_chat_dispatch_failed");if(!o.value?.ok)throw new Error(o.value?.error??"queued_chat_dispatch_failed")}async function P(e,i){const a={value:null},o=new Proxy(e.client,{get:(n,t)=>t==="sendRes"?l=>{a.value=l}:typeof Reflect.get(n,t,n)=="function"?Reflect.get(n,t,n).bind(n):Reflect.get(n,t,n)});if(await m({...e,client:o},{type:"req",id:`queue-promote-abort-${Date.now()}`,method:"chat.abort",params:{sessionId:i,discardPending:!1}}),!a.value?.ok)throw new Error(a.value?.error??"agent_stop_failed")}export{v as dispatchManagedChat,y as dispatchQueuedChat,P as interruptQueuedChat,d as managedChatContext,h as managedManagerEventContext};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.4.59",
3
+ "version": "0.4.61",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {