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.
- package/dist/publish-build-manifest.json +36 -31
- package/dist/src/agents/claude.js +4 -4
- package/dist/src/agents/model-registry/discovery.js +3 -3
- package/dist/src/agents/model-registry/parsers.js +4 -4
- package/dist/src/agents/model-registry/service.d.ts +6 -2
- package/dist/src/agents/model-registry/service.js +1 -1
- package/dist/src/agents/model-registry/types.d.ts +9 -1
- package/dist/src/agents/model-registry/types.js +1 -1
- package/dist/src/commands/agent-readiness.js +2 -2
- package/dist/src/commands/manager.js +1 -1
- package/dist/src/commands/worker.d.ts +11 -0
- package/dist/src/commands/worker.js +2 -0
- package/dist/src/daemon-ipc/agent-readiness.js +1 -1
- package/dist/src/index.js +3 -3
- package/dist/src/manager/prompt.d.ts +1 -1
- package/dist/src/manager/prompt.js +6 -6
- package/dist/src/manager/registry.d.ts +48 -2
- package/dist/src/manager/registry.js +1 -1
- package/dist/src/manager/runtime.d.ts +2 -2
- package/dist/src/manager/runtime.js +6 -6
- package/dist/src/manager/worker-ipc.d.ts +7 -0
- package/dist/src/manager/worker-ipc.js +1 -1
- package/dist/src/session/handlers/agent-workspace-validation.js +1 -1
- package/dist/src/session/queue.js +1 -1
- package/node_modules/@shennian/wire/dist/session.d.ts +6 -0
- package/package.json +1 -1
|
@@ -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. */
|