shennian 0.4.71 → 0.4.72

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.
@@ -1 +1 @@
1
- import{NativeSourceDeletionStore as U}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as V}from"./queue-retry.js";import{findDuplicateQueuedMessage as Q,stripLocalQueueFields as _,takeCompatibleBatch as X,formatQueuedBatch as P,dedupeQueuedAttachments as B,classifyQueueError as F,isControllableQueueMessage as v,takeControllableMessage as Y,normalizeQueuedModelId as W,resolveManagedRoomNativeSessionId as T,managedDirectTurnOptions as L}from"./queue-helpers.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as Z}from"@shennian/wire";import{isCodexThreadTailInterrupted as ee}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as te}from"./projection.js";import{assertFinishedAttachmentReferences as j,markAttachmentReferences as O}from"./attachment-transfer-store.js";import{appendMessage as N,getMessageDeliveryStatus as x,listSessionRecords as z,recordSession as $,tombstoneMessage as J,upsertMessage as C}from"./store.js";import{requestSessionIndexPublish as w}from"./index-publisher.js";import{clearPersistedManagerRoomOrigins as se,purgeQueuedSessionData as ne,readQueue as m,writeQueue as y}from"./queue-storage.js";import{getNativeSourceAgentType as ie}from"./handlers/chat-events.js";import{parsePersonalAttachmentReferences as K}from"./attachment-reference.js";import{getPersonalSyncIdentity as b}from"../personal-sync/epoch.js";import{getDaemonInstallationId as A}from"../personal-sync/identity.js";import{isTrustedManagerEvent as ae}from"./manager-event-authority.js";import{TerminalQueuedChatDispatchError as D,nowIso as E,queueEnvelope as R,queueMessageFromParams as G}from"./queue-message.js";import{purgeQueuedSessionData as xe}from"./queue-storage.js";import{TerminalQueuedChatDispatchError as qe}from"./queue-message.js";class Re{opts;validateScheduledTarget;async enqueueScheduled(e){return this.handleEnqueue(e,!0)}draining=new Set;promoting=new Set;disposed=!1;retries=new V(e=>{this.wakeSession(e)});deletions=new U;inFlightMessageIds=new Map;deferredLogState=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,se(),setTimeout(()=>this.drainIdleQueues(),0).unref?.()}stop(){this.disposed=!0,this.retries.stop()}getSnapshot(e){const s=m().sessions[e]??[],t=this.inFlightMessageIds.get(e),i=this.isImmediateSendAvailable(e),n=s.filter(a=>v(a)).map(a=>({..._(a),pendingState:a.externalQueuedSubmissionId?"provider_queued":t?.has(a.id)?"submitting":"waiting",canDelete:!a.externalQueuedSubmissionId&&!t?.has(a.id),canPromote:!a.externalQueuedSubmissionId&&!t?.has(a.id)&&i}));return{...b(A()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(_),controllablePending:s.filter(a=>v(a)&&!a.externalQueuedSubmissionId&&!t?.has(a.id)).map(_),visiblePending:n}}isImmediateSendAvailable(e){const s=this.opts.getRuntime(),t=s.sessions.get(e);return t?.currentRunId?t.journalRunId?s.runJournal?.get(e,t.journalRunId)?.deliveryMode==="direct":!1:!0}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const s=this.opts.getRuntime(),t=m(),i=new Set,n=new Map;let a=!1;for(const d of e){const r=d.params;if(this.deletions.hasSession(r.sessionId)){delete t.sessions[r.sessionId],a=!0;continue}if(!r.sessionId||!r.text||!r.agentType||!r.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=G({...b(A()),...r});u.origin="room",u.managedRoom=d.context,u.trigger={kind:"room_activation",...d.context};const l=t.sessions[r.sessionId]??[],g=Q(l,u),f=u.clientMessageId??u.id,k=x(r.sessionId,f);if(!g&&k.state==="not_found")l.push(u),t.sessions[r.sessionId]=l,n.set(`${u.sessionId}\0${f}`,u),i.add(r.sessionId),a=!0;else if(g&&k.state==="not_found")n.set(`${g.sessionId}\0${f}`,g),i.add(r.sessionId);else if(g&&k.state==="queued")i.add(r.sessionId);else if(g){const M=l.filter(S=>S.id!==g.id);M.length?t.sessions[r.sessionId]=M:delete t.sessions[r.sessionId],a=!0}}a&&y(t);let o;for(const d of i)try{for(const r of n.values())r.sessionId===d&&(N(d,R(r),"queued"),$({sessionId:d,agentType:r.agentType,sessionMode:r.sessionMode,managerConfig:r.managerConfig,workDir:r.workDir,agentSessionId:T(s,r),modelId:r.modelId}));this.logLifecycle("enqueue.persisted",d,{origin:"room",pendingCount:m().sessions[d]?.length??0}),this.broadcast(d),this.wakeSession(d)}catch(r){o=r,this.logLifecycle("enqueue.commit-failed",d,{failureClass:F(r instanceof Error?r.message:"","canonical_commit_failed")},"error"),this.retries.schedule(d,!0)}if(i.size&&w(s.client),o)throw o}async handleEnqueue(e,s=!1){const t=this.opts.getRuntime(),i=e.params;if(i.scheduledTask&&!s)throw new Error("schedule_source_untrusted");if((i.managerEventId||i.managerEventRoomOrigin||i.managedWorkerParentSessionId||i.managerWorkerRoomOrigin||e.params.managerWorkerNativeFull||e.params.managedRecoverySource)&&!ae(e))throw new Error("manager_event_source_untrusted");const n=!!(s&&i.scheduledTask?.bindingId);if(te(i.sessionListProjection),!i.sessionId||!i.text||!i.agentType||!i.workDir)return t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const a=L(i.managerEventId?{roomOrigin:i.managerEventRoomOrigin}:null,!!i.managedAgentDirect),o=i.managedAgentDirect||n?await this.prepareManagedAgentDirectTurn?.(i.sessionId,a):null;if((i.managedAgentDirect||n)&&!o)throw new Error("managed_agent_context_unavailable");const d=o?z().find(H=>H.sessionId===i.sessionId):null,r=o?{...i,...o,agentType:o.agentType,sessionMode:o.sessionMode,managerConfig:o.managerConfig,workDir:o.workDir,agentSessionId:o.agentSessionId?.startsWith("managed:")?d?.agentSessionId&&!d.agentSessionId.startsWith("managed:")?d.agentSessionId:null:o.agentSessionId,modelId:d?.modelId??void 0,systemPrompt:o.systemPrompt,externalChannel:i.managedAgentDirect?null:i.externalChannel,replyTarget:i.managedAgentDirect?null:i.replyTarget,managerDefaultWorkerAgentType:o.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:o.managerDefaultWorkerModelId}:i,u=t.resolvePath(r.workDir),l=K(r.attachments);l?.length&&j(u,l);const g={text:r.text,attachments:l,localized:!1},f=t.sessions.get(r.sessionId);if(r.agentType==="codex"&&r.agentSessionId&&f?.currentRunId?.startsWith("dispatch:")&&ee(r.agentSessionId))return t.client.sendRes({type:"res",id:e.id,ok:!1,error:Z}),!1;o?.managedRoomContextToken&&t.revokeManagedRoomContextToken?.(o.managedRoomContextToken);const k=await this.isSessionBusy(r.sessionId),M=m(),S=M.sessions[r.sessionId]??[],h=G({...r,text:g.text,attachments:g.attachments,systemPrompt:o?null:r.systemPrompt});o&&(i.managedAgentDirect&&(h.managedAgentDirect=!0),h.trigger?.kind!=="manager_event"&&h.trigger?.kind!=="scheduled"&&(h.trigger={kind:"managed_direct"}));const q=h.clientMessageId??h.id,c=x(r.sessionId,q);if(c.state!=="not_found")return t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:c.state==="queued",queueMessageId:h.id,...c,queue:this.getSnapshot(r.sessionId)}}),!0;const I=Q(S,h);I||(M.sessions[r.sessionId]=[...S,h],y(M));const p=I??h;return $({sessionId:p.sessionId,agentType:p.agentType,sessionMode:p.sessionMode,managerConfig:p.managerConfig,workDir:p.workDir,agentSessionId:p.agentSessionId,modelId:p.modelId}),N(p.sessionId,R(p),"queued"),O(t.resolvePath(p.workDir),p.attachments??[]),w(t.client),this.logLifecycle("enqueue.persisted",r.sessionId,{origin:r.origin??"app",queueMessageId:p.id,pendingCount:(m().sessions[r.sessionId]??[]).length}),this.broadcast(r.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:I?.id??h.id,...x(r.sessionId,I?.clientMessageId??I?.id??h.clientMessageId??h.id),queue:this.getSnapshot(r.sessionId),...g.localized?{localizedAttachments:!0}:{}}}),k?this.retries.schedule(r.sessionId):this.wakeSession(r.sessionId),!0}async handleGet(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleEdit(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],a=n.findIndex(r=>r.id===t.queueMessageId);if(a<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!v(n[a])||n[a].externalQueuedSubmissionId||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId)){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=K(t.attachments),d=s.resolvePath(n[a].workDir);o?.length&&j(d,o),n[a]={...n[a],text:t.text,attachments:o,updatedAt:E()},i.sessions[t.sessionId]=n,y(i),C(t.sessionId,R(n[a]),"queued"),n[a].attachments?.length&&O(d,n[a].attachments??[]),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],a=n.find(d=>d.id===t.queueMessageId);if(a&&(!v(a)||a.externalQueuedSubmissionId||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId))){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=n.filter(d=>d.id!==t.queueMessageId);if(o.length===n.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}o.length?i.sessions[t.sessionId]=o:delete i.sessions[t.sessionId],y(i),J(t.sessionId,a?.clientMessageId??a?.id??t.queueMessageId),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handlePromote(e){const s=this.opts.getRuntime(),t=e.params,i=a=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:a});if(!t.sessionId||!t.queueMessageId){i("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){i("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let n=[];try{for(;this.draining.has(t.sessionId);)await new Promise(d=>setTimeout(d,5));if(n=Y(m().sessions[t.sessionId]??[],t.queueMessageId),!n.length){i("Queued message not found");return}if(n[0].externalQueuedSubmissionId||!this.isImmediateSendAvailable(t.sessionId)){i("queue_immediate_send_unavailable");return}this.inFlightMessageIds.set(t.sessionId,new Set(n.map(d=>d.id)));const a=s.sessions.get(t.sessionId);let o="idle";if(a?.currentRunId)if(this.canInjectActiveBatch(a,n)){const d=n.length===1?n[0].text:P(n),r=n[0].clientMessageId??n[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:n[0].agentType,sourceAgentType:ie(n[0].agentType,n[0].modelId),canonicalMessageId:r,sourceSessionKey:a.agentSessionId,text:d,managedEchoPolicy:"suppress_following"});const u=Date.now();try{await a.adapter.injectActiveInput({text:d,attachments:B(n.flatMap(l=>l.attachments??[])),clientMessageId:r})}catch(l){throw s.nativeFusion?.cancelManagedSend(r),l}for(const l of n)C(t.sessionId,{...R(l),ts:u},"executing");this.removeBatch(t.sessionId,n),o="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId);const d=await this.dispatchQueuedMessages(n);d.deliveryMode==="external_owner_queue"?this.markExternalQueued(n,d.queuedSubmissionId):(this.removeBatch(t.sessionId,n),o="restarted")}else{const d=await this.dispatchQueuedMessages(n);d.deliveryMode==="external_owner_queue"?this.markExternalQueued(n,d.queuedSubmissionId):this.removeBatch(t.sessionId,n)}w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId),promotedMessageIds:n.map(d=>d.id),mode:o}})}catch(a){i(a instanceof Error?a.message:"queue_promotion_failed"),this.broadcast(t.sessionId)}finally{this.inFlightMessageIds.delete(t.sessionId),this.promoting.delete(t.sessionId),m().sessions[t.sessionId]?.length&&this.wakeSession(t.sessionId)}}noteTerminal(e){const s=m().sessions[e]?.find(t=>!!t.externalQueuedSubmissionId);s&&this.noteExternalClaim(e,s.clientMessageId??s.id),this.wakeSession(e)}purgeSession(e){ne(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const o=m().sessions[e]?.length??0;return this.purgeSession(e),o}const s=m(),t=s.sessions[e]??[],i=this.inFlightMessageIds.get(e)??new Set,n=t.filter(o=>o.managedRoom&&!i.has(o.id));if(n.length===0)return 0;const a=t.filter(o=>!n.includes(o));a.length?s.sessions[e]=a:delete s.sessions[e],y(s);for(const o of n)J(e,o.clientMessageId??o.id);return w(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:n.length,pendingCount:a.length}),this.broadcast(e),n.length}wakeSession(e){this.drainNext(e).catch(()=>{this.logLifecycle("drain.retry-required",e,{},"error"),this.retries.schedule(e,!0)})}drainIdleQueues(){if(this.disposed)return;const e=m();for(const s of Object.keys(e.sessions))this.wakeSession(s)}async drainNext(e){if(this.disposed||this.draining.has(e)||this.promoting.has(e))return;this.draining.add(e);let s=!1,t=!1,i=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const l=m().sessions[e]?.length??0,g=this.deferredLogState.get(e),f=Date.now();(!g||g.pendingCount!==l||f-g.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:l}),this.deferredLogState.set(e,{at:f,pendingCount:l}));return}this.deferredLogState.delete(e);const n=m(),a=n.sessions[e]??[],o=a.filter(l=>{const g=l.clientMessageId??l.id,f=x(e,g).state;return f==="queued"||f==="not_found"});if(o.length!==a.length&&(o.length?n.sessions[e]=o:delete n.sessions[e],y(n),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:a.length-o.length,pendingCount:o.length})),o.some(l=>!!l.externalQueuedSubmissionId)){this.broadcast(e);return}const d=X(o);if(d.length===0){this.broadcast(e);return}i=d,this.inFlightMessageIds.set(e,new Set(d.map(l=>l.id))),this.logLifecycle("dispatch.start",e,{batchCount:d.length,queueMessageId:d[0].id,origin:d[0].origin??"app"}),this.broadcast(e);for(const l of d)C(e,R(l),"queued");const r=await this.dispatchQueuedMessages(d);if(r.deliveryMode==="external_owner_queue"){this.markExternalQueued(d,r.queuedSubmissionId),this.logLifecycle("dispatch.external-queued",e,{batchCount:d.length,queueMessageId:d[0].id}),this.broadcast(e);return}const u=this.removeBatch(e,d);this.logLifecycle("dispatch.accepted",e,{batchCount:d.length,queueMessageId:d[0].id,pendingCount:u}),this.deferredLogState.delete(e),s=u>0,this.broadcast(e)}catch(n){const a=F(n instanceof Error?n.message:String(n),n instanceof D?"terminal_dispatch_failed":"transient_dispatch_failed"),o=i[0],d=!!(o&&!o.trigger&&!o.clientMessageId&&Date.now()-Date.parse(o.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(n instanceof D||a==="managed_claim_source_binding_conflict"||d){const u=this.removeBatch(e,i);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:i.length,queueMessageId:i[0]?.id,pendingCount:u,failureClass:d?"stale_legacy_managed_worker_dispatch_failure":a},"error"),s=u>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:a},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e);const n=m().sessions[e]??[];n.length?(this.broadcast(e),n.some(a=>!!a.externalQueuedSubmissionId)?this.retries.cancel(e):s?queueMicrotask(()=>this.wakeSession(e)):this.retries.schedule(e,t)):this.retries.cancel(e)}}noteExternalClaim(e,s){const t=m(),i=t.sessions[e]??[],n=i.filter(a=>(a.clientMessageId??a.id)!==s);n.length!==i.length&&(n.length?t.sessions[e]=n:delete t.sessions[e],y(t),this.broadcast(e))}canInjectActiveBatch(e,s){if(!e.adapter.supportsActiveInput||!e.adapter.injectActiveInput||!e.journalRunId)return!1;const t=this.opts.getRuntime().runJournal?.get(s[0].sessionId,e.journalRunId),i=s[0].trigger?.kind==="managed_direct"||s[0].managedAgentDirect?null:"personal";return!!(i&&s[0].systemPrompt==null&&t&&t.source===i&&t.deliveryMode==="direct"&&t.agentType===s[0].agentType&&t.workDir===e.workDir&&t.agentSessionId===e.agentSessionId&&t.modelId===(s[0].modelId??null)&&t.reasoningEffort===(s[0].reasoningEffort??null))}removeBatch(e,s){if(s.length===0)return m().sessions[e]?.length??0;const t=m(),i=new Set(s.map(a=>a.id)),n=(t.sessions[e]??[]).filter(a=>!i.has(a.id));return n.length?t.sessions[e]=n:delete t.sessions[e],y(t),n.length}logLifecycle(e,s,t,i="info"){const n=`[chat.queue] ${JSON.stringify({at:E(),event:e,sessionId:s,...t})}`;i==="error"?console.error(n):console.info(n)}async dispatchQueuedMessages(e){const s=e[0],t=s.trigger?.kind==="scheduled"?s.trigger:null;if(t&&await this.validateScheduledTarget?.({sessionId:s.sessionId,...t})!=="ready")throw new D("schedule_target_unavailable");const i=e.flatMap(c=>{if(c.trigger?.kind==="room_activation"){const{kind:I,...p}=c.trigger;return[p]}return!c.trigger&&c.managedRoom?[c.managedRoom]:[]}),n=e.find(c=>c.trigger?.kind==="manager_event")?.trigger,a=e.some(c=>c.trigger?.kind==="managed_direct"||!!c.managedAgentDirect),o=a||!!t?.bindingId,d=e.find(c=>c.trigger?.kind==="manager_worker")?.trigger,r=n?.kind==="manager_event"||a,u=i.length>0?await this.prepareManagedRoomTurn?.(i):o||n?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(s.sessionId,L(n?.kind==="manager_event"?n:null,a)):void 0;if(i.length>0&&!u)throw new Error("managed_room_turn_preparer_unavailable");if(o&&!u)throw new Error("managed_agent_context_unavailable");if(n?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(s.sessionId)&&!u)throw new Error("managed_agent_context_unavailable");const l=new Set((m().sessions[s.sessionId]??[]).map(c=>c.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(c=>!l.has(c.id)))throw u?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(u.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new D("queued_dispatch_cancelled");const g=u??null,f=g?.sessionMode==="manager"?g.managerConfig:null,k=e.length===1?s.text:P(e),M=B(e.flatMap(c=>c.attachments??[])),S=o||n?.kind==="manager_event"&&!!u,h=S?u.agentSessionId?.startsWith("managed:")?T(this.opts.getRuntime(),s):u.agentSessionId:i.length>0?T(this.opts.getRuntime(),s):s.agentSessionId??null,q=u?z().find(c=>c.sessionId===s.sessionId):null;return await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...b(A()),sessionId:s.sessionId,text:k,agentType:S?u.agentType:s.agentType,sessionMode:g?.sessionMode??s.sessionMode,managerConfig:g?f:s.managerConfig,workDir:S?u.workDir:s.workDir,agentSessionId:h,modelId:W(q?.modelId)??W(s.modelId),systemPrompt:u?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:g?g.managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:g?g.managerDefaultWorkerModelId:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:M,externalChannel:r?void 0:s.externalChannel,replyTarget:r?void 0:s.replyTarget,roomContextRegistryKey:r?void 0:u?.roomContextRegistryKey,activationCauseMessageId:r?void 0:i.at(-1)?.causeMessageId,rootCauseMessageId:r?void 0:i.at(-1)?.rootCauseMessageId,activationSequence:r?void 0:i.at(-1)?.sequence,activationHopCount:r?void 0:i.at(-1)?.hopCount,managedRecoverySource:n?.kind==="manager_event"?"manager_event":o?"managed_direct":i.length>0?"managed_room":void 0,managerEventId:n?.kind==="manager_event"?n.eventId:void 0,managedWorkerParentSessionId:d?.kind==="manager_worker"?d.parentSessionId:void 0,managerWorkerRoomOrigin:d?.kind==="manager_worker"?d.roomOrigin:void 0,managerWorkerNativeFull:d?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isNativeSkillManager(d.parentSessionId,s.workDir)||void 0,managedRoomContextToken:u?.managedRoomContextToken,scheduledDelivery:!!t,suppressExternalOwnerEcho:i.length>0||!!t||void 0,queuedBatchItems:e.map(c=>{const I=_(c);return r?{...I,systemPrompt:u?.systemPrompt??null,externalChannel:null,replyTarget:null}:I}),waitForDispatch:!0}},u??void 0)??{}}markExternalQueued(e,s){const t=m(),i=new Set(e.map(o=>o.id)),n=t.sessions[e[0].sessionId]??[];let a=!1;for(const o of n)i.has(o.id)&&(o.externalQueuedSubmissionId=s||o.id,o.updatedAt=E(),a=!0);a&&y(t)}async isSessionBusy(e){const s=this.opts.getRuntime().recoveryBarrier;if(s?.isDispatchFrozen()||s?.isRecovering(e))return!0;const t=this.opts.getRuntime().sessions.get(e);if(!t)return!1;if(t.currentRunId){if(!(t.currentRunId.startsWith("dispatch:")&&!!this.opts.getRuntime().runJournal&&!t.journalRunId)||(t.adapter.getStatus?await t.adapter.getStatus().catch(()=>null):null)?.active)return!0;this.logLifecycle("drain.recovered-stale-dispatch",e,{staleRunId:t.currentRunId}),t.currentRunId=null,t.currentRunPhase=null}if(!t.adapter.getStatus)return!1;const i=await t.adapter.getStatus().catch(()=>null);return i?.active?(i.runId&&(t.currentRunId=i.runId,t.currentRunPhase=i.runPhase??t.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}export{Re as ChatQueueManager,qe as TerminalQueuedChatDispatchError,xe as purgeQueuedSessionData};
1
+ import{NativeSourceDeletionStore as U}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as V}from"./queue-retry.js";import{findDuplicateQueuedMessage as Q,stripLocalQueueFields as _,takeCompatibleBatch as X,formatQueuedBatch as P,dedupeQueuedAttachments as B,classifyQueueError as W,isControllableQueueMessage as v,takeControllableMessage as Y,normalizeQueuedModelId as F,resolveManagedRoomNativeSessionId as T,managedDirectTurnOptions as L}from"./queue-helpers.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as Z}from"@shennian/wire";import{isCodexThreadTailInterrupted as ee}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as te}from"./projection.js";import{assertFinishedAttachmentReferences as j,markAttachmentReferences as O}from"./attachment-transfer-store.js";import{appendMessage as N,getMessageDeliveryStatus as x,listSessionRecords as z,recordSession as $,tombstoneMessage as J,upsertMessage as C}from"./store.js";import{requestSessionIndexPublish as w}from"./index-publisher.js";import{clearPersistedManagerRoomOrigins as se,purgeQueuedSessionData as ne,readQueue as m,writeQueue as y}from"./queue-storage.js";import{getNativeSourceAgentType as ie}from"./handlers/chat-events.js";import{parsePersonalAttachmentReferences as K}from"./attachment-reference.js";import{getPersonalSyncIdentity as b}from"../personal-sync/epoch.js";import{getDaemonInstallationId as E}from"../personal-sync/identity.js";import{isTrustedManagerEvent as ae}from"./manager-event-authority.js";import{TerminalQueuedChatDispatchError as D,nowIso as A,queueEnvelope as R,queueMessageFromParams as G}from"./queue-message.js";import{purgeQueuedSessionData as xe}from"./queue-storage.js";import{TerminalQueuedChatDispatchError as qe}from"./queue-message.js";class Re{opts;validateScheduledTarget;async enqueueScheduled(e){return this.handleEnqueue(e,!0)}draining=new Set;promoting=new Set;disposed=!1;retries=new V(e=>{this.wakeSession(e)});deletions=new U;inFlightMessageIds=new Map;deferredLogState=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,se(),setTimeout(()=>this.drainIdleQueues(),0).unref?.()}stop(){this.disposed=!0,this.retries.stop()}getSnapshot(e){const s=m().sessions[e]??[],t=this.inFlightMessageIds.get(e),i=this.isImmediateSendAvailable(e),n=s.filter(a=>v(a)).map(a=>({..._(a),pendingState:a.externalQueuedSubmissionId?"provider_queued":t?.has(a.id)?"submitting":"waiting",canDelete:!a.externalQueuedSubmissionId&&!t?.has(a.id),canPromote:!a.externalQueuedSubmissionId&&!t?.has(a.id)&&i}));return{...b(E()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(_),controllablePending:s.filter(a=>v(a)&&!a.externalQueuedSubmissionId&&!t?.has(a.id)).map(_),visiblePending:n}}isImmediateSendAvailable(e){const s=this.opts.getRuntime(),t=s.sessions.get(e);return t?.currentRunId?t.journalRunId?s.runJournal?.get(e,t.journalRunId)?.deliveryMode==="direct":!1:!0}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const s=this.opts.getRuntime(),t=m(),i=new Set,n=new Map;let a=!1;for(const d of e){const r=d.params;if(this.deletions.hasSession(r.sessionId)){delete t.sessions[r.sessionId],a=!0;continue}if(!r.sessionId||!r.text||!r.agentType||!r.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=G({...b(E()),...r});u.origin="room",u.managedRoom=d.context,u.trigger={kind:"room_activation",...d.context};const l=t.sessions[r.sessionId]??[],g=Q(l,u),f=u.clientMessageId??u.id,k=x(r.sessionId,f);if(!g&&k.state==="not_found")l.push(u),t.sessions[r.sessionId]=l,n.set(`${u.sessionId}\0${f}`,u),i.add(r.sessionId),a=!0;else if(g&&k.state==="not_found")n.set(`${g.sessionId}\0${f}`,g),i.add(r.sessionId);else if(g&&k.state==="queued")i.add(r.sessionId);else if(g){const M=l.filter(S=>S.id!==g.id);M.length?t.sessions[r.sessionId]=M:delete t.sessions[r.sessionId],a=!0}}a&&y(t);let o;for(const d of i)try{for(const r of n.values())r.sessionId===d&&(N(d,R(r),"queued"),$({sessionId:d,agentType:r.agentType,sessionMode:r.sessionMode,managerConfig:r.managerConfig,workDir:r.workDir,agentSessionId:T(s,r),modelId:r.modelId}));this.logLifecycle("enqueue.persisted",d,{origin:"room",pendingCount:m().sessions[d]?.length??0}),this.broadcast(d),this.wakeSession(d)}catch(r){o=r,this.logLifecycle("enqueue.commit-failed",d,{failureClass:W(r instanceof Error?r.message:"","canonical_commit_failed")},"error"),this.retries.schedule(d,!0)}if(i.size&&w(s.client),o)throw o}async handleEnqueue(e,s=!1){const t=this.opts.getRuntime(),i=e.params;if(i.scheduledTask&&!s)throw new Error("schedule_source_untrusted");if((i.managerEventId||i.managerEventRoomOrigin||i.managedWorkerParentSessionId||i.managerWorkerRoomOrigin||e.params.managerWorkerNativeFull||e.params.managedRecoverySource)&&!ae(e))throw new Error("manager_event_source_untrusted");const n=!!(s&&i.scheduledTask?.bindingId);if(te(i.sessionListProjection),!i.sessionId||!i.text||!i.agentType||!i.workDir)return t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const a=L(i.managerEventId?{roomOrigin:i.managerEventRoomOrigin}:null,!!i.managedAgentDirect),o=i.managedAgentDirect||n?await this.prepareManagedAgentDirectTurn?.(i.sessionId,a):null;if((i.managedAgentDirect||n)&&!o)throw new Error("managed_agent_context_unavailable");const d=o?z().find(H=>H.sessionId===i.sessionId):null,r=o?{...i,...o,agentType:o.agentType,sessionMode:o.sessionMode,managerConfig:o.managerConfig,workDir:o.workDir,agentSessionId:o.agentSessionId?.startsWith("managed:")?d?.agentSessionId&&!d.agentSessionId.startsWith("managed:")?d.agentSessionId:null:o.agentSessionId,modelId:d?.modelId??void 0,systemPrompt:o.systemPrompt,externalChannel:i.managedAgentDirect?null:i.externalChannel,replyTarget:i.managedAgentDirect?null:i.replyTarget,managerDefaultWorkerAgentType:o.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:o.managerDefaultWorkerModelId}:i,u=t.resolvePath(r.workDir),l=K(r.attachments);l?.length&&j(u,l);const g={text:r.text,attachments:l,localized:!1},f=t.sessions.get(r.sessionId);if(r.agentType==="codex"&&r.agentSessionId&&f?.currentRunId?.startsWith("dispatch:")&&ee(r.agentSessionId))return t.client.sendRes({type:"res",id:e.id,ok:!1,error:Z}),!1;o?.managedRoomContextToken&&t.revokeManagedRoomContextToken?.(o.managedRoomContextToken);const k=await this.isSessionBusy(r.sessionId),M=m(),S=M.sessions[r.sessionId]??[],h=G({...r,text:g.text,attachments:g.attachments,systemPrompt:o?null:r.systemPrompt});o&&(i.managedAgentDirect&&(h.managedAgentDirect=!0),h.trigger?.kind!=="manager_event"&&h.trigger?.kind!=="scheduled"&&(h.trigger={kind:"managed_direct"}));const q=h.clientMessageId??h.id,c=x(r.sessionId,q);if(c.state!=="not_found")return t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:c.state==="queued",queueMessageId:h.id,...c,queue:this.getSnapshot(r.sessionId)}}),!0;const I=Q(S,h);I||(M.sessions[r.sessionId]=[...S,h],y(M));const p=I??h;return $({sessionId:p.sessionId,agentType:p.agentType,sessionMode:p.sessionMode,managerConfig:p.managerConfig,workDir:p.workDir,agentSessionId:p.agentSessionId,modelId:p.modelId}),N(p.sessionId,R(p),"queued"),O(t.resolvePath(p.workDir),p.attachments??[]),w(t.client),this.logLifecycle("enqueue.persisted",r.sessionId,{origin:r.origin??"app",queueMessageId:p.id,pendingCount:(m().sessions[r.sessionId]??[]).length}),this.broadcast(r.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:I?.id??h.id,...x(r.sessionId,I?.clientMessageId??I?.id??h.clientMessageId??h.id),queue:this.getSnapshot(r.sessionId),...g.localized?{localizedAttachments:!0}:{}}}),k?this.retries.schedule(r.sessionId):this.wakeSession(r.sessionId),!0}async handleGet(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleEdit(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],a=n.findIndex(r=>r.id===t.queueMessageId);if(a<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!v(n[a])||n[a].externalQueuedSubmissionId||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId)){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=K(t.attachments),d=s.resolvePath(n[a].workDir);o?.length&&j(d,o),n[a]={...n[a],text:t.text,attachments:o,updatedAt:A()},i.sessions[t.sessionId]=n,y(i),C(t.sessionId,R(n[a]),"queued"),n[a].attachments?.length&&O(d,n[a].attachments??[]),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],a=n.find(d=>d.id===t.queueMessageId);if(a&&(!v(a)||a.externalQueuedSubmissionId||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId))){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=n.filter(d=>d.id!==t.queueMessageId);if(o.length===n.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}o.length?i.sessions[t.sessionId]=o:delete i.sessions[t.sessionId],y(i),J(t.sessionId,a?.clientMessageId??a?.id??t.queueMessageId),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handlePromote(e){const s=this.opts.getRuntime(),t=e.params,i=a=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:a});if(!t.sessionId||!t.queueMessageId){i("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){i("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let n=[];try{for(;this.draining.has(t.sessionId);)await new Promise(d=>setTimeout(d,5));if(n=Y(m().sessions[t.sessionId]??[],t.queueMessageId),!n.length){i("Queued message not found");return}if(n[0].externalQueuedSubmissionId||!this.isImmediateSendAvailable(t.sessionId)){i("queue_immediate_send_unavailable");return}this.inFlightMessageIds.set(t.sessionId,new Set(n.map(d=>d.id)));const a=s.sessions.get(t.sessionId);let o="idle";if(a?.currentRunId)if(this.canInjectActiveBatch(a,n)){const d=n.length===1?n[0].text:P(n),r=n[0].clientMessageId??n[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:n[0].agentType,sourceAgentType:ie(n[0].agentType,n[0].modelId),canonicalMessageId:r,sourceSessionKey:a.agentSessionId,text:d,managedEchoPolicy:"suppress_following"});const u=Date.now();try{await a.adapter.injectActiveInput({text:d,attachments:B(n.flatMap(l=>l.attachments??[])),clientMessageId:r})}catch(l){throw s.nativeFusion?.cancelManagedSend(r),l}for(const l of n)C(t.sessionId,{...R(l),ts:u},"executing");this.removeBatch(t.sessionId,n),o="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId);const d=await this.dispatchQueuedMessages(n);d.deliveryMode==="external_owner_queue"?this.markExternalQueued(n,d.queuedSubmissionId):(this.removeBatch(t.sessionId,n),o="restarted")}else{const d=await this.dispatchQueuedMessages(n);d.deliveryMode==="external_owner_queue"?this.markExternalQueued(n,d.queuedSubmissionId):this.removeBatch(t.sessionId,n)}w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId),promotedMessageIds:n.map(d=>d.id),mode:o}})}catch(a){i(a instanceof Error?a.message:"queue_promotion_failed"),this.broadcast(t.sessionId)}finally{this.inFlightMessageIds.delete(t.sessionId),this.promoting.delete(t.sessionId),m().sessions[t.sessionId]?.length&&this.wakeSession(t.sessionId)}}noteTerminal(e){const s=m().sessions[e]?.find(t=>!!t.externalQueuedSubmissionId);s&&this.noteExternalClaim(e,s.clientMessageId??s.id),this.wakeSession(e)}purgeSession(e){ne(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const o=m().sessions[e]?.length??0;return this.purgeSession(e),o}const s=m(),t=s.sessions[e]??[],i=this.inFlightMessageIds.get(e)??new Set,n=t.filter(o=>o.managedRoom&&!i.has(o.id));if(n.length===0)return 0;const a=t.filter(o=>!n.includes(o));a.length?s.sessions[e]=a:delete s.sessions[e],y(s);for(const o of n)J(e,o.clientMessageId??o.id);return w(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:n.length,pendingCount:a.length}),this.broadcast(e),n.length}wakeSession(e){this.drainNext(e).catch(()=>{this.logLifecycle("drain.retry-required",e,{},"error"),this.retries.schedule(e,!0)})}drainIdleQueues(){if(this.disposed)return;const e=m();for(const s of Object.keys(e.sessions))this.wakeSession(s)}async drainNext(e){if(this.disposed||this.draining.has(e)||this.promoting.has(e))return;this.draining.add(e);let s=!1,t=!1,i=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const l=m().sessions[e]?.length??0,g=this.deferredLogState.get(e),f=Date.now();(!g||g.pendingCount!==l||f-g.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:l}),this.deferredLogState.set(e,{at:f,pendingCount:l}));return}this.deferredLogState.delete(e);const n=m(),a=n.sessions[e]??[],o=a.filter(l=>{const g=l.clientMessageId??l.id,f=x(e,g).state;return f==="queued"||f==="not_found"});if(o.length!==a.length&&(o.length?n.sessions[e]=o:delete n.sessions[e],y(n),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:a.length-o.length,pendingCount:o.length})),o.some(l=>!!l.externalQueuedSubmissionId)){this.broadcast(e);return}const d=X(o);if(d.length===0){this.broadcast(e);return}i=d,this.inFlightMessageIds.set(e,new Set(d.map(l=>l.id))),this.logLifecycle("dispatch.start",e,{batchCount:d.length,queueMessageId:d[0].id,origin:d[0].origin??"app"}),this.broadcast(e);for(const l of d)C(e,R(l),"queued");const r=await this.dispatchQueuedMessages(d);if(r.deliveryMode==="external_owner_queue"){this.markExternalQueued(d,r.queuedSubmissionId),this.logLifecycle("dispatch.external-queued",e,{batchCount:d.length,queueMessageId:d[0].id}),this.broadcast(e);return}const u=this.removeBatch(e,d);this.logLifecycle("dispatch.accepted",e,{batchCount:d.length,queueMessageId:d[0].id,pendingCount:u}),this.deferredLogState.delete(e),s=u>0,this.broadcast(e)}catch(n){const a=W(n instanceof Error?n.message:String(n),n instanceof D?"terminal_dispatch_failed":"transient_dispatch_failed"),o=i[0],d=!!(o&&!o.trigger&&!o.clientMessageId&&Date.now()-Date.parse(o.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(n instanceof D||a==="managed_claim_source_binding_conflict"||d){const u=this.removeBatch(e,i);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:i.length,queueMessageId:i[0]?.id,pendingCount:u,failureClass:d?"stale_legacy_managed_worker_dispatch_failure":a},"error"),s=u>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:a},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e);const n=m().sessions[e]??[];n.length?(this.broadcast(e),n.some(a=>!!a.externalQueuedSubmissionId)?this.retries.cancel(e):s?queueMicrotask(()=>this.wakeSession(e)):this.retries.schedule(e,t)):this.retries.cancel(e)}}noteExternalClaim(e,s){const t=m(),i=t.sessions[e]??[],n=i.filter(a=>(a.clientMessageId??a.id)!==s);n.length!==i.length&&(n.length?t.sessions[e]=n:delete t.sessions[e],y(t),this.broadcast(e))}canInjectActiveBatch(e,s){if(!e.adapter.supportsActiveInput||!e.adapter.injectActiveInput||!e.journalRunId)return!1;const t=this.opts.getRuntime().runJournal?.get(s[0].sessionId,e.journalRunId),i=s[0].trigger?.kind==="managed_direct"||s[0].managedAgentDirect?null:"personal";return!!(i&&s[0].systemPrompt==null&&t&&t.source===i&&t.deliveryMode==="direct"&&t.agentType===s[0].agentType&&t.workDir===e.workDir&&t.agentSessionId===e.agentSessionId&&t.modelId===(s[0].modelId??null)&&t.reasoningEffort===(s[0].reasoningEffort??null))}removeBatch(e,s){if(s.length===0)return m().sessions[e]?.length??0;const t=m(),i=new Set(s.map(a=>a.id)),n=(t.sessions[e]??[]).filter(a=>!i.has(a.id));return n.length?t.sessions[e]=n:delete t.sessions[e],y(t),n.length}logLifecycle(e,s,t,i="info"){const n=`[chat.queue] ${JSON.stringify({at:A(),event:e,sessionId:s,...t})}`;i==="error"?console.error(n):console.info(n)}async dispatchQueuedMessages(e){const s=e[0],t=s.trigger?.kind==="scheduled"?s.trigger:null;if(t&&await this.validateScheduledTarget?.({sessionId:s.sessionId,...t})!=="ready")throw new D("schedule_target_unavailable");const i=e.flatMap(c=>{if(c.trigger?.kind==="room_activation"){const{kind:I,...p}=c.trigger;return[p]}return!c.trigger&&c.managedRoom?[c.managedRoom]:[]}),n=e.find(c=>c.trigger?.kind==="manager_event")?.trigger,a=e.some(c=>c.trigger?.kind==="managed_direct"||!!c.managedAgentDirect),o=a||!!t?.bindingId,d=e.find(c=>c.trigger?.kind==="manager_worker")?.trigger,r=n?.kind==="manager_event"||a,u=i.length>0?await this.prepareManagedRoomTurn?.(i):o||n?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(s.sessionId,L(n?.kind==="manager_event"?n:null,a)):void 0;if(i.length>0&&!u)throw new Error("managed_room_turn_preparer_unavailable");if(o&&!u)throw new Error("managed_agent_context_unavailable");if(n?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(s.sessionId)&&!u)throw new Error("managed_agent_context_unavailable");const l=new Set((m().sessions[s.sessionId]??[]).map(c=>c.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(c=>!l.has(c.id)))throw u?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(u.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new D("queued_dispatch_cancelled");const g=u??null,f=g?.sessionMode==="manager"?g.managerConfig:null,k=e.length===1?s.text:P(e),M=B(e.flatMap(c=>c.attachments??[])),S=o||n?.kind==="manager_event"&&!!u,h=S?u.agentSessionId?.startsWith("managed:")?T(this.opts.getRuntime(),s):u.agentSessionId:i.length>0?T(this.opts.getRuntime(),s):s.agentSessionId??null,q=u?z().find(c=>c.sessionId===s.sessionId):null;return await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...b(E()),sessionId:s.sessionId,text:k,agentType:S?u.agentType:s.agentType,sessionMode:g?.sessionMode??s.sessionMode,managerConfig:g?f:s.managerConfig,workDir:S?u.workDir:s.workDir,agentSessionId:h,modelId:F(q?.modelId)??F(s.modelId),systemPrompt:u?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:g?g.managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:g?g.managerDefaultWorkerModelId:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:M,externalChannel:r?void 0:s.externalChannel,replyTarget:r?void 0:s.replyTarget,roomContextRegistryKey:r?void 0:u?.roomContextRegistryKey,activationCauseMessageId:r?void 0:i.at(-1)?.causeMessageId,rootCauseMessageId:r?void 0:i.at(-1)?.rootCauseMessageId,activationSequence:r?void 0:i.at(-1)?.sequence,activationHopCount:r?void 0:i.at(-1)?.hopCount,managedRecoverySource:n?.kind==="manager_event"?"manager_event":o?"managed_direct":i.length>0?"managed_room":void 0,managerEventId:n?.kind==="manager_event"?n.eventId:void 0,managedWorkerParentSessionId:d?.kind==="manager_worker"?d.parentSessionId:void 0,managerWorkerRoomOrigin:d?.kind==="manager_worker"?d.roomOrigin:void 0,managerWorkerNativeFull:d?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isExternalWorkerController(d.parentSessionId,s.workDir)||void 0,managedRoomContextToken:u?.managedRoomContextToken,scheduledDelivery:!!t,suppressExternalOwnerEcho:i.length>0||!!t||void 0,queuedBatchItems:e.map(c=>{const I=_(c);return r?{...I,systemPrompt:u?.systemPrompt??null,externalChannel:null,replyTarget:null}:I}),waitForDispatch:!0}},u??void 0)??{}}markExternalQueued(e,s){const t=m(),i=new Set(e.map(o=>o.id)),n=t.sessions[e[0].sessionId]??[];let a=!1;for(const o of n)i.has(o.id)&&(o.externalQueuedSubmissionId=s||o.id,o.updatedAt=A(),a=!0);a&&y(t)}async isSessionBusy(e){const s=this.opts.getRuntime().recoveryBarrier;if(s?.isDispatchFrozen()||s?.isRecovering(e))return!0;const t=this.opts.getRuntime().sessions.get(e);if(!t)return!1;if(t.currentRunId){if(!(t.currentRunId.startsWith("dispatch:")&&!!this.opts.getRuntime().runJournal&&!t.journalRunId)||(t.adapter.getStatus?await t.adapter.getStatus().catch(()=>null):null)?.active)return!0;this.logLifecycle("drain.recovered-stale-dispatch",e,{staleRunId:t.currentRunId}),t.currentRunId=null,t.currentRunPhase=null}if(!t.adapter.getStatus)return!1;const i=await t.adapter.getStatus().catch(()=>null);return i?.active?(i.runId&&(t.currentRunId=i.runId,t.currentRunPhase=i.runPhase??t.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}export{Re as ChatQueueManager,qe as TerminalQueuedChatDispatchError,xe as purgeQueuedSessionData};
@@ -117,6 +117,8 @@ export type AcpExternalSessionIdentity = {
117
117
  export type AgentInfo = {
118
118
  type: AgentType;
119
119
  models: ModelInfo[];
120
+ /** True when this runtime can back an ordinary Shennian session. */
121
+ supportsStandardSession?: boolean;
120
122
  /** User-facing grouping. Omitted by older daemons and interpreted as deep. */
121
123
  integration?: AgentIntegration;
122
124
  /** Non-secret metadata used for dynamic ACP display fallbacks. */
@@ -137,6 +139,10 @@ export type AgentInfo = {
137
139
  supportsWorker?: boolean;
138
140
  /** True only after this real provider passed Manager-mode runtime validation. */
139
141
  supportsManagerMode?: boolean;
142
+ /** True when a managed Room turn can receive explicit Room tools. */
143
+ supportsRoomTools?: boolean;
144
+ /** True when the public temporary Worker controller may target this runtime. */
145
+ supportsExternalWorkerTarget?: boolean;
140
146
  /** Per-machine Agent provider config summary (additive, non-secret). */
141
147
  config?: AgentConfigSummary;
142
148
  /** Non-secret runtime version and compatibility summary. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.4.71",
3
+ "version": "0.4.72",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {