shennian 0.4.65 → 0.4.66

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.
@@ -801,8 +801,8 @@
801
801
  },
802
802
  {
803
803
  "file": "src/session/handlers/chat-recovery.js",
804
- "beforeBytes": 6752,
805
- "afterBytes": 3526
804
+ "beforeBytes": 6838,
805
+ "afterBytes": 3586
806
806
  },
807
807
  {
808
808
  "file": "src/session/handlers/chat-session-lifecycle.js",
@@ -931,13 +931,13 @@
931
931
  },
932
932
  {
933
933
  "file": "src/session/queue.js",
934
- "beforeBytes": 44308,
935
- "afterBytes": 19576
934
+ "beforeBytes": 44697,
935
+ "afterBytes": 19471
936
936
  },
937
937
  {
938
938
  "file": "src/session/queued-chat-dispatch.js",
939
- "beforeBytes": 2850,
940
- "afterBytes": 1594
939
+ "beforeBytes": 2902,
940
+ "afterBytes": 1626
941
941
  },
942
942
  {
943
943
  "file": "src/session/run-recovery/barrier.js",
@@ -1 +1 @@
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};
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 c}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"||e.source==="managed_direct")o.revokeManagedRoomContextToken?.(n.managedRoomContextToken),i={source:"upgrade_recovery",recovery:"upgrade"};else{const l={source:"room_activation",managedAgentPolicy:n.managedAgentPolicy,managedRoomPromptSnapshot:n.managedRoomPromptSnapshot};c(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"||e.source==="managed_direct")?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};
@@ -1 +1 @@
1
- import{NativeSourceDeletionStore as G}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as H}from"./queue-retry.js";import{findDuplicateQueuedMessage as E,stripLocalQueueFields as D,takeCompatibleBatch as U,formatQueuedBatch as P,dedupeQueuedAttachments as B,classifyQueueError as Q,isControllableQueueMessage as T,takeControllableMessage as V,normalizeQueuedModelId as F,resolveManagedRoomNativeSessionId as q}from"./queue-helpers.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as X}from"@shennian/wire";import{isCodexThreadTailInterrupted as Y}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as Z}from"./projection.js";import{assertFinishedAttachmentReferences as b,markAttachmentReferences as L}from"./attachment-transfer-store.js";import{appendMessage as W,getMessageDeliveryStatus as _,listSessionRecords as j,recordSession as N,tombstoneMessage as z,upsertMessage as C}from"./store.js";import{requestSessionIndexPublish as w}from"./index-publisher.js";import{purgeQueuedSessionData as ee,readQueue as c,writeQueue as S}from"./queue-storage.js";import{getNativeSourceAgentType as te}from"./handlers/chat-events.js";import{parsePersonalAttachmentReferences as $}from"./attachment-reference.js";import{getPersonalSyncIdentity as x}from"../personal-sync/epoch.js";import{getDaemonInstallationId as A}from"../personal-sync/identity.js";import{TerminalQueuedChatDispatchError as v,nowIso as O,queueEnvelope as R,queueMessageFromParams as K}from"./queue-message.js";import{purgeQueuedSessionData as Se}from"./queue-storage.js";import{TerminalQueuedChatDispatchError as Re}from"./queue-message.js";class ye{opts;validateScheduledTarget;async enqueueScheduled(e){return this.handleEnqueue(e,!0)}draining=new Set;promoting=new Set;disposed=!1;retries=new H(e=>{this.wakeSession(e)});deletions=new G;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=c().sessions[e]??[],t=this.inFlightMessageIds.get(e);return{...x(A()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(D),controllablePending:s.filter(r=>T(r)&&!t?.has(r.id)).map(D)}}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const s=this.opts.getRuntime(),t=c(),r=new Set,i=new Map;let a=!1;for(const n of e){const d=n.params;if(this.deletions.hasSession(d.sessionId)){delete t.sessions[d.sessionId],a=!0;continue}if(!d.sessionId||!d.text||!d.agentType||!d.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=K({...x(A()),...d});u.origin="room",u.managedRoom=n.context,u.trigger={kind:"room_activation",...n.context};const l=t.sessions[d.sessionId]??[],m=E(l,u),I=u.clientMessageId??u.id,f=_(d.sessionId,I);if(!m&&f.state==="not_found")l.push(u),t.sessions[d.sessionId]=l,i.set(`${u.sessionId}\0${I}`,u),r.add(d.sessionId),a=!0;else if(m&&f.state==="not_found")i.set(`${m.sessionId}\0${I}`,m),r.add(d.sessionId);else if(m&&f.state==="queued")r.add(d.sessionId);else if(m){const y=l.filter(h=>h.id!==m.id);y.length?t.sessions[d.sessionId]=y:delete t.sessions[d.sessionId],a=!0}}a&&S(t);let o;for(const n of r)try{for(const d of i.values())d.sessionId===n&&(W(n,R(d),"queued"),N({sessionId:n,agentType:d.agentType,sessionMode:d.sessionMode,managerConfig:d.managerConfig,workDir:d.workDir,agentSessionId:q(s,d),modelId:d.modelId}));this.logLifecycle("enqueue.persisted",n,{origin:"room",pendingCount:c().sessions[n]?.length??0}),this.broadcast(n),this.wakeSession(n)}catch(d){o=d,this.logLifecycle("enqueue.commit-failed",n,{failureClass:Q(d instanceof Error?d.message:"","canonical_commit_failed")},"error"),this.retries.schedule(n,!0)}if(r.size&&w(s.client),o)throw o}async handleEnqueue(e,s=!1){const t=this.opts.getRuntime(),r=e.params;if(r.scheduledTask&&!s)throw new Error("schedule_source_untrusted");const i=!!(s&&r.scheduledTask?.bindingId);if(Z(r.sessionListProjection),!r.sessionId||!r.text||!r.agentType||!r.workDir)return t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const a=r.managedAgentDirect||i?await this.prepareManagedAgentDirectTurn?.(r.sessionId):null;if((r.managedAgentDirect||i)&&!a)throw new Error("managed_agent_context_unavailable");const o=a?j().find(J=>J.sessionId===r.sessionId):null,n=a?{...r,...a,agentType:a.agentType,sessionMode:a.sessionMode,managerConfig:a.managerConfig,workDir:a.workDir,agentSessionId:a.agentSessionId?.startsWith("managed:")?o?.agentSessionId&&!o.agentSessionId.startsWith("managed:")?o.agentSessionId:null:a.agentSessionId,modelId:o?.modelId??void 0,systemPrompt:a.systemPrompt,managerDefaultWorkerAgentType:a.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:a.managerDefaultWorkerModelId}:r,d=t.resolvePath(n.workDir),u=$(n.attachments);u?.length&&b(d,u);const l={text:n.text,attachments:u,localized:!1},m=t.sessions.get(n.sessionId);if(n.agentType==="codex"&&n.agentSessionId&&m?.currentRunId?.startsWith("dispatch:")&&Y(n.agentSessionId))return t.client.sendRes({type:"res",id:e.id,ok:!1,error:X}),!1;a?.managedRoomContextToken&&t.revokeManagedRoomContextToken?.(a.managedRoomContextToken);const I=await this.isSessionBusy(n.sessionId),f=c(),y=f.sessions[n.sessionId]??[],h=K({...n,text:l.text,attachments:l.attachments,systemPrompt:a?null:n.systemPrompt});a&&(h.managedAgentDirect=!0,h.trigger?.kind!=="manager_event"&&h.trigger?.kind!=="scheduled"&&(h.trigger={kind:"managed_direct"}));const g=h.clientMessageId??h.id,k=_(n.sessionId,g);if(k.state!=="not_found")return t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:k.state==="queued",queueMessageId:h.id,...k,queue:this.getSnapshot(n.sessionId)}}),!0;const M=E(y,h);M||(f.sessions[n.sessionId]=[...y,h],S(f));const p=M??h;return N({sessionId:p.sessionId,agentType:p.agentType,sessionMode:p.sessionMode,managerConfig:p.managerConfig,workDir:p.workDir,agentSessionId:p.agentSessionId,modelId:p.modelId}),W(p.sessionId,R(p),"queued"),L(t.resolvePath(p.workDir),p.attachments??[]),w(t.client),this.logLifecycle("enqueue.persisted",n.sessionId,{origin:n.origin??"app",queueMessageId:p.id,pendingCount:(c().sessions[n.sessionId]??[]).length}),this.broadcast(n.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:M?.id??h.id,..._(n.sessionId,M?.clientMessageId??M?.id??h.clientMessageId??h.id),queue:this.getSnapshot(n.sessionId),...l.localized?{localizedAttachments:!0}:{}}}),I?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 r=c(),i=r.sessions[t.sessionId]??[],a=i.findIndex(d=>d.id===t.queueMessageId);if(a<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!T(i[a])||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=$(t.attachments),n=s.resolvePath(i[a].workDir);o?.length&&b(n,o),i[a]={...i[a],text:t.text,attachments:o,updatedAt:O()},r.sessions[t.sessionId]=i,S(r),C(t.sessionId,R(i[a]),"queued"),i[a].attachments?.length&&L(n,i[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 r=c(),i=r.sessions[t.sessionId]??[],a=i.find(n=>n.id===t.queueMessageId);if(a&&(!T(a)||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=i.filter(n=>n.id!==t.queueMessageId);if(o.length===i.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}o.length?r.sessions[t.sessionId]=o:delete r.sessions[t.sessionId],S(r),z(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,r=a=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:a});if(!t.sessionId||!t.queueMessageId){r("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){r("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let i=[];try{for(;this.draining.has(t.sessionId);)await new Promise(n=>setTimeout(n,5));if(i=V(c().sessions[t.sessionId]??[],t.queueMessageId),!i.length){r("Queued message not found");return}this.inFlightMessageIds.set(t.sessionId,new Set(i.map(n=>n.id)));const a=s.sessions.get(t.sessionId);let o="idle";if(a?.currentRunId)if(this.canInjectActiveBatch(a,i)){const n=i.length===1?i[0].text:P(i),d=i[0].clientMessageId??i[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:i[0].agentType,sourceAgentType:te(i[0].agentType,i[0].modelId),canonicalMessageId:d,sourceSessionKey:a.agentSessionId,text:n,managedEchoPolicy:"suppress_following"});const u=Date.now();try{await a.adapter.injectActiveInput({text:n,attachments:B(i.flatMap(l=>l.attachments??[])),clientMessageId:d})}catch(l){throw s.nativeFusion?.cancelManagedSend(d),l}for(const l of i)C(t.sessionId,{...R(l),ts:u},"executing");this.removeBatch(t.sessionId,i),o="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),o="restarted"}else await this.dispatchQueuedMessages(i),this.removeBatch(t.sessionId,i);w(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(n=>n.id),mode:o}})}catch(a){r(a instanceof Error?a.message:"queue_promotion_failed"),this.broadcast(t.sessionId)}finally{this.inFlightMessageIds.delete(t.sessionId),this.promoting.delete(t.sessionId),c().sessions[t.sessionId]?.length&&this.wakeSession(t.sessionId)}}noteTerminal(e){this.wakeSession(e)}purgeSession(e){ee(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const o=c().sessions[e]?.length??0;return this.purgeSession(e),o}const s=c(),t=s.sessions[e]??[],r=this.inFlightMessageIds.get(e)??new Set,i=t.filter(o=>o.managedRoom&&!r.has(o.id));if(i.length===0)return 0;const a=t.filter(o=>!i.includes(o));a.length?s.sessions[e]=a:delete s.sessions[e],S(s);for(const o of i)z(e,o.clientMessageId??o.id);return w(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:i.length,pendingCount:a.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=c();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,r=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const u=c().sessions[e]?.length??0,l=this.deferredLogState.get(e),m=Date.now();(!l||l.pendingCount!==u||m-l.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:u}),this.deferredLogState.set(e,{at:m,pendingCount:u}));return}this.deferredLogState.delete(e);const i=c(),a=i.sessions[e]??[],o=a.filter(u=>{const l=u.clientMessageId??u.id,m=_(e,l).state;return m==="queued"||m==="not_found"});o.length!==a.length&&(o.length?i.sessions[e]=o:delete i.sessions[e],S(i),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:a.length-o.length,pendingCount:o.length}));const n=U(o);if(n.length===0){this.broadcast(e);return}r=n,this.inFlightMessageIds.set(e,new Set(n.map(u=>u.id))),this.logLifecycle("dispatch.start",e,{batchCount:n.length,queueMessageId:n[0].id,origin:n[0].origin??"app"});for(const u of n)C(e,R(u),"queued");await this.dispatchQueuedMessages(n);const d=this.removeBatch(e,n);this.logLifecycle("dispatch.accepted",e,{batchCount:n.length,queueMessageId:n[0].id,pendingCount:d}),this.deferredLogState.delete(e),s=d>0,this.broadcast(e)}catch(i){const a=Q(i instanceof Error?i.message:String(i),i instanceof v?"terminal_dispatch_failed":"transient_dispatch_failed"),o=r[0],n=!!(o&&!o.trigger&&!o.clientMessageId&&Date.now()-Date.parse(o.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(i instanceof v||a==="managed_claim_source_binding_conflict"||n){const u=this.removeBatch(e,r);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:r.length,queueMessageId:r[0]?.id,pendingCount:u,failureClass:n?"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),c().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),r=s[0].trigger?.kind==="managed_direct"||s[0].managedAgentDirect?null:"personal";return!!(r&&s[0].systemPrompt==null&&t&&t.source===r&&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 c().sessions[e]?.length??0;const t=c(),r=new Set(s.map(a=>a.id)),i=(t.sessions[e]??[]).filter(a=>!r.has(a.id));return i.length?t.sessions[e]=i:delete t.sessions[e],S(t),i.length}logLifecycle(e,s,t,r="info"){const i=`[chat.queue] ${JSON.stringify({at:O(),event:e,sessionId:s,...t})}`;r==="error"?console.error(i):console.info(i)}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 v("schedule_target_unavailable");const r=e.flatMap(g=>{if(g.trigger?.kind==="room_activation"){const{kind:k,...M}=g.trigger;return[M]}return!g.trigger&&g.managedRoom?[g.managedRoom]:[]}),i=e.find(g=>g.trigger?.kind==="manager_event")?.trigger,a=e.some(g=>g.trigger?.kind==="managed_direct"||!!g.managedAgentDirect||g.trigger?.kind==="scheduled"&&!!g.trigger.bindingId),o=e.find(g=>g.trigger?.kind==="manager_worker")?.trigger,n=r.length>0?await this.prepareManagedRoomTurn?.(r):a||i?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(s.sessionId):void 0;if(r.length>0&&!n)throw new Error("managed_room_turn_preparer_unavailable");if(a&&!n)throw new Error("managed_agent_context_unavailable");if(i?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(s.sessionId)&&!n)throw new Error("managed_agent_context_unavailable");const d=new Set((c().sessions[s.sessionId]??[]).map(g=>g.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(g=>!d.has(g.id)))throw n?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(n.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new v("queued_dispatch_cancelled");i?.kind==="manager_event"&&n?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(n.managedRoomContextToken);const u=n??null,l=u?.sessionMode==="manager"?u.managerConfig:null,m=e.length===1?s.text:P(e),I=B(e.flatMap(g=>g.attachments??[])),f=a||i?.kind==="manager_event"&&!!n,y=f?n.agentSessionId?.startsWith("managed:")?q(this.opts.getRuntime(),s):n.agentSessionId:r.length>0?q(this.opts.getRuntime(),s):s.agentSessionId??null,h=n?j().find(g=>g.sessionId===s.sessionId):null;await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...x(A()),sessionId:s.sessionId,text:m,agentType:f?n.agentType:s.agentType,sessionMode:u?.sessionMode??s.sessionMode,managerConfig:u?l:s.managerConfig,workDir:f?n.workDir:s.workDir,agentSessionId:y,modelId:F(h?.modelId)??F(s.modelId),systemPrompt:i?.kind==="manager_event"?null:n?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:u?u.managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:u?u.managerDefaultWorkerModelId:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:I,externalChannel:i?.kind==="manager_event"?void 0:s.externalChannel,replyTarget:i?.kind==="manager_event"?void 0:s.replyTarget,roomContextRegistryKey:i?.kind==="manager_event"?void 0:n?.roomContextRegistryKey,activationCauseMessageId:i?.kind==="manager_event"?void 0:r.at(-1)?.causeMessageId,rootCauseMessageId:i?.kind==="manager_event"?void 0:r.at(-1)?.rootCauseMessageId,activationSequence:i?.kind==="manager_event"?void 0:r.at(-1)?.sequence,activationHopCount:i?.kind==="manager_event"?void 0:r.at(-1)?.hopCount,managedRecoverySource:i?.kind==="manager_event"?"manager_event":a?"managed_direct":r.length>0?"managed_room":void 0,managerEventId:i?.kind==="manager_event"?i.eventId:void 0,managedWorkerParentSessionId:o?.kind==="manager_worker"?o.parentSessionId:void 0,managerWorkerNativeFull:o?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isNativeSkillManager(o.parentSessionId,s.workDir)||void 0,managedRoomContextToken:i?.kind==="manager_event"?void 0:n?.managedRoomContextToken,scheduledDelivery:!!t,suppressExternalOwnerEcho:r.length>0||!!t||void 0,queuedBatchItems:e.map(g=>{const k=D(g);return i?.kind==="manager_event"?{...k,systemPrompt:null,externalChannel:null,replyTarget:null}:k}),waitForDispatch:!0}},n??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 r=await t.adapter.getStatus().catch(()=>null);return r?.active?(r.runId&&(t.currentRunId=r.runId,t.currentRunPhase=r.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,Re as TerminalQueuedChatDispatchError,Se as purgeQueuedSessionData};
1
+ import{NativeSourceDeletionStore as G}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as H}from"./queue-retry.js";import{findDuplicateQueuedMessage as P,stripLocalQueueFields as T,takeCompatibleBatch as U,formatQueuedBatch as B,dedupeQueuedAttachments as Q,classifyQueueError as F,isControllableQueueMessage as C,takeControllableMessage as V,normalizeQueuedModelId as b,resolveManagedRoomNativeSessionId as q}from"./queue-helpers.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as X}from"@shennian/wire";import{isCodexThreadTailInterrupted as Y}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as Z}from"./projection.js";import{assertFinishedAttachmentReferences as L,markAttachmentReferences as W}from"./attachment-transfer-store.js";import{appendMessage as j,getMessageDeliveryStatus as R,listSessionRecords as N,recordSession as z,tombstoneMessage as $,upsertMessage as x}from"./store.js";import{requestSessionIndexPublish as k}from"./index-publisher.js";import{purgeQueuedSessionData as ee,readQueue as m,writeQueue as M}from"./queue-storage.js";import{getNativeSourceAgentType as te}from"./handlers/chat-events.js";import{parsePersonalAttachmentReferences as O}from"./attachment-reference.js";import{getPersonalSyncIdentity as A}from"../personal-sync/epoch.js";import{getDaemonInstallationId as E}from"../personal-sync/identity.js";import{TerminalQueuedChatDispatchError as _,nowIso as K,queueEnvelope as w,queueMessageFromParams as J}from"./queue-message.js";import{purgeQueuedSessionData as Se}from"./queue-storage.js";import{TerminalQueuedChatDispatchError as Re}from"./queue-message.js";class ye{opts;validateScheduledTarget;async enqueueScheduled(e){return this.handleEnqueue(e,!0)}draining=new Set;promoting=new Set;disposed=!1;retries=new H(e=>{this.wakeSession(e)});deletions=new G;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=m().sessions[e]??[],t=this.inFlightMessageIds.get(e);return{...A(E()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(T),controllablePending:s.filter(i=>C(i)&&!t?.has(i.id)).map(T)}}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 o=!1;for(const a of e){const r=a.params;if(this.deletions.hasSession(r.sessionId)){delete t.sessions[r.sessionId],o=!0;continue}if(!r.sessionId||!r.text||!r.agentType||!r.workDir)throw new Error("sessionId, text, agentType and workDir are required");const d=J({...A(E()),...r});d.origin="room",d.managedRoom=a.context,d.trigger={kind:"room_activation",...a.context};const c=t.sessions[r.sessionId]??[],l=P(c,d),I=d.clientMessageId??d.id,f=R(r.sessionId,I);if(!l&&f.state==="not_found")c.push(d),t.sessions[r.sessionId]=c,n.set(`${d.sessionId}\0${I}`,d),i.add(r.sessionId),o=!0;else if(l&&f.state==="not_found")n.set(`${l.sessionId}\0${I}`,l),i.add(r.sessionId);else if(l&&f.state==="queued")i.add(r.sessionId);else if(l){const y=c.filter(h=>h.id!==l.id);y.length?t.sessions[r.sessionId]=y:delete t.sessions[r.sessionId],o=!0}}o&&M(t);let u;for(const a of i)try{for(const r of n.values())r.sessionId===a&&(j(a,w(r),"queued"),z({sessionId:a,agentType:r.agentType,sessionMode:r.sessionMode,managerConfig:r.managerConfig,workDir:r.workDir,agentSessionId:q(s,r),modelId:r.modelId}));this.logLifecycle("enqueue.persisted",a,{origin:"room",pendingCount:m().sessions[a]?.length??0}),this.broadcast(a),this.wakeSession(a)}catch(r){u=r,this.logLifecycle("enqueue.commit-failed",a,{failureClass:F(r instanceof Error?r.message:"","canonical_commit_failed")},"error"),this.retries.schedule(a,!0)}if(i.size&&k(s.client),u)throw u}async handleEnqueue(e,s=!1){const t=this.opts.getRuntime(),i=e.params;if(i.scheduledTask&&!s)throw new Error("schedule_source_untrusted");const n=!!(s&&i.scheduledTask?.bindingId);if(Z(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 o=i.managedAgentDirect||n?await this.prepareManagedAgentDirectTurn?.(i.sessionId):null;if((i.managedAgentDirect||n)&&!o)throw new Error("managed_agent_context_unavailable");const u=o?N().find(D=>D.sessionId===i.sessionId):null,a=o?{...i,...o,agentType:o.agentType,sessionMode:o.sessionMode,managerConfig:o.managerConfig,workDir:o.workDir,agentSessionId:o.agentSessionId?.startsWith("managed:")?u?.agentSessionId&&!u.agentSessionId.startsWith("managed:")?u.agentSessionId:null:o.agentSessionId,modelId:u?.modelId??void 0,systemPrompt:i.managedAgentDirect?null:o.systemPrompt,externalChannel:i.managedAgentDirect?null:i.externalChannel,replyTarget:i.managedAgentDirect?null:i.replyTarget,managerDefaultWorkerAgentType:o.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:o.managerDefaultWorkerModelId}:i,r=t.resolvePath(a.workDir),d=O(a.attachments);d?.length&&L(r,d);const c={text:a.text,attachments:d,localized:!1},l=t.sessions.get(a.sessionId);if(a.agentType==="codex"&&a.agentSessionId&&l?.currentRunId?.startsWith("dispatch:")&&Y(a.agentSessionId))return t.client.sendRes({type:"res",id:e.id,ok:!1,error:X}),!1;o?.managedRoomContextToken&&t.revokeManagedRoomContextToken?.(o.managedRoomContextToken);const I=await this.isSessionBusy(a.sessionId),f=m(),y=f.sessions[a.sessionId]??[],h=J({...a,text:c.text,attachments:c.attachments,systemPrompt:o?null:a.systemPrompt});o&&(i.managedAgentDirect&&(h.managedAgentDirect=!0),h.trigger?.kind!=="manager_event"&&h.trigger?.kind!=="scheduled"&&(h.trigger={kind:"managed_direct"}));const v=h.clientMessageId??h.id,S=R(a.sessionId,v);if(S.state!=="not_found")return t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:S.state==="queued",queueMessageId:h.id,...S,queue:this.getSnapshot(a.sessionId)}}),!0;const g=P(y,h);g||(f.sessions[a.sessionId]=[...y,h],M(f));const p=g??h;return z({sessionId:p.sessionId,agentType:p.agentType,sessionMode:p.sessionMode,managerConfig:p.managerConfig,workDir:p.workDir,agentSessionId:p.agentSessionId,modelId:p.modelId}),j(p.sessionId,w(p),"queued"),W(t.resolvePath(p.workDir),p.attachments??[]),k(t.client),this.logLifecycle("enqueue.persisted",a.sessionId,{origin:a.origin??"app",queueMessageId:p.id,pendingCount:(m().sessions[a.sessionId]??[]).length}),this.broadcast(a.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:g?.id??h.id,...R(a.sessionId,g?.clientMessageId??g?.id??h.clientMessageId??h.id),queue:this.getSnapshot(a.sessionId),...c.localized?{localizedAttachments:!0}:{}}}),I?this.retries.schedule(a.sessionId):this.wakeSession(a.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]??[],o=n.findIndex(r=>r.id===t.queueMessageId);if(o<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!C(n[o])||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 u=O(t.attachments),a=s.resolvePath(n[o].workDir);u?.length&&L(a,u),n[o]={...n[o],text:t.text,attachments:u,updatedAt:K()},i.sessions[t.sessionId]=n,M(i),x(t.sessionId,w(n[o]),"queued"),n[o].attachments?.length&&W(a,n[o].attachments??[]),k(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]??[],o=n.find(a=>a.id===t.queueMessageId);if(o&&(!C(o)||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 u=n.filter(a=>a.id!==t.queueMessageId);if(u.length===n.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}u.length?i.sessions[t.sessionId]=u:delete i.sessions[t.sessionId],M(i),$(t.sessionId,o?.clientMessageId??o?.id??t.queueMessageId),k(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=o=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:o});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(a=>setTimeout(a,5));if(n=V(m().sessions[t.sessionId]??[],t.queueMessageId),!n.length){i("Queued message not found");return}this.inFlightMessageIds.set(t.sessionId,new Set(n.map(a=>a.id)));const o=s.sessions.get(t.sessionId);let u="idle";if(o?.currentRunId)if(this.canInjectActiveBatch(o,n)){const a=n.length===1?n[0].text:B(n),r=n[0].clientMessageId??n[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:n[0].agentType,sourceAgentType:te(n[0].agentType,n[0].modelId),canonicalMessageId:r,sourceSessionKey:o.agentSessionId,text:a,managedEchoPolicy:"suppress_following"});const d=Date.now();try{await o.adapter.injectActiveInput({text:a,attachments:Q(n.flatMap(c=>c.attachments??[])),clientMessageId:r})}catch(c){throw s.nativeFusion?.cancelManagedSend(r),c}for(const c of n)x(t.sessionId,{...w(c),ts:d},"executing");this.removeBatch(t.sessionId,n),u="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId),await this.dispatchQueuedMessages(n),this.removeBatch(t.sessionId,n),u="restarted"}else await this.dispatchQueuedMessages(n),this.removeBatch(t.sessionId,n);k(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(a=>a.id),mode:u}})}catch(o){i(o instanceof Error?o.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){this.wakeSession(e)}purgeSession(e){ee(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const u=m().sessions[e]?.length??0;return this.purgeSession(e),u}const s=m(),t=s.sessions[e]??[],i=this.inFlightMessageIds.get(e)??new Set,n=t.filter(u=>u.managedRoom&&!i.has(u.id));if(n.length===0)return 0;const o=t.filter(u=>!n.includes(u));o.length?s.sessions[e]=o:delete s.sessions[e],M(s);for(const u of n)$(e,u.clientMessageId??u.id);return k(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:n.length,pendingCount:o.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 d=m().sessions[e]?.length??0,c=this.deferredLogState.get(e),l=Date.now();(!c||c.pendingCount!==d||l-c.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:d}),this.deferredLogState.set(e,{at:l,pendingCount:d}));return}this.deferredLogState.delete(e);const n=m(),o=n.sessions[e]??[],u=o.filter(d=>{const c=d.clientMessageId??d.id,l=R(e,c).state;return l==="queued"||l==="not_found"});u.length!==o.length&&(u.length?n.sessions[e]=u:delete n.sessions[e],M(n),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:o.length-u.length,pendingCount:u.length}));const a=U(u);if(a.length===0){this.broadcast(e);return}i=a,this.inFlightMessageIds.set(e,new Set(a.map(d=>d.id))),this.logLifecycle("dispatch.start",e,{batchCount:a.length,queueMessageId:a[0].id,origin:a[0].origin??"app"});for(const d of a)x(e,w(d),"queued");await this.dispatchQueuedMessages(a);const r=this.removeBatch(e,a);this.logLifecycle("dispatch.accepted",e,{batchCount:a.length,queueMessageId:a[0].id,pendingCount:r}),this.deferredLogState.delete(e),s=r>0,this.broadcast(e)}catch(n){const o=F(n instanceof Error?n.message:String(n),n instanceof _?"terminal_dispatch_failed":"transient_dispatch_failed"),u=i[0],a=!!(u&&!u.trigger&&!u.clientMessageId&&Date.now()-Date.parse(u.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(n instanceof _||o==="managed_claim_source_binding_conflict"||a){const d=this.removeBatch(e,i);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:i.length,queueMessageId:i[0]?.id,pendingCount:d,failureClass:a?"stale_legacy_managed_worker_dispatch_failure":o},"error"),s=d>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:o},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e),m().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),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(o=>o.id)),n=(t.sessions[e]??[]).filter(o=>!i.has(o.id));return n.length?t.sessions[e]=n:delete t.sessions[e],M(t),n.length}logLifecycle(e,s,t,i="info"){const n=`[chat.queue] ${JSON.stringify({at:K(),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 _("schedule_target_unavailable");const i=e.flatMap(g=>{if(g.trigger?.kind==="room_activation"){const{kind:p,...D}=g.trigger;return[D]}return!g.trigger&&g.managedRoom?[g.managedRoom]:[]}),n=e.find(g=>g.trigger?.kind==="manager_event")?.trigger,o=e.some(g=>g.trigger?.kind==="managed_direct"||!!g.managedAgentDirect),u=o||!!t?.bindingId,a=e.find(g=>g.trigger?.kind==="manager_worker")?.trigger,r=n?.kind==="manager_event"||o,d=i.length>0?await this.prepareManagedRoomTurn?.(i):u||n?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(s.sessionId):void 0;if(i.length>0&&!d)throw new Error("managed_room_turn_preparer_unavailable");if(u&&!d)throw new Error("managed_agent_context_unavailable");if(n?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(s.sessionId)&&!d)throw new Error("managed_agent_context_unavailable");const c=new Set((m().sessions[s.sessionId]??[]).map(g=>g.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(g=>!c.has(g.id)))throw d?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(d.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new _("queued_dispatch_cancelled");r&&d?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(d.managedRoomContextToken);const l=d??null,I=l?.sessionMode==="manager"?l.managerConfig:null,f=e.length===1?s.text:B(e),y=Q(e.flatMap(g=>g.attachments??[])),h=u||n?.kind==="manager_event"&&!!d,v=h?d.agentSessionId?.startsWith("managed:")?q(this.opts.getRuntime(),s):d.agentSessionId:i.length>0?q(this.opts.getRuntime(),s):s.agentSessionId??null,S=d?N().find(g=>g.sessionId===s.sessionId):null;await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...A(E()),sessionId:s.sessionId,text:f,agentType:h?d.agentType:s.agentType,sessionMode:l?.sessionMode??s.sessionMode,managerConfig:l?I:s.managerConfig,workDir:h?d.workDir:s.workDir,agentSessionId:v,modelId:b(S?.modelId)??b(s.modelId),systemPrompt:r?null:d?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:l?l.managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:l?l.managerDefaultWorkerModelId:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:y,externalChannel:r?void 0:s.externalChannel,replyTarget:r?void 0:s.replyTarget,roomContextRegistryKey:r?void 0:d?.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":u?"managed_direct":i.length>0?"managed_room":void 0,managerEventId:n?.kind==="manager_event"?n.eventId:void 0,managedWorkerParentSessionId:a?.kind==="manager_worker"?a.parentSessionId:void 0,managerWorkerNativeFull:a?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isNativeSkillManager(a.parentSessionId,s.workDir)||void 0,managedRoomContextToken:r?void 0:d?.managedRoomContextToken,scheduledDelivery:!!t,suppressExternalOwnerEcho:i.length>0||!!t||void 0,queuedBatchItems:e.map(g=>{const p=T(g);return r?{...p,systemPrompt:null,externalChannel:null,replyTarget:null}:p}),waitForDispatch:!0}},d??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 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{ye as ChatQueueManager,Re as TerminalQueuedChatDispatchError,Se as purgeQueuedSessionData};
@@ -1 +1 @@
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};
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==="managed_room"||i.params.scheduledDelivery===!0?d(a):h(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.65",
3
+ "version": "0.4.66",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {