shennian 0.4.69 → 0.4.71
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 +125 -70
- package/dist/src/agent-env.js +4 -4
- package/dist/src/agents/adapter.d.ts +7 -3
- package/dist/src/agents/auth-status.d.ts +4 -0
- package/dist/src/agents/auth-status.js +2 -0
- package/dist/src/agents/codex-protocol.d.ts +103 -0
- package/dist/src/agents/codex-protocol.js +3 -0
- package/dist/src/agents/codex-runtime.d.ts +91 -0
- package/dist/src/agents/codex-runtime.js +14 -0
- package/dist/src/agents/codex.d.ts +15 -91
- package/dist/src/agents/codex.js +2 -17
- package/dist/src/agents/command-spec.js +4 -3
- package/dist/src/agents/detect.js +1 -1
- package/dist/src/agents/manager.d.ts +4 -3
- package/dist/src/agents/manager.js +4 -4
- package/dist/src/agents/opencode.d.ts +4 -0
- package/dist/src/agents/opencode.js +1 -1
- package/dist/src/commands/agent-readiness.d.ts +17 -0
- package/dist/src/commands/agent-readiness.js +2 -0
- package/dist/src/commands/agent.js +2 -2
- package/dist/src/commands/daemon-instance-lock.js +1 -1
- package/dist/src/commands/daemon-process.d.ts +1 -0
- package/dist/src/commands/daemon-process.js +1 -1
- package/dist/src/commands/daemon-service-spec.d.ts +28 -0
- package/dist/src/commands/daemon-service-spec.js +1 -0
- package/dist/src/commands/daemon-start-wait.d.ts +1 -1
- package/dist/src/commands/daemon-start-wait.js +1 -1
- package/dist/src/commands/daemon-state.d.ts +57 -0
- package/dist/src/commands/daemon-state.js +1 -0
- package/dist/src/commands/daemon-windows.d.ts +8 -0
- package/dist/src/commands/daemon-windows.js +10 -10
- package/dist/src/commands/daemon.d.ts +4 -64
- package/dist/src/commands/daemon.js +13 -13
- package/dist/src/commands/upgrade.js +1 -1
- package/dist/src/daemon-ipc/agent-readiness.d.ts +14 -0
- package/dist/src/daemon-ipc/agent-readiness.js +1 -0
- package/dist/src/daemon-ipc/control.d.ts +1 -1
- package/dist/src/daemon-ipc/control.js +1 -1
- package/dist/src/index.js +3 -3
- package/dist/src/native-fusion/local-repository.d.ts +6 -1
- package/dist/src/native-fusion/local-repository.js +2 -2
- package/dist/src/native-fusion/service.d.ts +2 -0
- package/dist/src/native-fusion/service.js +2 -2
- package/dist/src/native-fusion/source-visibility-audit.d.ts +16 -0
- package/dist/src/native-fusion/source-visibility-audit.js +1 -0
- package/dist/src/native-fusion/source-visibility.d.ts +25 -0
- package/dist/src/native-fusion/source-visibility.js +1 -0
- package/dist/src/native-fusion/state.d.ts +1 -0
- package/dist/src/native-fusion/state.js +2 -2
- package/dist/src/native-fusion/types.d.ts +5 -0
- package/dist/src/room/managed-room-binding.d.ts +2 -0
- package/dist/src/room/managed-room-binding.js +1 -1
- package/dist/src/room/room-binding-client.d.ts +1 -0
- package/dist/src/room/room-binding-client.js +1 -1
- package/dist/src/runtime-distribution.d.ts +13 -0
- package/dist/src/runtime-distribution.js +1 -0
- package/dist/src/security/managed-agent-policy.d.ts +7 -0
- package/dist/src/security/managed-agent-policy.js +1 -1
- package/dist/src/session/handlers/agent-workspace-validation.d.ts +1 -0
- package/dist/src/session/handlers/agent-workspace-validation.js +1 -1
- package/dist/src/session/handlers/agents.d.ts +7 -1
- package/dist/src/session/handlers/agents.js +1 -1
- package/dist/src/session/handlers/chat-context.d.ts +31 -0
- package/dist/src/session/handlers/chat-context.js +1 -0
- package/dist/src/session/handlers/chat-events.d.ts +1 -1
- package/dist/src/session/handlers/chat-events.js +2 -2
- package/dist/src/session/handlers/chat-session-lifecycle.d.ts +2 -2
- package/dist/src/session/handlers/chat-session-lifecycle.js +1 -1
- package/dist/src/session/handlers/chat.d.ts +3 -29
- package/dist/src/session/handlers/chat.js +1 -1
- package/dist/src/session/manager.js +2 -2
- package/dist/src/session/queue-helpers.js +2 -2
- package/dist/src/session/queue-message.d.ts +2 -0
- package/dist/src/session/queue.d.ts +7 -1
- package/dist/src/session/queue.js +1 -1
- package/dist/src/session/queued-chat-dispatch.d.ts +4 -1
- package/dist/src/session/queued-chat-dispatch.js +1 -1
- package/dist/src/session/store.d.ts +4 -2
- package/dist/src/session/store.js +2 -2
- package/dist/src/session/types.d.ts +1 -0
- package/dist/src/upgrade/engine.js +2 -2
- package/dist/src/upgrade/scheduler.js +1 -1
- package/dist/src/upgrade/updater-launcher.js +3 -3
- package/node_modules/@shennian/wire/dist/room.d.ts +2 -0
- package/node_modules/@shennian/wire/dist/session-sync.d.ts +2 -1
- package/node_modules/@shennian/wire/dist/session.d.ts +19 -1
- package/node_modules/@shennian/wire/dist/session.js +8 -0
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{SESSION_SYNC_PROTOCOL_VERSION as g,SESSION_SYNC_DATA_EPOCH as f}from"@shennian/wire";import h from"node:crypto";import{getRegisteredAgents as k,unregisterAgent as I}from"../agents/adapter.js";import{resetCommandSpecCache as A}from"../agents/command-spec.js";import{loadConfig as T}from"../config/index.js";import{handleUpgradeStart as P,handleUpgradeStatus as C}from"../commands/upgrade.js";import{handleAgentsRefresh as M,handleModelsRefresh as D,refreshMachineAgents as x}from"./handlers/agents.js";import{AcpDiscoveryWatcher as E}from"../agents/acp/watcher.js";import{handleAgentCapabilitiesList as F}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as O,handleAgentConfigGet as _,handleAgentConfigTest as Q,handleAgentConfigUpsert as j}from"./handlers/agent-config.js";import{handleChatAbort as B,handleChatSend as U}from"./handlers/chat.js";import{recoverJournaledRun as z}from"./handlers/chat-recovery.js";import{handleSessionRefresh as W}from"./handlers/session-refresh.js";import{handleSessionToolDetail as $}from"./handlers/tool-detail.js";import{handleSessionTitleSet as H}from"./handlers/title.js";import{handleSessionMessageStatus as J}from"./handlers/message-status.js";import{handleAgentWorkspaceValidation as N}from"./handlers/agent-workspace-validation.js";import{handleVoiceSessionBodyWindow as V}from"./handlers/voice-window.js";import{handleRemoteAccessPause as L}from"./handlers/remote-access.js";import{cleanupPendingTransfers as G,handleFsLs as Y,handleFsRead as K,handleFsRename as X,handleFsWrite as Z,handleFsTransferAbort as q,handleFsTransferChunk as ee,handleFsTransferFinish as se,handleFsTransferStart as te,handleFsExportMarkdownPdf as ae,handleFsExportMarkdownPdfSetup as re,handleFsArchiveZip as ne}from"./handlers/fs.js";import{handleRegionProbe as ie,handleRegionSwitch as oe,handleUpgradeSetPolicy as ce}from"./handlers/control.js";import{ManagerRuntimeService as le,setManagerRuntimeService as v}from"../manager/runtime.js";import{ChatQueueManager as de}from"./queue.js";import{createAuthorizedFsRoot as he,createAuthorizedFsRootAsync as ue,resolveAuthorizedPath as pe,resolveAuthorizedPathAsync as me,resolveSessionWorkDir as ge}from"../fs/boundary.js";import"../agents/claude.js";import"../agents/codex.js";import"../agents/workbuddy.js";import"../agents/gemini.js";import"../agents/cursor.js";import"../agents/opencode.js";import"../agents/pi-coding-agent.js";import"../agents/manager.js";import{registerCustomAgent as fe}from"../agents/custom.js";import{handleManagedRoomBind as ve,handleManagedRoomRestart as ye,discardPreBindingSession as Re}from"./handlers/room-managed.js";import{getDaemonInstallationId as u,listSessionRecords as p,purgeCanonicalSession as be,purgeSessionRecord as Se,recordSession as we,upsertMessage as ke}from"./store.js";import{requestSessionIndexPublish as Ie}from"./index-publisher.js";import{reportUpgradeAggregateMetric as Ae}from"../log-reporter.js";import{PersonalHistorySubscriptions as Te}from"./history-subscriptions.js";import{sweepAttachmentTransfers as Pe}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as Ce}from"../native-fusion/native-source-deletions.js";import{purgeToolDetails as Me}from"./tool-detail-store.js";import{readDeletionSyncRevision as De,writeDeletionSyncRevision as y}from"./deletion-sync-state.js";import{assertDeletionSyncRunnable as xe,clearDeletionSyncFailure as Ee,DeletionSyncRetryWaitError as Fe,DeletionSyncTerminalError as Oe,recordDeletionSyncFailure as m}from"./deletion-sync-state.js";import{NativeSessionCleanupOutbox as _e}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as c}from"../personal-sync/epoch.js";import{ActiveRunJournal as Qe}from"./run-recovery/journal.js";import{RunRecoveryBarrier as je}from"./run-recovery/barrier.js";import{RunRecoveryCoordinator as Be}from"./run-recovery/coordinator.js";import{ProjectFsHandleStore as Ue}from"./project-fs-store.js";import{handleProjectFsRequest as ze}from"./handlers/project-fs.js";import{handleAgentPermissionResponse as We,handleAcpAgentRequest as $e}from"./handlers/agent-control.js";import{validateDeletionSyncResponse as He}from"./deletion-sync-validation.js";import{dispatchManagedChat as Je,dispatchQueuedChat as Ne,interruptQueuedChat as Ve}from"./queued-chat-dispatch.js";const Le=50;import{resolveSessionWorkDir as Xs}from"../fs/boundary.js";class Gs{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;remoteAccessPauseController;managedRoomContextRevoker;managedRoomExecutor;trustedProjectFsRequestResolver;scheduleHandler;getScheduleQueue(){return this.chatQueue}sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;historySubscriptions;nativeCleanupOutbox;activityProbeTimer=null;attachmentCleanupTimer=null;activeRequests=0;acpDiscoveryWatcher;runJournal;recoveryBarrier=new je;recoveryCoordinator;recoveryPass=null;prepareManagedRecovery;projectFsHandleStore=new Ue;constructor(e,t=null,s,n=null,r=null,d=null,o=null,R=null,b=null,S=null){this.client=e,this.nativeFusion=t,this.cliVersion=s,this.upgradePolicyController=n,this.managedRoomBinder=r,this.remoteAccessPauseController=d,this.managedRoomContextRevoker=o,this.managedRoomExecutor=R,this.trustedProjectFsRequestResolver=S,this.runJournal=b??new Qe(u());for(const a of this.runJournal.loadPendingReconciliation().records)this.recoveryBarrier.begin(a.sessionId);this.recoveryCoordinator=new Be({journal:this.runJournal,barrier:this.recoveryBarrier,recover:(a,i)=>z(this.getRuntime(),a,i),commitTerminal:(a,i)=>this.commitRecoveryTerminal(a,i),isCanonicalTerminal:a=>{const i=p().find(w=>w.sessionId===a.sessionId),l=`terminal:${h.createHash("sha256").update(`${a.runId}\0`).digest("hex")}`;return i?.lastAttentionId===l},onOutcome:a=>Ae({kind:"recovery",adapterType:a.adapterType,result:a.result,stage:a.result==="continued"?"continue":a.result==="reattached"?"reattach":a.result==="resumed"?"resume":a.result==="terminal_reconciled"?"inspect":"terminal",durationMs:a.durationMs,retries:Math.max(0,a.attempts-1),...a.reason?{errorCode:a.reason}:{}})}),this.managerRuntime=new le({getRuntime:()=>this.getRuntime(),dispatchReq:a=>this.handleReq(a,{propagateErrors:!0})}),this.chatQueue=new de({getRuntime:()=>this.getRuntime(),dispatchReq:a=>this.handleReq(a),dispatchManagedReq:(a,i)=>Je(this.getRuntime(),a,i),dispatchQueuedReq:(a,i)=>Ne(this.getRuntime(),a,i),interruptQueuedSession:a=>Ve(this.getRuntime(),a)}),this.historySubscriptions=new Te(this.client),this.nativeCleanupOutbox=new _e({logger:console.info}),this.acpDiscoveryWatcher=new E(async()=>{A(),await x(this.getRuntime())}),this.nativeFusion?.setExternalTerminalHandler?.(a=>{this.handleExternalTerminal(a)}),this.nativeCleanupOutbox.start(),v(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents();try{this.acpDiscoveryWatcher.start()}catch(a){console.warn(`[acp.discovery] watcher unavailable: ${a instanceof Error?a.message:String(a)}`)}this.activityProbeTimer=setInterval(()=>{this.publishManagedActivitySnapshots().catch(a=>{console.error("[session.activity] managed probe failed",a)})},15e3),this.activityProbeTimer.unref?.(),this.attachmentCleanupTimer=setInterval(()=>{const a=new Set([...p().map(i=>i.workDir),...Array.from(this.pendingTransfers.values()).map(i=>i.rootPath)]);for(const i of a)try{Pe(this.resolvePath(i))}catch(l){console.error("[attachment.cleanup] failed",l instanceof Error?l.message:String(l))}},6e4),this.attachmentCleanupTimer.unref?.()}executeManagedManagerAction(e){return this.managerRuntime.executeScopedWorkerAction(e.managerSessionId,e.action,e.params,e.roomOrigin)}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,t)=>this.resolveAuthorizedPath(e,t),resolveAuthorizedPathAsync:(e,t)=>this.resolveAuthorizedPathAsync(e,t),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,t)=>this.publishSessionActivity(e,t)},upgradePolicyController:this.upgradePolicyController,revokeManagedRoomContextToken:this.managedRoomContextRevoker,managedRoomExecutor:this.managedRoomExecutor,runJournal:this.runJournal,recoveryBarrier:this.recoveryBarrier,prepareManagedRecovery:this.prepareManagedRecovery}}async resumePendingAcpAuthentication(e){const t=[...this.sessions.values()].find(s=>s.agentType===e&&s.adapter.hasPendingAuthenticationRetry?.());return t?.adapter.resumeAfterExternalAuthentication?(await t.adapter.resumeAfterExternalAuthentication(),!0):!1}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(t=>t.currentRunId!==null)&&e.push("active-agent-turn"),this.pendingTransfers.size>0&&e.push("file-transfer"),this.activeRequests>0&&e.push("active-daemon-request"),{idle:e.length===0,reasons:e}}prepareForUpgrade(e){this.recoveryBarrier.freezeDispatch();const t=this.runJournal.markPlannedUpgrade(e);return this.runJournal.sync(),{activeRunCount:t}}cancelUpgradePreparation(){this.recoveryBarrier.unfreezeDispatch()}async recoverInterruptedRuns(){this.recoveryPass??=this.recoveryCoordinator.recoverAll(),await this.recoveryPass}getPendingRecoveryCount(){return this.runJournal.loadPendingReconciliation().records.length}revokeProjectFsHandles(){this.projectFsHandleStore.revokeAll()}async commitRecoveryTerminal(e,t){const s=this.sessions.get(e.sessionId);s&&(s.currentRunId=null,s.currentRunPhase=null,s.journalRunId=null,s.heartbeatTimer&&(clearInterval(s.heartbeatTimer),s.heartbeatTimer=null)),this.runTextAcc.delete(`${e.sessionId}:${e.providerRunId??e.runId}`),this.publishSessionActivity(e.sessionId,null);const n=t.result==="interrupted_by_upgrade"?`\u4EFB\u52A1\u56E0\u795E\u5FF5\u5347\u7EA7\u800C\u4E2D\u65AD\uFF08${t.reason}\uFF09\u3002\u539F\u4F1A\u8BDD\u5DF2\u4FDD\u7559\uFF0C\u53EF\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002
|
|
1
|
+
import{SESSION_SYNC_PROTOCOL_VERSION as g,SESSION_SYNC_DATA_EPOCH as f}from"@shennian/wire";import h from"node:crypto";import{getRegisteredAgents as k,unregisterAgent as I}from"../agents/adapter.js";import{resetCommandSpecCache as A}from"../agents/command-spec.js";import{loadConfig as T}from"../config/index.js";import{handleUpgradeStart as P,handleUpgradeStatus as C}from"../commands/upgrade.js";import{handleAgentsRefresh as M,handleModelsRefresh as D,refreshMachineAgents as x}from"./handlers/agents.js";import{AcpDiscoveryWatcher as E}from"../agents/acp/watcher.js";import{handleAgentCapabilitiesList as F}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as O,handleAgentConfigGet as _,handleAgentConfigTest as Q,handleAgentConfigUpsert as j}from"./handlers/agent-config.js";import{handleChatAbort as B,handleChatSend as U}from"./handlers/chat.js";import{recoverJournaledRun as z}from"./handlers/chat-recovery.js";import{handleSessionRefresh as W}from"./handlers/session-refresh.js";import{handleSessionToolDetail as $}from"./handlers/tool-detail.js";import{handleSessionTitleSet as H}from"./handlers/title.js";import{handleSessionMessageStatus as J}from"./handlers/message-status.js";import{handleAgentWorkspaceValidation as N}from"./handlers/agent-workspace-validation.js";import{handleVoiceSessionBodyWindow as V}from"./handlers/voice-window.js";import{handleRemoteAccessPause as L}from"./handlers/remote-access.js";import{cleanupPendingTransfers as G,handleFsLs as Y,handleFsRead as K,handleFsRename as X,handleFsWrite as Z,handleFsTransferAbort as q,handleFsTransferChunk as ee,handleFsTransferFinish as se,handleFsTransferStart as te,handleFsExportMarkdownPdf as ae,handleFsExportMarkdownPdfSetup as ne,handleFsArchiveZip as re}from"./handlers/fs.js";import{handleRegionProbe as ie,handleRegionSwitch as oe,handleUpgradeSetPolicy as ce}from"./handlers/control.js";import{ManagerRuntimeService as le,setManagerRuntimeService as v}from"../manager/runtime.js";import{ChatQueueManager as de}from"./queue.js";import{createAuthorizedFsRoot as he,createAuthorizedFsRootAsync as ue,resolveAuthorizedPath as me,resolveAuthorizedPathAsync as pe,resolveSessionWorkDir as ge}from"../fs/boundary.js";import"../agents/claude.js";import"../agents/codex.js";import"../agents/workbuddy.js";import"../agents/gemini.js";import"../agents/cursor.js";import"../agents/opencode.js";import"../agents/pi-coding-agent.js";import"../agents/manager.js";import{registerCustomAgent as fe}from"../agents/custom.js";import{handleManagedRoomBind as ve,handleManagedRoomRestart as ye,discardPreBindingSession as Re}from"./handlers/room-managed.js";import{getDaemonInstallationId as u,listSessionRecords as m,purgeCanonicalSession as be,purgeSessionRecord as Se,recordSession as we,upsertMessage as ke}from"./store.js";import{requestSessionIndexPublish as Ie}from"./index-publisher.js";import{reportUpgradeAggregateMetric as Ae}from"../log-reporter.js";import{PersonalHistorySubscriptions as Te}from"./history-subscriptions.js";import{sweepAttachmentTransfers as Pe}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as Ce}from"../native-fusion/native-source-deletions.js";import{purgeToolDetails as Me}from"./tool-detail-store.js";import{readDeletionSyncRevision as De,writeDeletionSyncRevision as y}from"./deletion-sync-state.js";import{assertDeletionSyncRunnable as xe,clearDeletionSyncFailure as Ee,DeletionSyncRetryWaitError as Fe,DeletionSyncTerminalError as Oe,recordDeletionSyncFailure as p}from"./deletion-sync-state.js";import{NativeSessionCleanupOutbox as _e}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as c}from"../personal-sync/epoch.js";import{ActiveRunJournal as Qe}from"./run-recovery/journal.js";import{RunRecoveryBarrier as je}from"./run-recovery/barrier.js";import{RunRecoveryCoordinator as Be}from"./run-recovery/coordinator.js";import{ProjectFsHandleStore as Ue}from"./project-fs-store.js";import{handleProjectFsRequest as ze}from"./handlers/project-fs.js";import{handleAgentPermissionResponse as We,handleAcpAgentRequest as $e}from"./handlers/agent-control.js";import{validateDeletionSyncResponse as He}from"./deletion-sync-validation.js";import{dispatchManagedChat as Je,dispatchQueuedChat as Ne,interruptQueuedChat as Ve}from"./queued-chat-dispatch.js";const Le=50;import{resolveSessionWorkDir as Xs}from"../fs/boundary.js";class Gs{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;remoteAccessPauseController;managedRoomContextRevoker;managedRoomExecutor;trustedProjectFsRequestResolver;scheduleHandler;getScheduleQueue(){return this.chatQueue}sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;historySubscriptions;nativeCleanupOutbox;activityProbeTimer=null;attachmentCleanupTimer=null;activeRequests=0;acpDiscoveryWatcher;runJournal;recoveryBarrier=new je;recoveryCoordinator;recoveryPass=null;prepareManagedRecovery;projectFsHandleStore=new Ue;constructor(e,t=null,s,r=null,n=null,d=null,o=null,R=null,b=null,S=null){this.client=e,this.nativeFusion=t,this.cliVersion=s,this.upgradePolicyController=r,this.managedRoomBinder=n,this.remoteAccessPauseController=d,this.managedRoomContextRevoker=o,this.managedRoomExecutor=R,this.trustedProjectFsRequestResolver=S,this.runJournal=b??new Qe(u());for(const a of this.runJournal.loadPendingReconciliation().records)this.recoveryBarrier.begin(a.sessionId);this.recoveryCoordinator=new Be({journal:this.runJournal,barrier:this.recoveryBarrier,recover:(a,i)=>z(this.getRuntime(),a,i),commitTerminal:(a,i)=>this.commitRecoveryTerminal(a,i),isCanonicalTerminal:a=>{const i=m().find(w=>w.sessionId===a.sessionId),l=`terminal:${h.createHash("sha256").update(`${a.runId}\0`).digest("hex")}`;return i?.lastAttentionId===l},onOutcome:a=>Ae({kind:"recovery",adapterType:a.adapterType,result:a.result,stage:a.result==="continued"?"continue":a.result==="reattached"?"reattach":a.result==="resumed"?"resume":a.result==="terminal_reconciled"?"inspect":"terminal",durationMs:a.durationMs,retries:Math.max(0,a.attempts-1),...a.reason?{errorCode:a.reason}:{}})}),this.managerRuntime=new le({getRuntime:()=>this.getRuntime(),dispatchReq:a=>this.handleReq(a,{propagateErrors:!0})}),this.chatQueue=new de({getRuntime:()=>this.getRuntime(),dispatchReq:a=>this.handleReq(a),dispatchManagedReq:(a,i)=>Je(this.getRuntime(),a,i),dispatchQueuedReq:(a,i)=>Ne(this.getRuntime(),a,i),interruptQueuedSession:a=>Ve(this.getRuntime(),a)}),this.historySubscriptions=new Te(this.client),this.nativeCleanupOutbox=new _e({logger:console.info}),this.acpDiscoveryWatcher=new E(async()=>{A(),await x(this.getRuntime())}),this.nativeFusion?.setExternalTerminalHandler?.(a=>{this.handleExternalTerminal(a)}),this.nativeFusion?.setExternalClaimHandler?.((a,i)=>{this.chatQueue.noteExternalClaim(a,i)}),this.nativeCleanupOutbox.start(),v(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents();try{this.acpDiscoveryWatcher.start()}catch(a){console.warn(`[acp.discovery] watcher unavailable: ${a instanceof Error?a.message:String(a)}`)}this.activityProbeTimer=setInterval(()=>{this.publishManagedActivitySnapshots().catch(a=>{console.error("[session.activity] managed probe failed",a)})},15e3),this.activityProbeTimer.unref?.(),this.attachmentCleanupTimer=setInterval(()=>{const a=new Set([...m().map(i=>i.workDir),...Array.from(this.pendingTransfers.values()).map(i=>i.rootPath)]);for(const i of a)try{Pe(this.resolvePath(i))}catch(l){console.error("[attachment.cleanup] failed",l instanceof Error?l.message:String(l))}},6e4),this.attachmentCleanupTimer.unref?.()}executeManagedManagerAction(e){return this.managerRuntime.executeScopedWorkerAction(e.managerSessionId,e.action,e.params,e.roomOrigin)}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,t)=>this.resolveAuthorizedPath(e,t),resolveAuthorizedPathAsync:(e,t)=>this.resolveAuthorizedPathAsync(e,t),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,t)=>this.publishSessionActivity(e,t)},upgradePolicyController:this.upgradePolicyController,revokeManagedRoomContextToken:this.managedRoomContextRevoker,managedRoomExecutor:this.managedRoomExecutor,runJournal:this.runJournal,recoveryBarrier:this.recoveryBarrier,prepareManagedRecovery:this.prepareManagedRecovery}}async resumePendingAcpAuthentication(e){const t=[...this.sessions.values()].find(s=>s.agentType===e&&s.adapter.hasPendingAuthenticationRetry?.());return t?.adapter.resumeAfterExternalAuthentication?(await t.adapter.resumeAfterExternalAuthentication(),!0):!1}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(t=>t.currentRunId!==null)&&e.push("active-agent-turn"),this.pendingTransfers.size>0&&e.push("file-transfer"),this.activeRequests>0&&e.push("active-daemon-request"),{idle:e.length===0,reasons:e}}prepareForUpgrade(e){this.recoveryBarrier.freezeDispatch();const t=this.runJournal.markPlannedUpgrade(e);return this.runJournal.sync(),{activeRunCount:t}}cancelUpgradePreparation(){this.recoveryBarrier.unfreezeDispatch()}async recoverInterruptedRuns(){this.recoveryPass??=this.recoveryCoordinator.recoverAll(),await this.recoveryPass}getPendingRecoveryCount(){return this.runJournal.loadPendingReconciliation().records.length}revokeProjectFsHandles(){this.projectFsHandleStore.revokeAll()}async commitRecoveryTerminal(e,t){const s=this.sessions.get(e.sessionId);s&&(s.currentRunId=null,s.currentRunPhase=null,s.journalRunId=null,s.heartbeatTimer&&(clearInterval(s.heartbeatTimer),s.heartbeatTimer=null)),this.runTextAcc.delete(`${e.sessionId}:${e.providerRunId??e.runId}`),this.publishSessionActivity(e.sessionId,null);const r=t.result==="interrupted_by_upgrade"?`\u4EFB\u52A1\u56E0\u795E\u5FF5\u5347\u7EA7\u800C\u4E2D\u65AD\uFF08${t.reason}\uFF09\u3002\u539F\u4F1A\u8BDD\u5DF2\u4FDD\u7559\uFF0C\u53EF\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002
|
|
2
2
|
Task interrupted by the Shennian upgrade (${t.reason}). The session is preserved and ready for a new message.`:`\u795E\u5FF5\u5347\u7EA7\u540E\u672A\u80FD\u6062\u590D\u4EFB\u52A1\uFF08${t.reason}\uFF09\u3002\u539F\u4F1A\u8BDD\u5DF2\u4FDD\u7559\uFF0C\u53EF\u91CD\u8BD5\u6216\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002
|
|
3
|
-
The task could not be recovered after the Shennian upgrade (${t.reason}). The session is preserved; retry or send a new message.`;ke(e.sessionId,{id:`upgrade-recovery-terminal-${e.runId}`,sessionId:e.sessionId,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:"error",content:n,error:!0,recoveryState:t.result,recoveryReason:t.reason})}),we({sessionId:e.sessionId,agentType:e.agentType,sessionMode:e.sessionMode,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,status:"failed",attentionId:`upgrade-recovery:${e.runId}`,lastActivityAt:new Date().toISOString()}),this.managerRuntime.noteAgentEvent(e.sessionId,{state:"error",runId:e.providerRunId??e.runId,seq:e.lastObservedEventSeq+1,message:n}),Ie(this.client);const r=e.lastObservedEventSeq+1;this.client.sendAgentEvent({type:"event",event:"agent",payload:t.result==="interrupted_by_upgrade"?{state:"interrupted_by_upgrade",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:r,reason:t.reason}:{state:"recovery_failed",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:r,reason:t.reason,...t.message?{message:t.message}:{}},seq:r,id:`agent-recovery-terminal-${e.runId}`}),this.chatQueue.noteTerminal(e.sessionId)}setManagedRoomTurnPreparer(e){this.chatQueue.setManagedRoomTurnPreparer(e)}setManagedAgentDirectTurnPreparer(e){this.chatQueue.setManagedAgentDirectTurnPreparer(e)}setManagedRecoveryPreparer(e){this.prepareManagedRecovery=e}async enqueueManagedRoomBatch(e){const t=e.map(s=>{const n=p().find(r=>r.sessionId===s.params.sessionId);return{context:s.context,params:{...s.params,modelId:n?.modelId??s.params.modelId,sessionMode:n?.sessionMode??s.params.sessionMode,managerConfig:n?.managerConfig??s.params.managerConfig,managerDefaultWorkerAgentType:n?.managerDefaultWorkerAgentType??s.params.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:n?.managerDefaultWorkerModelId??s.params.managerDefaultWorkerModelId}}});await this.chatQueue.enqueueManagedRoomBatch(t)}async retireManagedRoomSession(e){this.chatQueue.purgeSession(e),await Re(this.getRuntime(),e)}discardPendingManagedRoomMessages(e){return this.chatQueue.discardPendingManagedRoomMessages(e)}async synchronizeSessionDeletions(){xe();try{await this.synchronizeSessionDeletionsOnce(),Ee()}catch(e){if(e instanceof Fe||e instanceof Oe)throw e;const t=e instanceof Error?e.message:String(e),s=/offline|not connected|disconnect|timed out|timeout|temporar|unavailable|ECONN|ENOTFOUND|EIO|EBUSY/i.test(t)?"transport_unavailable":/invalid (?:deletion sync|session deletion)|deletion revision gap/i.test(t)?"invalid_deletion_sync_response":"deletion_sync_failed";throw console.warn("[session.deletions] retrying failed synchronization",{code:s}),m({code:s,retryable:!0}),e}}async synchronizeSessionDeletionsOnce(){const e=u(),t=De(),s=await this.client.sendReq({type:"req",id:`session-deletions-sync-${h.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:g,dataEpoch:f,daemonInstallationId:e,afterRevision:t}});if(!s.ok){const o=s.rejection;throw o?(m({code:o.code,retryable:o.retryable&&o.requiredAction==="retry"}),new Error(o.code)):(m({code:"unstructured_rejection",retryable:!0}),new Error(s.error??"unstructured_rejection"))}const n=s.payload;c(n),He(n,t);let r=n.baselineRevision;r>t&&y(r);for(const o of n.deletions)o.deletionRevision<=r||(await this.applySessionDeletion(o),r=o.deletionRevision,y(r));if(r!==n.latestRevision)throw new Error(`deletion revision gap: applied ${r}, latest ${n.latestRevision}`);if(r===0)return;const d=await this.client.sendReq({type:"req",id:`session-deletions-ack-${h.randomUUID()}`,method:"session.deletions.ack",params:{protocolVersion:g,dataEpoch:f,daemonInstallationId:e,deletionRevision:r}});if(!d.ok)throw new Error(d.error??"session deletion acknowledgement failed")}async applySessionDeletion(e){const t=new Ce({daemonInstallationId:u()});t.recordSession(e);for(const n of e.sources){const r=t.find(n.agentType,n.sourceSessionKey);r&&r.sessionId!==e.sessionId||t.record({...e,...n})}this.runJournal.markSessionAborted(e.sessionId),this.recoveryBarrier.end(e.sessionId);const s=this.sessions.get(e.sessionId);if(s){this.revokeManagedRoomToken(s);try{await s.adapter.stop()}catch{}this.sessions.delete(e.sessionId)}this.historySubscriptions.closeSession(e.sessionId),this.nativeFusion?.handleSessionDeleted(e.sessionId),this.chatQueue.purgeSession(e.sessionId),Me(e.sessionId),be(e.sessionId),Se(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null),this.nativeCleanupOutbox.enqueue(e),this.nativeCleanupOutbox.kick()}publishSessionActivity(e,t){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:t}})}async handleExternalTerminal(e){const t=this.sessions.get(e);t&&(t.journalRunId&&(this.recoveryCoordinator.completeFromProvider(e,t.journalRunId),t.journalRunId=null),this.sessions.delete(e),this.revokeManagedRoomToken(t),t.heartbeatTimer&&clearInterval(t.heartbeatTimer),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})),this.chatQueue.noteTerminal(e)}async publishManagedActivitySnapshots(){for(const[e,t]of this.sessions.entries()){if(t.heartbeatTimer||!t.currentRunId||!t.adapter.getStatus)continue;const s=await t.adapter.getStatus().catch(()=>null);if(!s?.active||!s.runPhase)continue;const n=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:s.runId||t.currentRunId,runPhase:s.runPhase,startedAt:new Date(t.lastActiveAt).toISOString(),updatedAt:n,canStop:s.canStop??!0});const r=t.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:s.runId||t.currentRunId,seq:r,runPhase:s.runPhase,canStop:s.canStop??!0},seq:r,id:`agent-status-${s.runId||t.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const t of k())t.startsWith("custom:")&&I(t);const e=T();for(const[t,s]of Object.entries(e.customAgents??{}))fe(t,s)}async handleReq(e,t={}){const s=this.getRuntime();let n;this.activeRequests++;try{switch(e.method){case"schedule.deliver":if(!this.scheduleHandler)throw new Error("schedules_unavailable");await this.scheduleHandler(e);break;case"chat.send":if(e.params.scheduledDelivery)throw new Error("schedule_source_untrusted");c(e.params),this.recoveryBarrier.isDispatchFrozen()||this.recoveryBarrier.isRecovering(String(e.params.sessionId??""))?n={durableQueueHandoff:await this.chatQueue.handleEnqueue({...e,method:"chat.enqueue"})}:await U(s,e);break;case"chat.enqueue":c(e.params),n={durableQueueHandoff:await this.chatQueue.handleEnqueue(e)};break;case"chat.queue.get":c(e.params),await this.chatQueue.handleGet(e);break;case"chat.queue.edit":c(e.params),await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":c(e.params),await this.chatQueue.handleDelete(e);break;case"chat.queue.promote":c(e.params),await this.chatQueue.handlePromote(e);break;case"chat.abort":c(e.params),await B(s,e);break;case"session.refresh":c(e.params),await W(s,e);break;case"session.history.open":this.historySubscriptions.handleOpen(e);break;case"session.history.sync":this.historySubscriptions.handleSync(e);break;case"session.history.page":this.historySubscriptions.handlePage(e);break;case"session.history.close":this.historySubscriptions.handleClose(e);break;case"session.body.replica.subscribe":this.historySubscriptions.handleReplicaSubscribe(e);break;case"session.body.replica.unsubscribe":this.historySubscriptions.handleReplicaUnsubscribe(e);break;case"session.history":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"upgrade_required"});break;case"session.message.status":J(s,e);break;case"session.tool.detail":await $(s,e);break;case"voice.session.body.window":V(s,e);break;case"session.title.set":await H(s,e);break;case"room.agent.bind-managed":await ve(s,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await ye(s,this.managedRoomBinder,e);break;case"fs.ls":await Y(s,e);break;case"project.fs.open":case"project.fs.list":case"project.fs.read":case"project.fs.close":await ze(this.client,this.projectFsHandleStore,e,this.trustedProjectFsRequestResolver);break;case"fs.read":await K(s,e);break;case"fs.write":await Z(s,e);break;case"fs.export.markdown-pdf":await ae(s,e);break;case"fs.export.markdown-pdf.setup":await re(s,e);break;case"fs.archive.zip":await ne(s,e);break;case"fs.rename":await X(s,e);break;case"fs.transfer.start":await te(s,e);break;case"fs.transfer.chunk":await ee(s,e);break;case"fs.transfer.finish":await se(s,e);break;case"fs.transfer.abort":await q(s,e);break;case"region.probe":await ie(s,e);break;case"region.switch":await oe(s,e);break;case"upgrade.start":await P(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0,prepareForUpgrade:r=>this.prepareForUpgrade(r),cancelUpgradePreparation:()=>this.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()}});break;case"upgrade.status":await C(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await ce(s,e);break;case"machine.remote-access.pause":await L(this.client,this.remoteAccessPauseController,e);break;case"agents.refresh":await M(s,e);break;case"agent.permission.respond":await We(s,e);break;case"agent.auth.start":case"agent.auth.logout":case"agent.session-options.get":case"agent.external-sessions.list":case"agent.external-sessions.import":case"agent.external-sessions.close":case"agent.external-sessions.delete":await $e(s,e);break;case"models.refresh":await D(s,e);break;case"agent.config.get":await _(s,e);break;case"agent.config.upsert":await j(s,e);break;case"agent.config.clear":await O(s,e);break;case"agent.config.test":await Q(s,e);break;case"agent.workspace.validate":await N(s,e);break;case"agent.capabilities.list":await F(s,e);break;case"skill.list":case"skill.install":case"skill.doctor":case"skill.setup":case"skill.use":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;case"session.wechat-rpa.channel.get":case"session.wechat-rpa.channel.upsert":case"session.wechat-rpa.channel.sync":case"manager.wechat-rpa.channel.get":case"manager.wechat-rpa.channel.upsert":case"manager.wechat-rpa.channel.sync":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;default:this.client.sendRes({type:"res",id:e.id,ok:!1,error:`Unknown method: ${e.method}`})}}catch(r){if(t.propagateErrors)throw r;this.client.sendRes({type:"res",id:e.id,ok:!1,error:r instanceof Error?r.message:String(r)})}finally{this.activeRequests--}return n}evictIdleSessions(){if(this.sessions.size<Le)return;let e=null;for(const[t,s]of this.sessions)(!e||s.lastActiveAt<e.session.lastActiveAt)&&(e={key:t,session:s});e&&(this.revokeManagedRoomToken(e.session),e.session.heartbeatTimer&&(clearInterval(e.session.heartbeatTimer),e.session.heartbeatTimer=null),e.session.adapter.removeAllListeners(),e.session.adapter.stop().catch(()=>{}),this.sessions.delete(e.key))}resolvePath(e){return ge(e)}resolveAuthorizedPath(e,t){return pe(e,he(t))}async resolveAuthorizedPathAsync(e,t){return me(e,await ue(t))}async cleanup(){this.chatQueue.stop(),this.projectFsHandleStore.revokeAll(),this.acpDiscoveryWatcher.stop(),this.nativeFusion?.setExternalTerminalHandler?.(null),this.nativeCleanupOutbox.stop(),this.historySubscriptions.closeAll(),this.activityProbeTimer&&(clearInterval(this.activityProbeTimer),this.activityProbeTimer=null),this.attachmentCleanupTimer&&(clearInterval(this.attachmentCleanupTimer),this.attachmentCleanupTimer=null);for(const[,e]of this.sessions)this.revokeManagedRoomToken(e),e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),G(this.getRuntime()),await this.managerRuntime.stop(),v(null)}revokeManagedRoomToken(e){const t=e.managedRoomContextToken;e.managedRoomContextToken=null,t&&this.managedRoomContextRevoker?.(t)}}export{Gs as SessionManager,Xs as resolveSessionWorkDir};
|
|
3
|
+
The task could not be recovered after the Shennian upgrade (${t.reason}). The session is preserved; retry or send a new message.`;ke(e.sessionId,{id:`upgrade-recovery-terminal-${e.runId}`,sessionId:e.sessionId,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:"error",content:r,error:!0,recoveryState:t.result,recoveryReason:t.reason})}),we({sessionId:e.sessionId,agentType:e.agentType,sessionMode:e.sessionMode,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,status:"failed",attentionId:`upgrade-recovery:${e.runId}`,lastActivityAt:new Date().toISOString()}),this.managerRuntime.noteAgentEvent(e.sessionId,{state:"error",runId:e.providerRunId??e.runId,seq:e.lastObservedEventSeq+1,message:r}),Ie(this.client);const n=e.lastObservedEventSeq+1;this.client.sendAgentEvent({type:"event",event:"agent",payload:t.result==="interrupted_by_upgrade"?{state:"interrupted_by_upgrade",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:n,reason:t.reason}:{state:"recovery_failed",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:n,reason:t.reason,...t.message?{message:t.message}:{}},seq:n,id:`agent-recovery-terminal-${e.runId}`}),this.chatQueue.noteTerminal(e.sessionId)}setManagedRoomTurnPreparer(e){this.chatQueue.setManagedRoomTurnPreparer(e)}setManagedAgentDirectTurnPreparer(e){this.chatQueue.setManagedAgentDirectTurnPreparer(e)}setManagedRecoveryPreparer(e){this.prepareManagedRecovery=e}async enqueueManagedRoomBatch(e){const t=e.map(s=>{const r=m().find(n=>n.sessionId===s.params.sessionId);return{context:s.context,params:{...s.params,modelId:r?.modelId??s.params.modelId,sessionMode:r?.sessionMode??s.params.sessionMode,managerConfig:r?.managerConfig??s.params.managerConfig,managerDefaultWorkerAgentType:r?.managerDefaultWorkerAgentType??s.params.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:r?.managerDefaultWorkerModelId??s.params.managerDefaultWorkerModelId}}});await this.chatQueue.enqueueManagedRoomBatch(t)}async retireManagedRoomSession(e){this.chatQueue.purgeSession(e),await Re(this.getRuntime(),e)}discardPendingManagedRoomMessages(e){return this.chatQueue.discardPendingManagedRoomMessages(e)}async synchronizeSessionDeletions(){xe();try{await this.synchronizeSessionDeletionsOnce(),Ee()}catch(e){if(e instanceof Fe||e instanceof Oe)throw e;const t=e instanceof Error?e.message:String(e),s=/offline|not connected|disconnect|timed out|timeout|temporar|unavailable|ECONN|ENOTFOUND|EIO|EBUSY/i.test(t)?"transport_unavailable":/invalid (?:deletion sync|session deletion)|deletion revision gap/i.test(t)?"invalid_deletion_sync_response":"deletion_sync_failed";throw console.warn("[session.deletions] retrying failed synchronization",{code:s}),p({code:s,retryable:!0}),e}}async synchronizeSessionDeletionsOnce(){const e=u(),t=De(),s=await this.client.sendReq({type:"req",id:`session-deletions-sync-${h.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:g,dataEpoch:f,daemonInstallationId:e,afterRevision:t}});if(!s.ok){const o=s.rejection;throw o?(p({code:o.code,retryable:o.retryable&&o.requiredAction==="retry"}),new Error(o.code)):(p({code:"unstructured_rejection",retryable:!0}),new Error(s.error??"unstructured_rejection"))}const r=s.payload;c(r),He(r,t);let n=r.baselineRevision;n>t&&y(n);for(const o of r.deletions)o.deletionRevision<=n||(await this.applySessionDeletion(o),n=o.deletionRevision,y(n));if(n!==r.latestRevision)throw new Error(`deletion revision gap: applied ${n}, latest ${r.latestRevision}`);if(n===0)return;const d=await this.client.sendReq({type:"req",id:`session-deletions-ack-${h.randomUUID()}`,method:"session.deletions.ack",params:{protocolVersion:g,dataEpoch:f,daemonInstallationId:e,deletionRevision:n}});if(!d.ok)throw new Error(d.error??"session deletion acknowledgement failed")}async applySessionDeletion(e){const t=new Ce({daemonInstallationId:u()});t.recordSession(e);for(const r of e.sources){const n=t.find(r.agentType,r.sourceSessionKey);n&&n.sessionId!==e.sessionId||t.record({...e,...r})}this.runJournal.markSessionAborted(e.sessionId),this.recoveryBarrier.end(e.sessionId);const s=this.sessions.get(e.sessionId);if(s){this.revokeManagedRoomToken(s);try{await s.adapter.stop()}catch{}this.sessions.delete(e.sessionId)}this.historySubscriptions.closeSession(e.sessionId),this.nativeFusion?.handleSessionDeleted(e.sessionId),this.chatQueue.purgeSession(e.sessionId),Me(e.sessionId),be(e.sessionId),Se(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null),this.nativeCleanupOutbox.enqueue(e),this.nativeCleanupOutbox.kick()}publishSessionActivity(e,t){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:t}})}async handleExternalTerminal(e){const t=this.sessions.get(e);t&&(t.journalRunId&&(this.recoveryCoordinator.completeFromProvider(e,t.journalRunId),t.journalRunId=null),this.sessions.delete(e),this.revokeManagedRoomToken(t),t.heartbeatTimer&&clearInterval(t.heartbeatTimer),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})),this.chatQueue.noteTerminal(e)}async publishManagedActivitySnapshots(){for(const[e,t]of this.sessions.entries()){if(t.heartbeatTimer||!t.currentRunId||!t.adapter.getStatus)continue;const s=await t.adapter.getStatus().catch(()=>null);if(!s?.active||!s.runPhase)continue;const r=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:s.runId||t.currentRunId,runPhase:s.runPhase,startedAt:new Date(t.lastActiveAt).toISOString(),updatedAt:r,canStop:s.canStop??!0});const n=t.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:s.runId||t.currentRunId,seq:n,runPhase:s.runPhase,canStop:s.canStop??!0},seq:n,id:`agent-status-${s.runId||t.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const t of k())t.startsWith("custom:")&&I(t);const e=T();for(const[t,s]of Object.entries(e.customAgents??{}))fe(t,s)}async handleReq(e,t={}){const s=this.getRuntime();let r;this.activeRequests++;try{switch(e.method){case"schedule.deliver":if(!this.scheduleHandler)throw new Error("schedules_unavailable");await this.scheduleHandler(e);break;case"chat.send":if(e.params.scheduledDelivery)throw new Error("schedule_source_untrusted");c(e.params),this.recoveryBarrier.isDispatchFrozen()||this.recoveryBarrier.isRecovering(String(e.params.sessionId??""))?r={durableQueueHandoff:await this.chatQueue.handleEnqueue({...e,method:"chat.enqueue"})}:await U(s,e);break;case"chat.enqueue":c(e.params),r={durableQueueHandoff:await this.chatQueue.handleEnqueue(e)};break;case"chat.queue.get":c(e.params),await this.chatQueue.handleGet(e);break;case"chat.queue.edit":c(e.params),await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":c(e.params),await this.chatQueue.handleDelete(e);break;case"chat.queue.promote":c(e.params),await this.chatQueue.handlePromote(e);break;case"chat.abort":c(e.params),await B(s,e);break;case"session.refresh":c(e.params),await W(s,e);break;case"session.history.open":this.historySubscriptions.handleOpen(e);break;case"session.history.sync":this.historySubscriptions.handleSync(e);break;case"session.history.page":this.historySubscriptions.handlePage(e);break;case"session.history.close":this.historySubscriptions.handleClose(e);break;case"session.body.replica.subscribe":this.historySubscriptions.handleReplicaSubscribe(e);break;case"session.body.replica.unsubscribe":this.historySubscriptions.handleReplicaUnsubscribe(e);break;case"session.history":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"upgrade_required"});break;case"session.message.status":J(s,e);break;case"session.tool.detail":await $(s,e);break;case"voice.session.body.window":V(s,e);break;case"session.title.set":await H(s,e);break;case"room.agent.bind-managed":await ve(s,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await ye(s,this.managedRoomBinder,e);break;case"fs.ls":await Y(s,e);break;case"project.fs.open":case"project.fs.list":case"project.fs.read":case"project.fs.close":await ze(this.client,this.projectFsHandleStore,e,this.trustedProjectFsRequestResolver);break;case"fs.read":await K(s,e);break;case"fs.write":await Z(s,e);break;case"fs.export.markdown-pdf":await ae(s,e);break;case"fs.export.markdown-pdf.setup":await ne(s,e);break;case"fs.archive.zip":await re(s,e);break;case"fs.rename":await X(s,e);break;case"fs.transfer.start":await te(s,e);break;case"fs.transfer.chunk":await ee(s,e);break;case"fs.transfer.finish":await se(s,e);break;case"fs.transfer.abort":await q(s,e);break;case"region.probe":await ie(s,e);break;case"region.switch":await oe(s,e);break;case"upgrade.start":await P(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0,prepareForUpgrade:n=>this.prepareForUpgrade(n),cancelUpgradePreparation:()=>this.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()}});break;case"upgrade.status":await C(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await ce(s,e);break;case"machine.remote-access.pause":await L(this.client,this.remoteAccessPauseController,e);break;case"agents.refresh":await M(s,e);break;case"agent.permission.respond":await We(s,e);break;case"agent.auth.start":case"agent.auth.logout":case"agent.session-options.get":case"agent.external-sessions.list":case"agent.external-sessions.import":case"agent.external-sessions.close":case"agent.external-sessions.delete":await $e(s,e);break;case"models.refresh":await D(s,e);break;case"agent.config.get":await _(s,e);break;case"agent.config.upsert":await j(s,e);break;case"agent.config.clear":await O(s,e);break;case"agent.config.test":await Q(s,e);break;case"agent.workspace.validate":await N(s,e);break;case"agent.capabilities.list":await F(s,e);break;case"skill.list":case"skill.install":case"skill.doctor":case"skill.setup":case"skill.use":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;case"session.wechat-rpa.channel.get":case"session.wechat-rpa.channel.upsert":case"session.wechat-rpa.channel.sync":case"manager.wechat-rpa.channel.get":case"manager.wechat-rpa.channel.upsert":case"manager.wechat-rpa.channel.sync":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;default:this.client.sendRes({type:"res",id:e.id,ok:!1,error:`Unknown method: ${e.method}`})}}catch(n){if(t.propagateErrors)throw n;this.client.sendRes({type:"res",id:e.id,ok:!1,error:n instanceof Error?n.message:String(n)})}finally{this.activeRequests--}return r}evictIdleSessions(){if(this.sessions.size<Le)return;let e=null;for(const[t,s]of this.sessions)(!e||s.lastActiveAt<e.session.lastActiveAt)&&(e={key:t,session:s});e&&(this.revokeManagedRoomToken(e.session),e.session.heartbeatTimer&&(clearInterval(e.session.heartbeatTimer),e.session.heartbeatTimer=null),e.session.adapter.removeAllListeners(),e.session.adapter.stop().catch(()=>{}),this.sessions.delete(e.key))}resolvePath(e){return ge(e)}resolveAuthorizedPath(e,t){return me(e,he(t))}async resolveAuthorizedPathAsync(e,t){return pe(e,await ue(t))}async cleanup(){this.chatQueue.stop(),this.projectFsHandleStore.revokeAll(),this.acpDiscoveryWatcher.stop(),this.nativeFusion?.setExternalTerminalHandler?.(null),this.nativeCleanupOutbox.stop(),this.historySubscriptions.closeAll(),this.activityProbeTimer&&(clearInterval(this.activityProbeTimer),this.activityProbeTimer=null),this.attachmentCleanupTimer&&(clearInterval(this.attachmentCleanupTimer),this.attachmentCleanupTimer=null);for(const[,e]of this.sessions)this.revokeManagedRoomToken(e),e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),G(this.getRuntime()),await this.managerRuntime.stop(),v(null)}revokeManagedRoomToken(e){const t=e.managedRoomContextToken;e.managedRoomContextToken=null,t&&this.managedRoomContextRevoker?.(t)}}export{Gs as SessionManager,Xs as resolveSessionWorkDir};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{listSessionRecords as p}from"./store.js";function x(n,r){return n?{roomOrigin:n.roomOrigin,allowedRoomActions:n.roomOrigin?["send"]:[],managerEvent:!0}:r?{allowedRoomActions:[]}:void 0}function I(n,r){const e=u(r);if(e)return n.find(
|
|
1
|
+
import{listSessionRecords as p}from"./store.js";function x(n,r){return n?{roomOrigin:n.roomOrigin,allowedRoomActions:n.roomOrigin?["send"]:[],managerEvent:!0}:r?{allowedRoomActions:[]}:void 0}function I(n,r){const e=u(r);if(e)return n.find(o=>u(o)===e)??null;if(r.origin!=="external")return null;const t=f(r);return n.find(o=>f(o)===t)??null}function b(n){const{origin:r,managedRoom:e,managedAgentDirect:t,trigger:o,externalQueuedSubmissionId:a,...i}=n;return i}function M(n){const r=n.reduce((i,g)=>{const l=s(g);return i===null||l<i?l:i},null);if(r===null)return[];const e=n.filter(i=>s(i)===r),t=e[0];if(!t)return[];if(d(t))return[t];if(t.trigger?.kind==="manager_worker"||t.trigger?.kind==="scheduled")return[t];const o=m(t),a=[];for(const i of e){if(m(i)!==o)break;a.push(i)}return a}function d(n){if(n.origin==="external"||n.origin==="room"||n.managedRoom)return!1;const r=n.trigger?.kind;return r==null||r==="personal"||r==="managed_direct"}function _(n){return n?.trim()||void 0}function S(n,r){const e=n.find(t=>t.id===r&&d(t));return e?[e]:[]}function s(n){const r=c(n).kind;return r==="personal"||r==="managed_direct"?0:r==="manager_event"||r==="manager_worker"?1:2}function c(n){const r=n.trigger;return r?r.kind==="room_activation"?{kind:r.kind,registryKey:r.registryKey}:r.kind==="manager_worker"?{kind:r.kind,parentSessionId:r.parentSessionId,roomOrigin:r.roomOrigin??null}:r.kind==="manager_event"?{kind:r.kind,roomOrigin:r.roomOrigin??null}:{kind:r.kind}:n.managedRoom?{kind:"room_activation",registryKey:n.managedRoom.registryKey}:n.managedAgentDirect?{kind:"managed_direct"}:{kind:"personal"}}function m(n){return JSON.stringify({agentType:n.agentType,sessionMode:n.sessionMode??null,workDir:n.workDir,agentSessionId:n.agentSessionId??null,modelId:_(n.modelId)??null,systemPrompt:n.systemPrompt??null,reasoningEffort:n.reasoningEffort??null,managerConfig:n.managerConfig??null,managerDefaultWorkerAgentType:n.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:n.managerDefaultWorkerModelId??null,externalChannel:n.externalChannel??null,replyTarget:n.replyTarget??null,roomRegistryKey:n.managedRoom?.registryKey??null,trigger:c(n)})}function D(n){return n.every(r=>r.managedRoom)?`[\u7FA4\u6D88\u606F]
|
|
2
2
|
${n.map(r=>y(r.text)).filter(Boolean).join(`
|
|
3
3
|
|
|
4
4
|
`)}`:`[\u8FDE\u7EED\u7528\u6237\u6D88\u606F]
|
|
@@ -8,4 +8,4 @@ ${r.text.trim()}`).join(`
|
|
|
8
8
|
`)}`}function y(n){const r=n.trim(),e=r.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);return e?`<${e[1]}>
|
|
9
9
|
${e[2]}`:r.startsWith(`[\u7FA4\u6D88\u606F]
|
|
10
10
|
`)?r.slice(6):r}function f(n){const r=u(n);return r?`id:${r}`:["body",k(n.text),...(n.attachments??[]).map(e=>e.path||`${e.name}:${e.mimeType}`)].join(`
|
|
11
|
-
`)}function u(n){const r=typeof n.clientMessageId=="string"?n.clientMessageId.trim():"";if(r)return r;const e=typeof n.id=="string"?n.id.trim():"";return e&&!e.startsWith("queue-")?e:""}function
|
|
11
|
+
`)}function u(n){const r=typeof n.clientMessageId=="string"?n.clientMessageId.trim():"";if(r)return r;const e=typeof n.id=="string"?n.id.trim():"";return e&&!e.startsWith("queue-")?e:""}function R(n){const r=new Set,e=[];for(const t of n){const o=t.path||`${t.name}:${t.mimeType}`;r.has(o)||(r.add(o),e.push(t))}return e.length?e:void 0}function k(n){return String(n||"").replace(/\s+/g," ").trim()}function $(n,r){const e=n.toLowerCase();if(e.includes("managed claim session conflicts with source binding"))return"managed_claim_source_binding_conflict";const t=e.match(/\b(?:managed_(?:agent|room)|agent_validation|acp|native)_[a-z0-9_]+\b/)?.[0];return t||(e.includes("no rollout found")?"missing_rollout":e.includes("managed_room_permission_snapshot_mismatch")?"managed_room_permission_snapshot_mismatch":e.includes("managed_room_prompt_snapshot_mismatch")?"managed_room_prompt_snapshot_mismatch":e.includes("managed_room_turn_preparer_unavailable")?"managed_room_turn_preparer_unavailable":e.includes("timeout")?"timeout":e.includes("connection")?"connection_error":r)}function v(n,r){if(n.sessions.has(r.sessionId))return null;const e=p().find(t=>t.sessionId===r.sessionId)?.agentSessionId?.trim();return!e||e.startsWith("managed:")?null:e}export{$ as classifyQueueError,R as dedupeQueuedAttachments,I as findDuplicateQueuedMessage,D as formatQueuedBatch,d as isControllableQueueMessage,x as managedDirectTurnOptions,_ as normalizeQueuedModelId,v as resolveManagedRoomNativeSessionId,b as stripLocalQueueFields,M as takeCompatibleBatch,S as takeControllableMessage};
|
|
@@ -7,6 +7,8 @@ export type LocalQueuedChatMessage = QueuedChatMessage & {
|
|
|
7
7
|
/** Legacy persisted fields remain readable until all v1 queue files drain. */
|
|
8
8
|
managedRoom?: ManagedRoomTriggerContext;
|
|
9
9
|
managedAgentDirect?: true;
|
|
10
|
+
/** Codex accepted this item into an externally-owned native queue. */
|
|
11
|
+
externalQueuedSubmissionId?: string;
|
|
10
12
|
};
|
|
11
13
|
export type InternalEnqueueParams = ChatEnqueueParams & {
|
|
12
14
|
managedWorkerParentSessionId?: string;
|
|
@@ -29,11 +29,15 @@ export declare class ChatQueueManager {
|
|
|
29
29
|
getRuntime: () => SessionManagerRuntime;
|
|
30
30
|
dispatchReq: LocalReqDispatcher;
|
|
31
31
|
dispatchManagedReq?: (req: ReqFrame, prepared: PreparedManagedRoomTurn) => Promise<void>;
|
|
32
|
-
dispatchQueuedReq: (req: ReqFrame, prepared?: PreparedManagedRoomTurn) => Promise<void
|
|
32
|
+
dispatchQueuedReq: (req: ReqFrame, prepared?: PreparedManagedRoomTurn) => Promise<void | {
|
|
33
|
+
deliveryMode?: string;
|
|
34
|
+
queuedSubmissionId?: string;
|
|
35
|
+
}>;
|
|
33
36
|
interruptQueuedSession?: (sessionId: string) => Promise<void>;
|
|
34
37
|
});
|
|
35
38
|
stop(): void;
|
|
36
39
|
getSnapshot(sessionId: string): ChatQueueSnapshot;
|
|
40
|
+
private isImmediateSendAvailable;
|
|
37
41
|
setManagedRoomTurnPreparer(prepare: (contexts: ManagedRoomQueueContext[]) => Promise<PreparedManagedRoomTurn>): void;
|
|
38
42
|
setManagedAgentDirectTurnPreparer(prepare: ManagedAgentDirectTurnPreparer): void;
|
|
39
43
|
enqueueManagedRoomBatch(inputs: Array<{
|
|
@@ -57,10 +61,12 @@ export declare class ChatQueueManager {
|
|
|
57
61
|
private wakeSession;
|
|
58
62
|
private drainIdleQueues;
|
|
59
63
|
private drainNext;
|
|
64
|
+
noteExternalClaim(sessionId: string, canonicalMessageId: string): void;
|
|
60
65
|
private canInjectActiveBatch;
|
|
61
66
|
private removeBatch;
|
|
62
67
|
private logLifecycle;
|
|
63
68
|
private dispatchQueuedMessages;
|
|
69
|
+
private markExternalQueued;
|
|
64
70
|
private isSessionBusy;
|
|
65
71
|
private broadcast;
|
|
66
72
|
}
|
|
@@ -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 P,stripLocalQueueFields as T,takeCompatibleBatch as X,formatQueuedBatch as B,dedupeQueuedAttachments as Q,classifyQueueError as F,isControllableQueueMessage as C,takeControllableMessage as Y,normalizeQueuedModelId as W,resolveManagedRoomNativeSessionId as q,managedDirectTurnOptions as b}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 L,markAttachmentReferences as O}from"./attachment-transfer-store.js";import{appendMessage as j,getMessageDeliveryStatus as _,listSessionRecords as N,recordSession as z,tombstoneMessage as $,upsertMessage as x}from"./store.js";import{requestSessionIndexPublish as w}from"./index-publisher.js";import{clearPersistedManagerRoomOrigins as se,purgeQueuedSessionData as ne,readQueue as m,writeQueue as S}from"./queue-storage.js";import{getNativeSourceAgentType as ie}from"./handlers/chat-events.js";import{parsePersonalAttachmentReferences as K}from"./attachment-reference.js";import{getPersonalSyncIdentity as A}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 v,nowIso as J,queueEnvelope as R,queueMessageFromParams as G}from"./queue-message.js";import{purgeQueuedSessionData as De}from"./queue-storage.js";import{TerminalQueuedChatDispatchError as Ce}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);return{...A(E()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(T),controllablePending:s.filter(n=>C(n)&&!t?.has(n.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(),n=new Set,i=new Map;let d=!1;for(const o of e){const a=o.params;if(this.deletions.hasSession(a.sessionId)){delete t.sessions[a.sessionId],d=!0;continue}if(!a.sessionId||!a.text||!a.agentType||!a.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=G({...A(E()),...a});u.origin="room",u.managedRoom=o.context,u.trigger={kind:"room_activation",...o.context};const c=t.sessions[a.sessionId]??[],g=P(c,u),M=u.clientMessageId??u.id,k=_(a.sessionId,M);if(!g&&k.state==="not_found")c.push(u),t.sessions[a.sessionId]=c,i.set(`${u.sessionId}\0${M}`,u),n.add(a.sessionId),d=!0;else if(g&&k.state==="not_found")i.set(`${g.sessionId}\0${M}`,g),n.add(a.sessionId);else if(g&&k.state==="queued")n.add(a.sessionId);else if(g){const I=c.filter(y=>y.id!==g.id);I.length?t.sessions[a.sessionId]=I:delete t.sessions[a.sessionId],d=!0}}d&&S(t);let r;for(const o of n)try{for(const a of i.values())a.sessionId===o&&(j(o,R(a),"queued"),z({sessionId:o,agentType:a.agentType,sessionMode:a.sessionMode,managerConfig:a.managerConfig,workDir:a.workDir,agentSessionId:q(s,a),modelId:a.modelId}));this.logLifecycle("enqueue.persisted",o,{origin:"room",pendingCount:m().sessions[o]?.length??0}),this.broadcast(o),this.wakeSession(o)}catch(a){r=a,this.logLifecycle("enqueue.commit-failed",o,{failureClass:F(a instanceof Error?a.message:"","canonical_commit_failed")},"error"),this.retries.schedule(o,!0)}if(n.size&&w(s.client),r)throw r}async handleEnqueue(e,s=!1){const t=this.opts.getRuntime(),n=e.params;if(n.scheduledTask&&!s)throw new Error("schedule_source_untrusted");if((n.managerEventId||n.managerEventRoomOrigin||n.managedWorkerParentSessionId||n.managerWorkerRoomOrigin||e.params.managerWorkerNativeFull||e.params.managedRecoverySource)&&!ae(e))throw new Error("manager_event_source_untrusted");const i=!!(s&&n.scheduledTask?.bindingId);if(te(n.sessionListProjection),!n.sessionId||!n.text||!n.agentType||!n.workDir)return t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const d=b(n.managerEventId?{roomOrigin:n.managerEventRoomOrigin}:null,!!n.managedAgentDirect),r=n.managedAgentDirect||i?await this.prepareManagedAgentDirectTurn?.(n.sessionId,d):null;if((n.managedAgentDirect||i)&&!r)throw new Error("managed_agent_context_unavailable");const o=r?N().find(H=>H.sessionId===n.sessionId):null,a=r?{...n,...r,agentType:r.agentType,sessionMode:r.sessionMode,managerConfig:r.managerConfig,workDir:r.workDir,agentSessionId:r.agentSessionId?.startsWith("managed:")?o?.agentSessionId&&!o.agentSessionId.startsWith("managed:")?o.agentSessionId:null:r.agentSessionId,modelId:o?.modelId??void 0,systemPrompt:r.systemPrompt,externalChannel:n.managedAgentDirect?null:n.externalChannel,replyTarget:n.managedAgentDirect?null:n.replyTarget,managerDefaultWorkerAgentType:r.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:r.managerDefaultWorkerModelId}:n,u=t.resolvePath(a.workDir),c=K(a.attachments);c?.length&&L(u,c);const g={text:a.text,attachments:c,localized:!1},M=t.sessions.get(a.sessionId);if(a.agentType==="codex"&&a.agentSessionId&&M?.currentRunId?.startsWith("dispatch:")&&ee(a.agentSessionId))return t.client.sendRes({type:"res",id:e.id,ok:!1,error:Z}),!1;r?.managedRoomContextToken&&t.revokeManagedRoomContextToken?.(r.managedRoomContextToken);const k=await this.isSessionBusy(a.sessionId),I=m(),y=I.sessions[a.sessionId]??[],h=G({...a,text:g.text,attachments:g.attachments,systemPrompt:r?null:a.systemPrompt});r&&(n.managedAgentDirect&&(h.managedAgentDirect=!0),h.trigger?.kind!=="manager_event"&&h.trigger?.kind!=="scheduled"&&(h.trigger={kind:"managed_direct"}));const D=h.clientMessageId??h.id,l=_(a.sessionId,D);if(l.state!=="not_found")return t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:l.state==="queued",queueMessageId:h.id,...l,queue:this.getSnapshot(a.sessionId)}}),!0;const f=P(y,h);f||(I.sessions[a.sessionId]=[...y,h],S(I));const p=f??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,R(p),"queued"),O(t.resolvePath(p.workDir),p.attachments??[]),w(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:f?.id??h.id,..._(a.sessionId,f?.clientMessageId??f?.id??h.clientMessageId??h.id),queue:this.getSnapshot(a.sessionId),...g.localized?{localizedAttachments:!0}:{}}}),k?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 n=m(),i=n.sessions[t.sessionId]??[],d=i.findIndex(a=>a.id===t.queueMessageId);if(d<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!C(i[d])||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId)){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const r=K(t.attachments),o=s.resolvePath(i[d].workDir);r?.length&&L(o,r),i[d]={...i[d],text:t.text,attachments:r,updatedAt:J()},n.sessions[t.sessionId]=i,S(n),x(t.sessionId,R(i[d]),"queued"),i[d].attachments?.length&&O(o,i[d].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 n=m(),i=n.sessions[t.sessionId]??[],d=i.find(o=>o.id===t.queueMessageId);if(d&&(!C(d)||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId))){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const r=i.filter(o=>o.id!==t.queueMessageId);if(r.length===i.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}r.length?n.sessions[t.sessionId]=r:delete n.sessions[t.sessionId],S(n),$(t.sessionId,d?.clientMessageId??d?.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,n=d=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:d});if(!t.sessionId||!t.queueMessageId){n("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){n("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let i=[];try{for(;this.draining.has(t.sessionId);)await new Promise(o=>setTimeout(o,5));if(i=Y(m().sessions[t.sessionId]??[],t.queueMessageId),!i.length){n("Queued message not found");return}this.inFlightMessageIds.set(t.sessionId,new Set(i.map(o=>o.id)));const d=s.sessions.get(t.sessionId);let r="idle";if(d?.currentRunId)if(this.canInjectActiveBatch(d,i)){const o=i.length===1?i[0].text:B(i),a=i[0].clientMessageId??i[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:i[0].agentType,sourceAgentType:ie(i[0].agentType,i[0].modelId),canonicalMessageId:a,sourceSessionKey:d.agentSessionId,text:o,managedEchoPolicy:"suppress_following"});const u=Date.now();try{await d.adapter.injectActiveInput({text:o,attachments:Q(i.flatMap(c=>c.attachments??[])),clientMessageId:a})}catch(c){throw s.nativeFusion?.cancelManagedSend(a),c}for(const c of i)x(t.sessionId,{...R(c),ts:u},"executing");this.removeBatch(t.sessionId,i),r="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId),await this.dispatchQueuedMessages(i),this.removeBatch(t.sessionId,i),r="restarted"}else await this.dispatchQueuedMessages(i),this.removeBatch(t.sessionId,i);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(o=>o.id),mode:r}})}catch(d){n(d instanceof Error?d.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){ne(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const r=m().sessions[e]?.length??0;return this.purgeSession(e),r}const s=m(),t=s.sessions[e]??[],n=this.inFlightMessageIds.get(e)??new Set,i=t.filter(r=>r.managedRoom&&!n.has(r.id));if(i.length===0)return 0;const d=t.filter(r=>!i.includes(r));d.length?s.sessions[e]=d:delete s.sessions[e],S(s);for(const r of i)$(e,r.clientMessageId??r.id);return w(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:i.length,pendingCount:d.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=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,n=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const u=m().sessions[e]?.length??0,c=this.deferredLogState.get(e),g=Date.now();(!c||c.pendingCount!==u||g-c.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:u}),this.deferredLogState.set(e,{at:g,pendingCount:u}));return}this.deferredLogState.delete(e);const i=m(),d=i.sessions[e]??[],r=d.filter(u=>{const c=u.clientMessageId??u.id,g=_(e,c).state;return g==="queued"||g==="not_found"});r.length!==d.length&&(r.length?i.sessions[e]=r:delete i.sessions[e],S(i),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:d.length-r.length,pendingCount:r.length}));const o=X(r);if(o.length===0){this.broadcast(e);return}n=o,this.inFlightMessageIds.set(e,new Set(o.map(u=>u.id))),this.logLifecycle("dispatch.start",e,{batchCount:o.length,queueMessageId:o[0].id,origin:o[0].origin??"app"});for(const u of o)x(e,R(u),"queued");await this.dispatchQueuedMessages(o);const a=this.removeBatch(e,o);this.logLifecycle("dispatch.accepted",e,{batchCount:o.length,queueMessageId:o[0].id,pendingCount:a}),this.deferredLogState.delete(e),s=a>0,this.broadcast(e)}catch(i){const d=F(i instanceof Error?i.message:String(i),i instanceof v?"terminal_dispatch_failed":"transient_dispatch_failed"),r=n[0],o=!!(r&&!r.trigger&&!r.clientMessageId&&Date.now()-Date.parse(r.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(i instanceof v||d==="managed_claim_source_binding_conflict"||o){const u=this.removeBatch(e,n);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:n.length,queueMessageId:n[0]?.id,pendingCount:u,failureClass:o?"stale_legacy_managed_worker_dispatch_failure":d},"error"),s=u>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:d},"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),n=s[0].trigger?.kind==="managed_direct"||s[0].managedAgentDirect?null:"personal";return!!(n&&s[0].systemPrompt==null&&t&&t.source===n&&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(),n=new Set(s.map(d=>d.id)),i=(t.sessions[e]??[]).filter(d=>!n.has(d.id));return i.length?t.sessions[e]=i:delete t.sessions[e],S(t),i.length}logLifecycle(e,s,t,n="info"){const i=`[chat.queue] ${JSON.stringify({at:J(),event:e,sessionId:s,...t})}`;n==="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 n=e.flatMap(l=>{if(l.trigger?.kind==="room_activation"){const{kind:f,...p}=l.trigger;return[p]}return!l.trigger&&l.managedRoom?[l.managedRoom]:[]}),i=e.find(l=>l.trigger?.kind==="manager_event")?.trigger,d=e.some(l=>l.trigger?.kind==="managed_direct"||!!l.managedAgentDirect),r=d||!!t?.bindingId,o=e.find(l=>l.trigger?.kind==="manager_worker")?.trigger,a=i?.kind==="manager_event"||d,u=n.length>0?await this.prepareManagedRoomTurn?.(n):r||i?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(s.sessionId,b(i?.kind==="manager_event"?i:null,d)):void 0;if(n.length>0&&!u)throw new Error("managed_room_turn_preparer_unavailable");if(r&&!u)throw new Error("managed_agent_context_unavailable");if(i?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(s.sessionId)&&!u)throw new Error("managed_agent_context_unavailable");const c=new Set((m().sessions[s.sessionId]??[]).map(l=>l.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(l=>!c.has(l.id)))throw u?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(u.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new v("queued_dispatch_cancelled");const g=u??null,M=g?.sessionMode==="manager"?g.managerConfig:null,k=e.length===1?s.text:B(e),I=Q(e.flatMap(l=>l.attachments??[])),y=r||i?.kind==="manager_event"&&!!u,h=y?u.agentSessionId?.startsWith("managed:")?q(this.opts.getRuntime(),s):u.agentSessionId:n.length>0?q(this.opts.getRuntime(),s):s.agentSessionId??null,D=u?N().find(l=>l.sessionId===s.sessionId):null;await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...A(E()),sessionId:s.sessionId,text:k,agentType:y?u.agentType:s.agentType,sessionMode:g?.sessionMode??s.sessionMode,managerConfig:g?M:s.managerConfig,workDir:y?u.workDir:s.workDir,agentSessionId:h,modelId:W(D?.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:I,externalChannel:a?void 0:s.externalChannel,replyTarget:a?void 0:s.replyTarget,roomContextRegistryKey:a?void 0:u?.roomContextRegistryKey,activationCauseMessageId:a?void 0:n.at(-1)?.causeMessageId,rootCauseMessageId:a?void 0:n.at(-1)?.rootCauseMessageId,activationSequence:a?void 0:n.at(-1)?.sequence,activationHopCount:a?void 0:n.at(-1)?.hopCount,managedRecoverySource:i?.kind==="manager_event"?"manager_event":r?"managed_direct":n.length>0?"managed_room":void 0,managerEventId:i?.kind==="manager_event"?i.eventId:void 0,managedWorkerParentSessionId:o?.kind==="manager_worker"?o.parentSessionId:void 0,managerWorkerRoomOrigin:o?.kind==="manager_worker"?o.roomOrigin:void 0,managerWorkerNativeFull:o?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isNativeSkillManager(o.parentSessionId,s.workDir)||void 0,managedRoomContextToken:u?.managedRoomContextToken,scheduledDelivery:!!t,suppressExternalOwnerEcho:n.length>0||!!t||void 0,queuedBatchItems:e.map(l=>{const f=T(l);return a?{...f,systemPrompt:u?.systemPrompt??null,externalChannel:null,replyTarget:null}:f}),waitForDispatch:!0}},u??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 n=await t.adapter.getStatus().catch(()=>null);return n?.active?(n.runId&&(t.currentRunId=n.runId,t.currentRunPhase=n.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,Ce as TerminalQueuedChatDispatchError,De 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 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};
|
|
@@ -5,5 +5,8 @@ import { type PreparedManagedRoomTurn } from './queue.js';
|
|
|
5
5
|
export declare function managedChatContext(prepared: PreparedManagedRoomTurn): ChatSendExecutionContext;
|
|
6
6
|
export declare function managedManagerEventContext(prepared: PreparedManagedRoomTurn): ChatSendExecutionContext;
|
|
7
7
|
export declare function dispatchManagedChat(runtime: SessionManagerRuntime, req: ReqFrame, prepared: PreparedManagedRoomTurn): Promise<void>;
|
|
8
|
-
export declare function dispatchQueuedChat(runtime: SessionManagerRuntime, req: ReqFrame, prepared?: PreparedManagedRoomTurn): Promise<
|
|
8
|
+
export declare function dispatchQueuedChat(runtime: SessionManagerRuntime, req: ReqFrame, prepared?: PreparedManagedRoomTurn): Promise<{
|
|
9
|
+
deliveryMode?: string;
|
|
10
|
+
queuedSubmissionId?: string;
|
|
11
|
+
}>;
|
|
9
12
|
export declare function interruptQueuedChat(runtime: SessionManagerRuntime, sessionId: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{handleChatAbort as s,handleChatSend as
|
|
1
|
+
import{handleChatAbort as s,handleChatSend as d}from"./handlers/chat.js";import{TerminalQueuedChatDispatchError as h}from"./queue.js";import{markTrustedManagerEvent as r}from"./manager-event-authority.js";function c(e){return{source:"room_activation",managedAgentPolicy:e.managedAgentPolicy,managedRoomPromptSnapshot:e.managedRoomPromptSnapshot}}function f(e){return{source:"manager_event",managedAgentPolicy:e.managedAgentPolicy,managedRoomPolicyVersion:e.managedRoomPromptSnapshot.managedRoomPolicyVersion,managedRuntimePolicyHash:e.managedRoomPromptSnapshot.runtimePermissionPolicyHash}}async function p(e,i,o){await d(e,r(i),c(o))}async function P(e,i,o){const a={value:null},n=new Proxy(e.client,{get:(t,l)=>{if(l==="sendRes")return m=>{a.value=m};const u=Reflect.get(t,l,t);return typeof u=="function"?u.bind(t):u}});if(await d({...e,client:n},r(i),o?i.params.managedRecoverySource==="managed_room"||i.params.scheduledDelivery===!0?c(o):f(o):void 0),a.value?.payload?.deliveryState==="failed")throw new h(a.value.payload.failureMessage??a.value.error??"queued_chat_dispatch_failed");if(!a.value?.ok)throw new Error(a.value?.error??"queued_chat_dispatch_failed");return{deliveryMode:a.value.payload?.deliveryMode,queuedSubmissionId:a.value.payload?.queuedSubmissionId}}async function R(e,i){const o={value:null},a=new Proxy(e.client,{get:(n,t)=>t==="sendRes"?l=>{o.value=l}:typeof Reflect.get(n,t,n)=="function"?Reflect.get(n,t,n).bind(n):Reflect.get(n,t,n)});if(await s({...e,client:a},{type:"req",id:`queue-promote-abort-${Date.now()}`,method:"chat.abort",params:{sessionId:i,discardPending:!1}}),!o.value?.ok)throw new Error(o.value?.error??"agent_stop_failed")}export{p as dispatchManagedChat,P as dispatchQueuedChat,R as interruptQueuedChat,c as managedChatContext,f as managedManagerEventContext};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { type AgentType, type Envelope, type ManagerConfig, type
|
|
1
|
+
import { type AgentType, type Envelope, type ManagerConfig, type SessionDeliveryState, type SessionIndexUpdate, type SessionMessageStatusResponse, type SessionMode, type SessionTitleGenerateResponse, type SessionTitleGenerationEligibility, type SessionTitleSource } from '@shennian/wire';
|
|
2
|
+
import { getDaemonInstallationId } from '../personal-sync/identity.js';
|
|
2
3
|
import { PERSONAL_SYNC_V1_ROOT_NAME } from '../personal-sync/paths.js';
|
|
3
4
|
import { CanonicalSessionStore } from './canonical-store.js';
|
|
4
|
-
import { getDaemonInstallationId } from '../personal-sync/identity.js';
|
|
5
5
|
export { PERSONAL_SYNC_V1_ROOT_NAME };
|
|
6
6
|
export type LocalSessionRecord = {
|
|
7
7
|
daemonInstallationId: string;
|
|
8
8
|
sessionId: string;
|
|
9
9
|
agentType: AgentType;
|
|
10
10
|
sessionMode?: SessionMode;
|
|
11
|
+
nativeSessionVisibility?: 'user' | 'internal';
|
|
11
12
|
managerConfig?: ManagerConfig | null;
|
|
12
13
|
workDir: string;
|
|
13
14
|
agentSessionId?: string | null;
|
|
@@ -60,6 +61,7 @@ export declare function recordSession(input: {
|
|
|
60
61
|
sessionId: string;
|
|
61
62
|
agentType: AgentType;
|
|
62
63
|
sessionMode?: SessionMode;
|
|
64
|
+
nativeSessionVisibility?: 'user' | 'internal';
|
|
63
65
|
managerConfig?: ManagerConfig | null;
|
|
64
66
|
workDir: string;
|
|
65
67
|
agentSessionId?: string | null;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import O from"node:crypto";import c from"node:fs";import A from"node:path";import{SESSION_SYNC_PROTOCOL_VERSION as x,SESSION_SYNC_DATA_EPOCH as I,SESSION_TITLE_SOURCE_PRIORITY as m,isToolPayload as P,sessionPreviewFromPayload as h}from"@shennian/wire";import{PERSONAL_SYNC_V1_ROOT_NAME as V,resolvePersonalSyncV1Path as G}from"../personal-sync/paths.js";import{CanonicalSessionStore as L}from"./canonical-store.js";import{getDaemonInstallationId as g}from"../personal-sync/identity.js";import{NativeSourceDeletionStore as q}from"../native-fusion/native-source-deletions.js";const D=120;function p(t){if(t.length<=D)return t;let e=t.slice(0,D);const n=e.charCodeAt(e.length-1);return n>=55296&&n<=56319&&(e=e.slice(0,-1)),e}let w=null,_=null;function R(){return G()}function u(){const t=A.join(R(),"canonical");return(!w||_!==t)&&(w=new L(t,g()),_=t),w}function k(){return A.join(R(),"session-index.json")}function d(){const t=k();if(!c.existsSync(t))return{};const e=JSON.parse(c.readFileSync(t,"utf8"));if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`invalid personal session index: ${t}`);const n=g();for(const s of Object.values(e))if(s.daemonInstallationId!==n)throw new Error(`personal session index belongs to another daemon installation: ${t}`);return e}function S(t){J(k(),t)}function M(t){return h(t.payload,120)}function y(t){if(new q({rootDir:R(),daemonInstallationId:g()}).hasSession(t))throw new Error(`session_deleted:${t}`)}function j(t){y(t.sessionId),g();const e=d(),n=e[t.sessionId],s=new Date().toISOString(),i=u().getManifest(t.sessionId)?.bodyRevision??n?.bodyRevision??0,o=u().getManifest(t.sessionId),a=z(n,t),r=t.attentionId?.trim()||null,l=Array.from(new Set([...n?.seenAttentionIds??[],...n?.lastAttentionId?[n.lastAttentionId]:[]])),f=!!(r&&!l.includes(r)),v=t.lastMessagePreview===null?{preview:null,messageId:null,at:null}:t.lastMessagePreview!==void 0&&t.lastMessagePreviewMessageId&&t.lastMessagePreviewAt?{preview:t.lastMessagePreview,messageId:t.lastMessagePreviewMessageId,at:t.lastMessagePreviewAt}:{preview:n?.lastMessagePreview??null,messageId:n?.lastMessagePreviewMessageId??null,at:n?.lastMessagePreviewAt??null},b=Date.parse(t.lastVisibleEventAt??""),E=Date.parse(n?.lastVisibleEventAt??""),U=Number.isFinite(b)&&(!Number.isFinite(E)||b>=E)?t.lastVisibleEventAt:n?.lastVisibleEventAt??null,C={daemonInstallationId:g(),sessionId:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode??n?.sessionMode,managerConfig:t.managerConfig??n?.managerConfig??null,workDir:t.workDir,agentSessionId:t.agentSessionId??n?.agentSessionId??null,modelId:t.modelId??n?.modelId??null,managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType??n?.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:t.managerDefaultWorkerModelId??n?.managerDefaultWorkerModelId??null,status:t.status??n?.status??"active",createdAt:n?.createdAt??s,updatedAt:s,lastActivityAt:t.lastActivityAt??n?.lastActivityAt??s,lastMessagePreview:v.preview,lastMessagePreviewMessageId:v.messageId,lastMessagePreviewAt:v.at,lastVisibleEventAt:U,localReady:n?.localReady===!0||t.localReady===!0,firstUserMessageId:n?.firstUserMessageId??t.firstUserMessageId??null,firstUserBodyRevision:n?.firstUserBodyRevision??t.firstUserBodyRevision??null,bodyRevision:i,indexRevision:(n?.indexRevision??0)+1,...a,firstAvailableSeq:o?.firstAvailableSeq??n?.firstAvailableSeq??null,visibleMessageCount:o?.visibleMessageCount??n?.visibleMessageCount??0,lastVisibleMessageId:o?.lastVisibleMessageId??n?.lastVisibleMessageId??null,attentionCount:(n?.attentionCount??0)+(f?1:0),lastAttentionId:f?r:n?.lastAttentionId??null,seenAttentionIds:f&&r?[...l,r]:l,lastBodyCommittedAt:o?.lastBodyCommittedAt??n?.lastBodyCommittedAt??null,deletionRevision:n?.deletionRevision??0,deletedAt:t.deletedAt??n?.deletedAt??null,syncedIndexRevision:n?.syncedIndexRevision??0,indexSyncState:"pending",indexSyncStateRevision:(n?.indexRevision??0)+1,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,indexQuarantineCode:null,titleGenerationAttemptedAt:n?.titleGenerationAttemptedAt&&n.titleGenerationNextAttemptAt!==void 0?n.titleGenerationAttemptedAt:null,titleGenerationNextAttemptAt:n?.titleGenerationNextAttemptAt??null,titleGenerationAttempts:n?.titleGenerationAttempts??0};return e[t.sessionId]=C,S(e),C}function F(){return Object.values(d())}function se(t){if(!t)throw new Error("sessionId is required");const e=d();e[t]&&(delete e[t],S(e))}function oe(t){u().purgeSession(t)}function W(){const t=d();let e=!1;const n=new Date().toISOString();for(const[s,i]of Object.entries(t)){const o=p(i.title);if(o===i.title)continue;const a=i.indexRevision+1;t[s]={...i,title:o,indexRevision:a,indexSyncState:"pending",indexSyncStateRevision:a,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,indexQuarantineCode:null,updatedAt:n},e=!0}e&&S(t)}function ae(){const t=g();return Y(),Q(),W(),F().filter(e=>{const n=e.indexSyncStateRevision===e.indexRevision;return e.indexQuarantineCode||n&&(e.indexSyncState==="quarantined"||e.indexSyncState==="terminal")||n&&e.indexSyncNextAttemptAt&&Date.parse(e.indexSyncNextAttemptAt)>Date.now()?!1:e.indexRevision>e.syncedIndexRevision&&e.localReady&&!!e.firstUserMessageId&&e.firstUserBodyRevision!=null}).sort((e,n)=>e.indexRevision-n.indexRevision).map(e=>{const n=!!(e.lastMessagePreview&&e.lastMessagePreviewMessageId&&e.lastMessagePreviewAt);return{protocolVersion:x,dataEpoch:I,daemonInstallationId:t,session:{id:e.sessionId,agentType:e.agentType,sessionMode:e.sessionMode,managerConfig:e.managerConfig,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null,managerDefaultWorkerAgentType:e.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:e.managerDefaultWorkerModelId??null,title:e.title,titleSource:e.titleSource,titleRevision:e.titleRevision,titleLockedByUser:e.titleLockedByUser,titleOriginEventId:e.titleOriginEventId,status:e.status??"active",workDir:e.workDir,lastMessagePreview:n?e.lastMessagePreview:null,lastMessagePreviewMessageId:n?e.lastMessagePreviewMessageId:null,lastMessagePreviewAt:n?e.lastMessagePreviewAt:null,lastVisibleEventAt:e.lastVisibleEventAt??e.lastMessagePreviewAt??null,bodyStorageMode:"machine_local",localReady:!0,firstUserBoundary:{role:"user",messageId:e.firstUserMessageId,bodyRevision:e.firstUserBodyRevision},bodyRevision:e.bodyRevision,indexRevision:e.indexRevision,firstAvailableSeq:e.firstAvailableSeq,visibleMessageCount:e.visibleMessageCount,lastVisibleMessageId:e.lastVisibleMessageId,attentionCount:e.attentionCount??0,lastAttentionId:e.lastAttentionId??null,lastBodyCommittedAt:e.lastBodyCommittedAt,deletionRevision:e.deletionRevision,lastActivityAt:e.lastActivityAt,createdAt:e.createdAt,updatedAt:e.updatedAt,deletedAt:e.deletedAt}}})}function le(t,e){const n=d(),s=n[t];if(!(!s||e<=s.syncedIndexRevision)){if(e>s.indexRevision)throw new Error(`cannot acknowledge future index revision ${e} for ${t}`);n[t]={...s,syncedIndexRevision:e,indexSyncState:"applied",indexSyncStateRevision:e,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null},S(n)}}function re(){const t=d();let e=0;const n=new Date().toISOString();for(const[s,i]of Object.entries(t)){if(i.deletedAt||i.indexQuarantineCode||!i.localReady)continue;const o=i.indexRevision+1;t[s]={...i,indexRevision:o,indexSyncState:"pending",indexSyncStateRevision:o,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,updatedAt:n},e+=1}return e>0&&S(t),e}function de(t){const e=d(),n=e[t.sessionId];if(!n||n.indexRevision!==t.indexRevision)return"terminal";const i=(n.indexSyncStateRevision===t.indexRevision?n.indexSyncAttempts??0:0)+1;let o=t.disposition;o==="retry"&&i>=(t.maxAttempts??8)&&(o="terminal");const a=t.now??Date.now(),r=Math.min(5e3*2**Math.max(0,i-1),360*60*1e3),l=Math.round(r*.2*(Math.random()*2-1));return e[t.sessionId]={...n,indexSyncState:o==="retry"?"pending":o,indexSyncStateRevision:t.indexRevision,indexSyncAttempts:i,indexSyncNextAttemptAt:o==="retry"?new Date(a+r+l).toISOString():null,indexSyncErrorCode:t.code,indexQuarantineCode:o==="quarantined"?t.code:n.indexQuarantineCode??null},S(e),o}const $=2*6e4;function ce(t=Date.now(),e=$){const n=d(),s=[],i=new Date(t).toISOString();for(const o of Object.values(n)){if(o.titleSource!=="first_user_fallback"||o.titleLockedByUser||o.titleGenerationAttemptedAt||!o.title.trim()||o.syncedIndexRevision<o.indexRevision||o.titleGenerationNextAttemptAt&&Date.parse(o.titleGenerationNextAttemptAt)>t)continue;const a=o.status==="completed"||o.status==="failed",r=o.visibleMessageCount>=2;let l=null;if(o.agentType==="claude"&&r?l="agent_native_unavailable":a?l="terminal":r&&t-Date.parse(o.createdAt)>=e&&(l="grace_elapsed"),!l)continue;const f=h(o.title,240);if(!f)continue;const v=O.randomUUID();n[o.sessionId]={...o,titleGenerationAttemptedAt:i,titleGenerationNextAttemptAt:null,titleGenerationAttempts:(o.titleGenerationAttempts??0)+1},s.push({sessionId:o.sessionId,requestId:v,titleSeed:f,baseTitleRevision:o.titleRevision,eligibility:l})}return s.length>0&&S(n),s}function ue(t,e=Date.now()){const n=d(),s=n[t];if(!s?.titleGenerationAttemptedAt||s.titleSource!=="first_user_fallback")return null;const i=Math.max(1,s.titleGenerationAttempts??1),o=Math.min(10*6e4,3e4*2**Math.min(i-1,5)),a=new Date(e+o).toISOString();return n[t]={...s,titleGenerationAttemptedAt:null,titleGenerationNextAttemptAt:a},S(n),a}function ge(t,e){const s=d()[t];return!s||s.titleLockedByUser||m[s.titleSource]>=m.shennian_generated||e.titleRevision<=s.titleRevision?!1:(j({sessionId:t,agentType:s.agentType,sessionMode:s.sessionMode,managerConfig:s.managerConfig,workDir:s.workDir,agentSessionId:s.agentSessionId,modelId:s.modelId,status:s.status,title:e.title,titleSource:"shennian_generated",titleRevision:e.titleRevision,titleLockedByUser:!1,titleOriginEventId:e.titleOriginEventId}),!0)}function Se(t,e,n){y(t),g();const s=u().append(t,e,n);return T(t,e,s.bodyRevision),s.bodyRevision}function fe(t,e,n){y(t),g();const s=u().upsert(t,e,n);return T(t,e,s.bodyRevision),s.bodyRevision}function ve(t,e){const n=u().getMessageState(t,e);return!n||n.tombstoned||!n.message||!n.deliveryState?{protocolVersion:x,dataEpoch:I,daemonInstallationId:g(),state:"not_found",sessionId:t,clientMessageId:e}:{protocolVersion:x,dataEpoch:I,daemonInstallationId:g(),state:n.deliveryState,sessionId:t,clientMessageId:e,messageId:n.message.id,deliveryState:n.deliveryState,bodyRevision:n.lastRevision,committedAt:new Date(n.message.ts).toISOString()}}function me(t,e){y(t),g();const n=u().tombstone(t,e),s=d(),i=s[t];if(i){const o=e===i.lastMessagePreviewMessageId?N(t):{preview:i.lastMessagePreview??null,messageId:i.lastMessagePreviewMessageId??null,at:i.lastMessagePreviewAt??null};s[t]={...i,bodyRevision:n.bodyRevision,indexRevision:i.indexRevision+1,indexSyncState:"pending",indexSyncStateRevision:i.indexRevision+1,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,updatedAt:new Date().toISOString(),lastMessagePreview:o.preview,lastMessagePreviewMessageId:o.messageId,lastMessagePreviewAt:o.at,lastVisibleEventAt:B(t)},S(s)}return n.bodyRevision}function T(t,e,n){const s=d(),i=s[t];if(!i)return;const o=M(e),a=o?{lastMessagePreview:o,lastMessagePreviewMessageId:e.id,lastMessagePreviewAt:new Date(e.ts).toISOString()}:null,r=o||P(e.payload)?new Date(e.ts).toISOString():i.lastVisibleEventAt,l=u().getManifest(t),f=e.role==="user"&&o&&!i.title?{title:p(o),titleSource:"first_user_fallback",titleRevision:i.titleRevision+1,titleLockedByUser:!1,titleOriginEventId:e.id}:null,v=e.role==="user"&&!i.localReady?{localReady:!0,firstUserMessageId:e.id,firstUserBodyRevision:n}:null;s[t]={...i,...f??{},...v??{},bodyRevision:n,indexRevision:i.indexRevision+1,indexSyncState:"pending",indexSyncStateRevision:i.indexRevision+1,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,updatedAt:new Date().toISOString(),lastActivityAt:new Date(e.ts).toISOString(),lastVisibleEventAt:r,...a??{},firstAvailableSeq:l?.firstAvailableSeq??i.firstAvailableSeq,visibleMessageCount:l?.visibleMessageCount??i.visibleMessageCount,lastVisibleMessageId:l?.lastVisibleMessageId??i.lastVisibleMessageId,attentionCount:i.attentionCount??0,lastAttentionId:i.lastAttentionId??null,lastBodyCommittedAt:l?.lastBodyCommittedAt??i.lastBodyCommittedAt},S(s)}function N(t){let e;do{const n=u().readPage(t,{limit:500,beforeSequence:e});for(let s=n.entries.length-1;s>=0;s-=1){const i=n.entries[s],o=M(i.message);if(o)return{preview:o,messageId:i.message.id,at:new Date(i.message.ts).toISOString()}}if(!n.hasOlder||n.entries.length===0)break;e=n.entries[0].sequence}while(e!==void 0);return{preview:null,messageId:null,at:null}}function B(t){let e;do{const n=u().readPage(t,{limit:500,beforeSequence:e});for(let s=n.entries.length-1;s>=0;s-=1){const i=n.entries[s].message;if(!(!M(i)&&!P(i.payload)))return new Date(i.ts).toISOString()}if(!n.hasOlder||n.entries.length===0)break;e=n.entries[0].sequence}while(e!==void 0);return null}function Q(){const t=d();let e=!1;const n=new Date().toISOString();for(const[s,i]of Object.entries(t)){if(Object.prototype.hasOwnProperty.call(i,"lastVisibleEventAt"))continue;const o=i.indexRevision+1;t[s]={...i,lastVisibleEventAt:B(s)??i.lastMessagePreviewAt??null,indexRevision:o,indexSyncState:"pending",indexSyncStateRevision:o,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,updatedAt:n},e=!0}e&&S(t)}function Y(){const t=d();let e=!1;const n=new Date().toISOString();for(const[s,i]of Object.entries(t)){if(Object.prototype.hasOwnProperty.call(i,"lastMessagePreviewMessageId")&&Object.prototype.hasOwnProperty.call(i,"lastMessagePreviewAt"))continue;const o=N(s);t[s]={...i,lastMessagePreview:o.preview,lastMessagePreviewMessageId:o.messageId,lastMessagePreviewAt:o.at,indexRevision:i.indexRevision+1,indexSyncState:"pending",indexSyncStateRevision:i.indexRevision+1,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,updatedAt:n},e=!0}e&&S(t)}function z(t,e){const n={title:t?.title??"",titleSource:t?.titleSource??"first_user_fallback",titleRevision:t?.titleRevision??0,titleLockedByUser:t?.titleLockedByUser??!1,titleOriginEventId:t?.titleOriginEventId??null},s=p(e.title?.trim()??"");if(!s)return n;if(!e.titleSource)throw new Error("titleSource is required with title");if(n.title&&n.titleSource==="first_user_fallback"&&e.titleSource==="first_user_fallback")return n;const i=e.titleSource==="shennian_manual";if(e.titleLockedByUser!==void 0&&e.titleLockedByUser!==i)throw new Error("titleLockedByUser conflicts with titleSource");const o=s===n.title&&e.titleSource===n.titleSource,a=e.titleRevision??n.titleRevision+(o?0:1);if(!Number.isSafeInteger(a)||a<0)throw new Error("invalid titleRevision");const r=m[e.titleSource],l=m[n.titleSource];return r<l||a<n.titleRevision||o&&a===n.titleRevision||a===n.titleRevision&&!o?n:{title:s,titleSource:e.titleSource,titleRevision:a,titleLockedByUser:i,titleOriginEventId:e.titleOriginEventId??n.titleOriginEventId}}function ye(t,e){const n=e?.limit&&e.limit>0?Math.min(e.limit,500):500;let i=u().readRecent(t,n).messages;return e?.before!==void 0&&(i=i.filter(o=>o.ts<e.before)),[...i].sort((o,a)=>a.ts-o.ts)}function Ae(){return u().listSessionIds()}function xe(){return u()}function J(t,e){const n=A.dirname(t);c.mkdirSync(n,{recursive:!0,mode:448});const s=`${t}.tmp-${process.pid}-${O.randomBytes(6).toString("hex")}`;let i;try{if(i=c.openSync(s,"wx",384),c.writeFileSync(i,`${JSON.stringify(e,null,2)}
|
|
2
|
-
`,"utf8"),c.fsyncSync(i),c.closeSync(i),i=void 0,c.renameSync(s,t),process.platform!=="win32"){const o=c.openSync(n,"r");try{c.fsyncSync(o)}finally{c.closeSync(o)}}}finally{i!==void 0&&c.closeSync(i);try{c.unlinkSync(s)}catch{}}}export{
|
|
1
|
+
import{SESSION_SYNC_DATA_EPOCH as x,SESSION_SYNC_PROTOCOL_VERSION as I,SESSION_TITLE_SOURCE_PRIORITY as y,isInternalSession as O,isToolPayload as P,sessionPreviewFromPayload as h}from"@shennian/wire";import D from"node:crypto";import c from"node:fs";import p from"node:path";import{NativeSourceDeletionStore as G}from"../native-fusion/native-source-deletions.js";import{registerInternalNativeSource as L}from"../native-fusion/source-visibility.js";import{getDaemonInstallationId as g}from"../personal-sync/identity.js";import{PERSONAL_SYNC_V1_ROOT_NAME as q,resolvePersonalSyncV1Path as j}from"../personal-sync/paths.js";import{CanonicalSessionStore as F}from"./canonical-store.js";const _=120;function w(t){if(t.length<=_)return t;let e=t.slice(0,_);const n=e.charCodeAt(e.length-1);return n>=55296&&n<=56319&&(e=e.slice(0,-1)),e}let M=null,k=null;function R(){return j()}function u(){const t=p.join(R(),"canonical");return(!M||k!==t)&&(M=new F(t,g()),k=t),M}function T(){return p.join(R(),"session-index.json")}function d(){const t=T();if(!c.existsSync(t))return{};const e=JSON.parse(c.readFileSync(t,"utf8"));if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`invalid personal session index: ${t}`);const n=g();for(const s of Object.values(e))if(s.daemonInstallationId!==n)throw new Error(`personal session index belongs to another daemon installation: ${t}`);return e}function S(t){X(T(),t)}function b(t){return h(t.payload,120)}function A(t){if(new G({rootDir:R(),daemonInstallationId:g()}).hasSession(t))throw new Error(`session_deleted:${t}`)}function W(t){A(t.sessionId),g();const e=d(),n=e[t.sessionId],s=new Date().toISOString(),i=u().getManifest(t.sessionId)?.bodyRevision??n?.bodyRevision??0,o=u().getManifest(t.sessionId),a=H(n,t),r=t.attentionId?.trim()||null,l=Array.from(new Set([...n?.seenAttentionIds??[],...n?.lastAttentionId?[n.lastAttentionId]:[]])),f=!!(r&&!l.includes(r)),v=t.lastMessagePreview===null?{preview:null,messageId:null,at:null}:t.lastMessagePreview!==void 0&&t.lastMessagePreviewMessageId&&t.lastMessagePreviewAt?{preview:t.lastMessagePreview,messageId:t.lastMessagePreviewMessageId,at:t.lastMessagePreviewAt}:{preview:n?.lastMessagePreview??null,messageId:n?.lastMessagePreviewMessageId??null,at:n?.lastMessagePreviewAt??null},E=Date.parse(t.lastVisibleEventAt??""),C=Date.parse(n?.lastVisibleEventAt??""),U=Number.isFinite(E)&&(!Number.isFinite(C)||E>=C)?t.lastVisibleEventAt:n?.lastVisibleEventAt??null,m={daemonInstallationId:g(),sessionId:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode??n?.sessionMode,nativeSessionVisibility:O({...n,id:t.sessionId})||O({...n,...t,id:t.sessionId,sessionMode:t.sessionMode??n?.sessionMode})?"internal":"user",managerConfig:t.managerConfig??n?.managerConfig??null,workDir:t.workDir,agentSessionId:t.agentSessionId??n?.agentSessionId??null,modelId:t.modelId??n?.modelId??null,managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType??n?.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:t.managerDefaultWorkerModelId??n?.managerDefaultWorkerModelId??null,status:t.status??n?.status??"active",createdAt:n?.createdAt??s,updatedAt:s,lastActivityAt:t.lastActivityAt??n?.lastActivityAt??s,lastMessagePreview:v.preview,lastMessagePreviewMessageId:v.messageId,lastMessagePreviewAt:v.at,lastVisibleEventAt:U,localReady:n?.localReady===!0||t.localReady===!0,firstUserMessageId:n?.firstUserMessageId??t.firstUserMessageId??null,firstUserBodyRevision:n?.firstUserBodyRevision??t.firstUserBodyRevision??null,bodyRevision:i,indexRevision:(n?.indexRevision??0)+1,...a,firstAvailableSeq:o?.firstAvailableSeq??n?.firstAvailableSeq??null,visibleMessageCount:o?.visibleMessageCount??n?.visibleMessageCount??0,lastVisibleMessageId:o?.lastVisibleMessageId??n?.lastVisibleMessageId??null,attentionCount:(n?.attentionCount??0)+(f?1:0),lastAttentionId:f?r:n?.lastAttentionId??null,seenAttentionIds:f&&r?[...l,r]:l,lastBodyCommittedAt:o?.lastBodyCommittedAt??n?.lastBodyCommittedAt??null,deletionRevision:n?.deletionRevision??0,deletedAt:t.deletedAt??n?.deletedAt??null,syncedIndexRevision:n?.syncedIndexRevision??0,indexSyncState:"pending",indexSyncStateRevision:(n?.indexRevision??0)+1,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,indexQuarantineCode:null,titleGenerationAttemptedAt:n?.titleGenerationAttemptedAt&&n.titleGenerationNextAttemptAt!==void 0?n.titleGenerationAttemptedAt:null,titleGenerationNextAttemptAt:n?.titleGenerationNextAttemptAt??null,titleGenerationAttempts:n?.titleGenerationAttempts??0};return m.nativeSessionVisibility==="internal"&&m.agentSessionId&&L(m.sessionId,m.agentType,m.agentSessionId),e[t.sessionId]=m,S(e),m}function $(){return Object.values(d())}function le(t){if(!t)throw new Error("sessionId is required");const e=d();e[t]&&(delete e[t],S(e))}function re(t){u().purgeSession(t)}function Q(){const t=d();let e=!1;const n=new Date().toISOString();for(const[s,i]of Object.entries(t)){const o=w(i.title);if(o===i.title)continue;const a=i.indexRevision+1;t[s]={...i,title:o,indexRevision:a,indexSyncState:"pending",indexSyncStateRevision:a,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,indexQuarantineCode:null,updatedAt:n},e=!0}e&&S(t)}function de(){const t=g();return J(),z(),Q(),$().filter(e=>{const n=e.indexSyncStateRevision===e.indexRevision;return e.indexQuarantineCode||n&&(e.indexSyncState==="quarantined"||e.indexSyncState==="terminal")||n&&e.indexSyncNextAttemptAt&&Date.parse(e.indexSyncNextAttemptAt)>Date.now()?!1:e.indexRevision>e.syncedIndexRevision&&e.localReady&&!!e.firstUserMessageId&&e.firstUserBodyRevision!=null}).sort((e,n)=>e.indexRevision-n.indexRevision).map(e=>{const n=!!(e.lastMessagePreview&&e.lastMessagePreviewMessageId&&e.lastMessagePreviewAt);return{protocolVersion:I,dataEpoch:x,daemonInstallationId:t,session:{id:e.sessionId,agentType:e.agentType,sessionMode:e.sessionMode,nativeSessionVisibility:e.nativeSessionVisibility,managerConfig:e.managerConfig,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null,managerDefaultWorkerAgentType:e.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:e.managerDefaultWorkerModelId??null,title:e.title,titleSource:e.titleSource,titleRevision:e.titleRevision,titleLockedByUser:e.titleLockedByUser,titleOriginEventId:e.titleOriginEventId,status:e.status??"active",workDir:e.workDir,lastMessagePreview:n?e.lastMessagePreview:null,lastMessagePreviewMessageId:n?e.lastMessagePreviewMessageId:null,lastMessagePreviewAt:n?e.lastMessagePreviewAt:null,lastVisibleEventAt:e.lastVisibleEventAt??e.lastMessagePreviewAt??null,bodyStorageMode:"machine_local",localReady:!0,firstUserBoundary:{role:"user",messageId:e.firstUserMessageId,bodyRevision:e.firstUserBodyRevision},bodyRevision:e.bodyRevision,indexRevision:e.indexRevision,firstAvailableSeq:e.firstAvailableSeq,visibleMessageCount:e.visibleMessageCount,lastVisibleMessageId:e.lastVisibleMessageId,attentionCount:e.attentionCount??0,lastAttentionId:e.lastAttentionId??null,lastBodyCommittedAt:e.lastBodyCommittedAt,deletionRevision:e.deletionRevision,lastActivityAt:e.lastActivityAt,createdAt:e.createdAt,updatedAt:e.updatedAt,deletedAt:e.deletedAt}}})}function ce(t,e){const n=d(),s=n[t];if(!(!s||e<=s.syncedIndexRevision)){if(e>s.indexRevision)throw new Error(`cannot acknowledge future index revision ${e} for ${t}`);n[t]={...s,syncedIndexRevision:e,indexSyncState:"applied",indexSyncStateRevision:e,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null},S(n)}}function ue(){const t=d();let e=0;const n=new Date().toISOString();for(const[s,i]of Object.entries(t)){if(i.deletedAt||i.indexQuarantineCode||!i.localReady)continue;const o=i.indexRevision+1;t[s]={...i,indexRevision:o,indexSyncState:"pending",indexSyncStateRevision:o,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,updatedAt:n},e+=1}return e>0&&S(t),e}function ge(t){const e=d(),n=e[t.sessionId];if(!n||n.indexRevision!==t.indexRevision)return"terminal";const i=(n.indexSyncStateRevision===t.indexRevision?n.indexSyncAttempts??0:0)+1;let o=t.disposition;o==="retry"&&i>=(t.maxAttempts??8)&&(o="terminal");const a=t.now??Date.now(),r=Math.min(5e3*2**Math.max(0,i-1),360*60*1e3),l=Math.round(r*.2*(Math.random()*2-1));return e[t.sessionId]={...n,indexSyncState:o==="retry"?"pending":o,indexSyncStateRevision:t.indexRevision,indexSyncAttempts:i,indexSyncNextAttemptAt:o==="retry"?new Date(a+r+l).toISOString():null,indexSyncErrorCode:t.code,indexQuarantineCode:o==="quarantined"?t.code:n.indexQuarantineCode??null},S(e),o}const Y=2*6e4;function Se(t=Date.now(),e=Y){const n=d(),s=[],i=new Date(t).toISOString();for(const o of Object.values(n)){if(o.titleSource!=="first_user_fallback"||o.titleLockedByUser||o.titleGenerationAttemptedAt||!o.title.trim()||o.syncedIndexRevision<o.indexRevision||o.titleGenerationNextAttemptAt&&Date.parse(o.titleGenerationNextAttemptAt)>t)continue;const a=o.status==="completed"||o.status==="failed",r=o.visibleMessageCount>=2;let l=null;if(o.agentType==="claude"&&r?l="agent_native_unavailable":a?l="terminal":r&&t-Date.parse(o.createdAt)>=e&&(l="grace_elapsed"),!l)continue;const f=h(o.title,240);if(!f)continue;const v=D.randomUUID();n[o.sessionId]={...o,titleGenerationAttemptedAt:i,titleGenerationNextAttemptAt:null,titleGenerationAttempts:(o.titleGenerationAttempts??0)+1},s.push({sessionId:o.sessionId,requestId:v,titleSeed:f,baseTitleRevision:o.titleRevision,eligibility:l})}return s.length>0&&S(n),s}function fe(t,e=Date.now()){const n=d(),s=n[t];if(!s?.titleGenerationAttemptedAt||s.titleSource!=="first_user_fallback")return null;const i=Math.max(1,s.titleGenerationAttempts??1),o=Math.min(10*6e4,3e4*2**Math.min(i-1,5)),a=new Date(e+o).toISOString();return n[t]={...s,titleGenerationAttemptedAt:null,titleGenerationNextAttemptAt:a},S(n),a}function ve(t,e){const s=d()[t];return!s||s.titleLockedByUser||y[s.titleSource]>=y.shennian_generated||e.titleRevision<=s.titleRevision?!1:(W({sessionId:t,agentType:s.agentType,sessionMode:s.sessionMode,managerConfig:s.managerConfig,workDir:s.workDir,agentSessionId:s.agentSessionId,modelId:s.modelId,status:s.status,title:e.title,titleSource:"shennian_generated",titleRevision:e.titleRevision,titleLockedByUser:!1,titleOriginEventId:e.titleOriginEventId}),!0)}function me(t,e,n){A(t),g();const s=u().append(t,e,n);return N(t,e,s.bodyRevision),s.bodyRevision}function ye(t,e,n){A(t),g();const s=u().upsert(t,e,n);return N(t,e,s.bodyRevision),s.bodyRevision}function Ae(t,e){const n=u().getMessageState(t,e);return!n||n.tombstoned||!n.message||!n.deliveryState?{protocolVersion:I,dataEpoch:x,daemonInstallationId:g(),state:"not_found",sessionId:t,clientMessageId:e}:{protocolVersion:I,dataEpoch:x,daemonInstallationId:g(),state:n.deliveryState,sessionId:t,clientMessageId:e,messageId:n.message.id,deliveryState:n.deliveryState,bodyRevision:n.lastRevision,committedAt:new Date(n.message.ts).toISOString()}}function xe(t,e){A(t),g();const n=u().tombstone(t,e),s=d(),i=s[t];if(i){const o=e===i.lastMessagePreviewMessageId?V(t):{preview:i.lastMessagePreview??null,messageId:i.lastMessagePreviewMessageId??null,at:i.lastMessagePreviewAt??null};s[t]={...i,bodyRevision:n.bodyRevision,indexRevision:i.indexRevision+1,indexSyncState:"pending",indexSyncStateRevision:i.indexRevision+1,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,updatedAt:new Date().toISOString(),lastMessagePreview:o.preview,lastMessagePreviewMessageId:o.messageId,lastMessagePreviewAt:o.at,lastVisibleEventAt:B(t)},S(s)}return n.bodyRevision}function N(t,e,n){const s=d(),i=s[t];if(!i)return;const o=b(e),a=o?{lastMessagePreview:o,lastMessagePreviewMessageId:e.id,lastMessagePreviewAt:new Date(e.ts).toISOString()}:null,r=o||P(e.payload)?new Date(e.ts).toISOString():i.lastVisibleEventAt,l=u().getManifest(t),f=e.role==="user"&&o&&!i.title?{title:w(o),titleSource:"first_user_fallback",titleRevision:i.titleRevision+1,titleLockedByUser:!1,titleOriginEventId:e.id}:null,v=e.role==="user"&&!i.localReady?{localReady:!0,firstUserMessageId:e.id,firstUserBodyRevision:n}:null;s[t]={...i,...f??{},...v??{},bodyRevision:n,indexRevision:i.indexRevision+1,indexSyncState:"pending",indexSyncStateRevision:i.indexRevision+1,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,updatedAt:new Date().toISOString(),lastActivityAt:new Date(e.ts).toISOString(),lastVisibleEventAt:r,...a??{},firstAvailableSeq:l?.firstAvailableSeq??i.firstAvailableSeq,visibleMessageCount:l?.visibleMessageCount??i.visibleMessageCount,lastVisibleMessageId:l?.lastVisibleMessageId??i.lastVisibleMessageId,attentionCount:i.attentionCount??0,lastAttentionId:i.lastAttentionId??null,lastBodyCommittedAt:l?.lastBodyCommittedAt??i.lastBodyCommittedAt},S(s)}function V(t){let e;do{const n=u().readPage(t,{limit:500,beforeSequence:e});for(let s=n.entries.length-1;s>=0;s-=1){const i=n.entries[s],o=b(i.message);if(o)return{preview:o,messageId:i.message.id,at:new Date(i.message.ts).toISOString()}}if(!n.hasOlder||n.entries.length===0)break;e=n.entries[0].sequence}while(e!==void 0);return{preview:null,messageId:null,at:null}}function B(t){let e;do{const n=u().readPage(t,{limit:500,beforeSequence:e});for(let s=n.entries.length-1;s>=0;s-=1){const i=n.entries[s].message;if(!(!b(i)&&!P(i.payload)))return new Date(i.ts).toISOString()}if(!n.hasOlder||n.entries.length===0)break;e=n.entries[0].sequence}while(e!==void 0);return null}function z(){const t=d();let e=!1;const n=new Date().toISOString();for(const[s,i]of Object.entries(t)){if(Object.prototype.hasOwnProperty.call(i,"lastVisibleEventAt"))continue;const o=i.indexRevision+1;t[s]={...i,lastVisibleEventAt:B(s)??i.lastMessagePreviewAt??null,indexRevision:o,indexSyncState:"pending",indexSyncStateRevision:o,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,updatedAt:n},e=!0}e&&S(t)}function J(){const t=d();let e=!1;const n=new Date().toISOString();for(const[s,i]of Object.entries(t)){if(Object.prototype.hasOwnProperty.call(i,"lastMessagePreviewMessageId")&&Object.prototype.hasOwnProperty.call(i,"lastMessagePreviewAt"))continue;const o=V(s);t[s]={...i,lastMessagePreview:o.preview,lastMessagePreviewMessageId:o.messageId,lastMessagePreviewAt:o.at,indexRevision:i.indexRevision+1,indexSyncState:"pending",indexSyncStateRevision:i.indexRevision+1,indexSyncAttempts:0,indexSyncNextAttemptAt:null,indexSyncErrorCode:null,updatedAt:n},e=!0}e&&S(t)}function H(t,e){const n={title:t?.title??"",titleSource:t?.titleSource??"first_user_fallback",titleRevision:t?.titleRevision??0,titleLockedByUser:t?.titleLockedByUser??!1,titleOriginEventId:t?.titleOriginEventId??null},s=w(e.title?.trim()??"");if(!s)return n;if(!e.titleSource)throw new Error("titleSource is required with title");if(n.title&&n.titleSource==="first_user_fallback"&&e.titleSource==="first_user_fallback")return n;const i=e.titleSource==="shennian_manual";if(e.titleLockedByUser!==void 0&&e.titleLockedByUser!==i)throw new Error("titleLockedByUser conflicts with titleSource");const o=s===n.title&&e.titleSource===n.titleSource,a=e.titleRevision??n.titleRevision+(o?0:1);if(!Number.isSafeInteger(a)||a<0)throw new Error("invalid titleRevision");const r=y[e.titleSource],l=y[n.titleSource];return r<l||a<n.titleRevision||o&&a===n.titleRevision||a===n.titleRevision&&!o?n:{title:s,titleSource:e.titleSource,titleRevision:a,titleLockedByUser:i,titleOriginEventId:e.titleOriginEventId??n.titleOriginEventId}}function Ie(t,e){const n=e?.limit&&e.limit>0?Math.min(e.limit,500):500;let i=u().readRecent(t,n).messages;return e?.before!==void 0&&(i=i.filter(o=>o.ts<e.before)),[...i].sort((o,a)=>a.ts-o.ts)}function pe(){return u().listSessionIds()}function we(){return u()}function X(t,e){const n=p.dirname(t);c.mkdirSync(n,{recursive:!0,mode:448});const s=`${t}.tmp-${process.pid}-${D.randomBytes(6).toString("hex")}`;let i;try{if(i=c.openSync(s,"wx",384),c.writeFileSync(i,`${JSON.stringify(e,null,2)}
|
|
2
|
+
`,"utf8"),c.fsyncSync(i),c.closeSync(i),i=void 0,c.renameSync(s,t),process.platform!=="win32"){const o=c.openSync(n,"r");try{c.fsyncSync(o)}finally{c.closeSync(o)}}}finally{i!==void 0&&c.closeSync(i);try{c.unlinkSync(s)}catch{}}}export{q as PERSONAL_SYNC_V1_ROOT_NAME,ce as acknowledgeSessionIndexUpdate,me as appendMessage,ve as applyGeneratedSessionTitle,Se as claimSessionTitleGenerationCandidates,fe as deferSessionTitleGeneration,we as getCanonicalSessionStore,g as getDaemonInstallationId,Ae as getMessageDeliveryStatus,de as listPendingSessionIndexUpdates,$ as listSessionRecords,pe as listSessions,ue as markAllSessionIndexesForRepublish,re as purgeCanonicalSession,le as purgeSessionRecord,Ie as readMessages,W as recordSession,ge as recordSessionIndexRejection,xe as tombstoneMessage,ye as upsertMessage};
|
|
@@ -47,6 +47,7 @@ export type ChatQueueService = {
|
|
|
47
47
|
handleEdit(req: import('@shennian/wire').ReqFrame): Promise<void>;
|
|
48
48
|
handleDelete(req: import('@shennian/wire').ReqFrame): Promise<void>;
|
|
49
49
|
handlePromote(req: import('@shennian/wire').ReqFrame): Promise<void>;
|
|
50
|
+
noteExternalClaim(sessionId: string, canonicalMessageId: string): void;
|
|
50
51
|
noteTerminal(sessionId: string): void;
|
|
51
52
|
purgeSession(sessionId: string): void;
|
|
52
53
|
getSnapshot(sessionId: string): import('@shennian/wire').ChatQueueSnapshot;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import s from"node:fs";import c from"node:path";import{execSync as
|
|
1
|
+
import s from"node:fs";import{assertStandaloneUpgrade as F,isDesktopDistribution as T}from"../runtime-distribution.js";import c from"node:path";import{execSync as U,execFile as J}from"node:child_process";import{promisify as O}from"node:util";import{fileURLToPath as L}from"node:url";import{getShennianDir as _,resolveShennianPath as m}from"../config/index.js";import{getUpgradeDistTag as P}from"./channel.js";import{fetchCliDistribution as I}from"./distribution.js";const f=O(J),y=m("upgrade-attempt.json"),G=3,H="https://registry.npmjs.org",B=10*6e4,$=[5*6e4,30*6e4,120*6e4,360*6e4];function g(){try{const r=h();if(r)return JSON.parse(s.readFileSync(r,"utf-8")).version}catch{}return"0.0.0"}function h(r=L(import.meta.url)){let e=c.dirname(r);for(let n=0;n<6;n++){const t=c.join(e,"package.json");if(s.existsSync(t)&&JSON.parse(s.readFileSync(t,"utf-8")).name==="shennian")return t;e=c.dirname(e)}return null}async function Y(){let r;try{const e=await fetch(`https://registry.npmjs.org/shennian/${P()}`,{headers:{Accept:"application/json"},signal:AbortSignal.timeout(1e4)});if(!e.ok)throw new Error(`npm registry returned ${e.status}`);const n=await e.json();if(typeof n?.version!="string"||!/^\d+\.\d+\.\d+$/.test(n.version))throw new Error("npm registry returned an unsupported CLI version (expected numeric major.minor.patch)");return n.version}catch(e){r=e}try{return(await I()).version}catch(e){const n=r instanceof Error?r.message:String(r),t=e instanceof Error?e.message:String(e);throw new Error(`npm registry unavailable: ${n}; Shennian fallback failed: ${t}`)}}function z(r,e){const n=p=>p.replace(/^v/,"").split(".").map(Number),[t,a,o]=n(r),[i,u,l]=n(e);return i>t?"major":i===t&&u>a?"minor":i===t&&u===a&&l>o?"patch":"none"}function K(){return U("npm root -g",{encoding:"utf-8",stdio:"pipe",windowsHide:!0}).trim()}function X(r,e=process.platform){const n=e==="win32"?c.win32:c.posix,t=n.dirname(r),a=n.dirname(t);if(n.basename(a)!=="node_modules")return null;const o=n.dirname(a);return n.basename(o)==="lib"?n.dirname(o):o}function N(){const r=h();return r?X(r):null}function Z(){const r=h();return r?c.join(c.dirname(r),"dist","bin","shennian.js"):c.join(K(),"shennian","dist","bin","shennian.js")}function q(){try{return JSON.parse(s.readFileSync(y,"utf-8"))}catch{return null}}function d(r){s.mkdirSync(_(),{recursive:!0}),s.writeFileSync(y,JSON.stringify(r,null,2))}function Q(){try{s.unlinkSync(y)}catch{}}function j(){return m("upgrade-failures.json")}function W(){return m("upgrade.lock")}function V(r=W(),e={}){const n=e.now??Date.now(),t=e.pid??process.pid,a=e.isProcessAlive??(l=>{try{return process.kill(l,0),!0}catch{return!1}});s.mkdirSync(c.dirname(r),{recursive:!0});const o=()=>s.openSync(r,"wx",384);let i;try{i=o()}catch(l){if(l.code!=="EEXIST")throw l;let p=!1;try{const A=JSON.parse(s.readFileSync(r,"utf-8")),E=Number(A.pid),b=Number(A.createdAt);p=!Number.isInteger(E)||!Number.isFinite(b)||n-b>B||!a(E)}catch{p=!0}if(!p)throw new Error("Another Shennian upgrade is already running");s.rmSync(r,{force:!0}),i=o()}s.writeFileSync(i,JSON.stringify({pid:t,createdAt:n})),s.closeSync(i);let u=!1;return{release(){if(!u){u=!0;try{s.unlinkSync(r)}catch{}}}}}function w(r=j()){try{const e=JSON.parse(s.readFileSync(r,"utf-8"));return!e||typeof e!="object"||!e.versions?{versions:{}}:{versions:e.versions}}catch{return{versions:{}}}}function C(r){s.mkdirSync(_(),{recursive:!0}),s.writeFileSync(j(),JSON.stringify(r,null,2))}function rr(r){return $[Math.min(Math.max(r-1,0),$.length-1)]}function dr(r,e=Date.now(),n=w()){const t=n.versions[r];return!!(t&&t.nextRetryAt>e)}function mr(r,e,n=Date.now()){const t=w(),a=t.versions[r],o=(a?.failureCount??0)+1,i={version:r,failureCount:o,firstFailedAt:a?.firstFailedAt??n,lastFailedAt:n,nextRetryAt:n+rr(o),error:e};return t.versions[r]=i,C(t),i}function k(r){const e=w();e.versions[r]&&(delete e.versions[r],C(e))}async function yr(){if(T())return null;const r=q();return r?Date.now()-r.attemptAt>600*1e3?(Q(),null):(r.attemptCount++,r.attemptCount>=G?(d(r),r):(d(r),null)):null}async function gr(r,e,n={}){let t;try{F(),t=V()}catch(a){return{ok:!1,error:a instanceof Error?a.message:String(a)}}try{return await er(r,e,n)}finally{t.release()}}async function er(r,e,n={}){const t=n.currentVersion??g(),a=g();if(t===r)return k(r),{ok:!1,error:`Already on version ${r}`};e({step:"checking"});try{U("npm --version",{stdio:"pipe",windowsHide:!0})}catch{return{ok:!1,error:"npm is not available in PATH"}}if(a===r){e({step:"verifying",version:r});try{await v(r)}catch(o){return{ok:!1,error:`Smoke test failed: ${o instanceof Error?o.message:String(o)}`}}return d({from:t,to:r,attemptCount:0,attemptAt:Date.now()}),k(r),e({step:"restarting",from:t,to:r}),{ok:!0,from:t,to:r}}e({step:"backing-up"}),e({step:"installing",version:r});try{await M(r)}catch(o){const i=await R(t);return{ok:!1,error:`npm install failed: ${o instanceof Error?o.message:String(o)}${i.ok?"":`; rollback failed: ${i.error}`}`,rolledBack:i.ok}}e({step:"verifying",version:r});try{await v(r)}catch(o){const i=await R(t);return{ok:!1,error:`Smoke test failed: ${o instanceof Error?o.message:String(o)}${i.ok?"":`; rollback failed: ${i.error}`}`,rolledBack:i.ok}}return d({from:t,to:r,attemptCount:0,attemptAt:Date.now()}),k(r),e({step:"restarting",from:t,to:r}),{ok:!0,from:t,to:r}}function D(r){if(!/^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(r))throw new Error(`Invalid npm version: ${r}`)}function S(){return process.platform==="win32"?"npm.cmd":"npm"}function tr(r,e){return{timeout:e,windowsHide:!0,shell:r==="win32"}}function x(r){return tr(process.platform,r)}async function M(r){F(),D(r);const e=N(),n=["install","-g",`shennian@${r}`,...e?["--prefix",e]:[]];try{await f(S(),n,x(12e4))}catch(t){const a=t instanceof Error?t.message:String(t);try{await f(S(),[...n,"--registry",H],{...x(12e4)})}catch(o){const i=o instanceof Error?o.message:String(o);throw new Error(`${a}
|
|
2
2
|
--- npmjs fallback ---
|
|
3
|
-
${i}`)}}}async function v(r){
|
|
3
|
+
${i}`)}}}async function v(r){D(r);const e=Z(),{stdout:n}=await f(process.execPath,[e,"--version"],{timeout:1e4,windowsHide:!0});if(n.trim()!==r)throw new Error(`Version verification failed: expected ${r}, got ${n.trim()||"empty output"}`);const t=N();await f(S(),["ls","-g",`shennian@${r}`,"--omit=dev","--depth=0",...t?["--prefix",t]:[]],{...x(3e4)})}async function nr(r,e={}){await(e.install??M)(r),await(e.verify??v)(r)}async function R(r){try{return await nr(r),{ok:!0}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}async function hr(r){const e=r??g(),n=await Y(),t=z(e,n);return t==="none"?{hasUpdate:!1}:{hasUpdate:!0,current:e,latest:n,changeType:t}}export{V as acquireUpgradeLock,hr as checkForUpdate,Q as clearUpgradeAttempt,k as clearUpgradeFailure,z as compareVersions,Y as fetchLatestVersion,h as findPackageJson,g as getCurrentVersion,rr as getUpgradeRetryDelayMs,yr as handleStartupCrashCheck,M as installShennianVersion,dr as isUpgradeVersionInCooldown,tr as npmExecOptionsForPlatform,gr as performUpgrade,q as readUpgradeAttempt,w as readUpgradeFailures,mr as recordUpgradeFailure,X as resolveNpmGlobalPrefixFromPackageJson,nr as rollbackToExactVersion,v as verifyInstalledVersion,d as writeUpgradeAttempt};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{checkForUpdate as
|
|
1
|
+
import{isDesktopDistribution as a}from"../runtime-distribution.js";import{checkForUpdate as u,isUpgradeVersionInCooldown as c}from"./engine.js";const d={automaticPatchEnabled:!1,rolloutPercentage:0,pausedVersions:[],blockedVersions:[],blockedCurrentVersions:[],killSwitch:!0,releaseLevel:"normal",maxIdleWaitMs:30*6e4,requiredCapabilities:[],externalUpdaterEnabled:!1};function m(r){let i=2166136261;for(let t=0;t<r.length;t++)i^=r.charCodeAt(t),i=Math.imul(i,16777619);return(i>>>0)%100}function h(r,i,t,e){if(r.killSwitch||!r.automaticPatchEnabled||r.blockedVersions.includes(e)||r.pausedVersions.includes(e)||r.pausedVersions.includes(t))return!1;const s=Math.max(0,Math.min(100,Math.floor(r.rolloutPercentage)));return m(i)<s}async function p(r){const i=new URL("/api/releases/cli-policy",r),t=await fetch(i,{headers:{Accept:"application/json"},signal:AbortSignal.timeout(1e4)});if(!t.ok)throw new Error(`release policy returned ${t.status}`);const e=await t.json();return{automaticPatchEnabled:e.automaticPatchEnabled===!0,rolloutPercentage:typeof e.rolloutPercentage=="number"?e.rolloutPercentage:0,pausedVersions:Array.isArray(e.pausedVersions)?e.pausedVersions.filter(s=>typeof s=="string"):[],blockedVersions:Array.isArray(e.blockedVersions)?e.blockedVersions.filter(s=>typeof s=="string"):[],blockedCurrentVersions:Array.isArray(e.blockedCurrentVersions)?e.blockedCurrentVersions.filter(s=>typeof s=="string"):[],killSwitch:e.killSwitch!==!1,releaseLevel:e.releaseLevel==="required"||e.releaseLevel==="blocked"?e.releaseLevel:"normal",maxIdleWaitMs:typeof e.maxIdleWaitMs=="number"&&Number.isFinite(e.maxIdleWaitMs)?Math.max(0,Math.min(1440*6e4,Math.floor(e.maxIdleWaitMs))):30*6e4,...typeof e.minimumSessionSyncProtocolVersion=="number"?{minimumSessionSyncProtocolVersion:e.minimumSessionSyncProtocolVersion}:{},...typeof e.minimumDataEpoch=="number"?{minimumDataEpoch:e.minimumDataEpoch}:{},requiredCapabilities:Array.isArray(e.requiredCapabilities)?e.requiredCapabilities.filter(s=>typeof s=="string"):[],externalUpdaterEnabled:e.externalUpdaterEnabled===!0,...typeof e.minimumUpdaterProtocolVersion=="number"?{minimumUpdaterProtocolVersion:e.minimumUpdaterProtocolVersion}:{}}}class b{deps;policy="none";timer=null;started=!1;upgradeInFlight=!1;constructor(i){this.deps=i}start(){a()||this.started||(this.started=!0,this.arm(this.deps.initialDelayMs??3e4))}stop(){this.started=!1,this.timer&&(this.deps.clearTimeout??clearTimeout)(this.timer),this.timer=null}setPolicy(i){this.policy=i,this.started&&(this.timer||this.arm(0))}getPolicy(){return this.policy}async runOnce(){if(a()||this.upgradeInFlight)return;const i=await(this.deps.checkForUpdate??u)(this.deps.currentVersion);if(!i.hasUpdate||(this.deps.inCooldown??c)(i.latest))return;let t=d;try{t=await(this.deps.fetchReleasePolicy??(()=>p(this.deps.serverUrl)))()}catch{return}if(t.killSwitch||!t.externalUpdaterEnabled||t.blockedVersions.includes(i.latest)||t.pausedVersions.includes(i.latest)||t.pausedVersions.includes(i.current))return;const e=t.blockedCurrentVersions.includes(i.current)?"blocked":t.releaseLevel,s=t.minimumSessionSyncProtocolVersion!==void 0&&(this.deps.sessionSyncProtocolVersion??0)<t.minimumSessionSyncProtocolVersion||t.minimumDataEpoch!==void 0&&(this.deps.dataEpoch??0)<t.minimumDataEpoch||t.requiredCapabilities.some(l=>!(this.deps.advertisedCapabilities??[]).includes(l))||t.minimumUpdaterProtocolVersion!==void 0&&(this.deps.updaterProtocolVersion??0)<t.minimumUpdaterProtocolVersion,n=e==="required"||e==="blocked"||s;if(!n&&(this.policy!=="patch"||i.changeType!=="patch"||!h(t,this.deps.machineId,i.current,i.latest)))return;const o=this.deps.getIdleState();if(!(!n&&!o.idle)){this.upgradeInFlight=!0;try{await this.deps.startUpgrade(i.latest,n)}finally{this.upgradeInFlight=!1}}}nextIntervalMs(){return this.deps.intervalMs??5*6e4+Math.floor((this.deps.random??Math.random)()*10*6e4)}arm(i){if(!this.started||this.timer)return;const t=this.deps.setTimeout??setTimeout;this.timer=t(()=>{this.timer=null,this.runOnce().catch(()=>{}).finally(()=>{this.started&&this.arm(this.nextIntervalMs())})},i),this.timer.unref?.()}}export{b as AutoUpgradeScheduler,m as deterministicRolloutBucket,p as fetchCliReleasePolicy,h as isAutomaticPatchAllowed};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import r from"node:fs";import d from"node:path";import{randomUUID as y}from"node:crypto";import{spawn as
|
|
2
|
-
`,{mode:384,flag:"wx"});const n=r.openSync(a,process.platform==="win32"?"r+":"r");try{r.fsyncSync(n)}finally{r.closeSync(n)}if(r.renameSync(a,e),process.platform!=="win32"){const t=r.openSync(d.dirname(e),"r");try{r.fsyncSync(t)}finally{r.closeSync(t)}}}finally{try{r.unlinkSync(a)}catch{}}}function
|
|
3
|
-
`,{mode:384,flag:"wx"});const c=r.openSync(t,process.platform==="win32"?"r+":"r");try{r.fsyncSync(c)}finally{r.closeSync(c)}if(r.renameSync(t,n),process.platform!=="win32"){const s=r.openSync(a,"r");try{r.fsyncSync(s)}finally{r.closeSync(s)}}return n}finally{try{r.unlinkSync(t)}catch{}}}function
|
|
1
|
+
import r from"node:fs";import{assertStandaloneUpgrade as I}from"../runtime-distribution.js";import d from"node:path";import{randomUUID as y}from"node:crypto";import{spawn as j,spawnSync as P}from"node:child_process";import{resolveShennianPath as p}from"../config/index.js";import{findPackageJson as _,resolveNpmGlobalPrefixFromPackageJson as E}from"./engine.js";import{ensureUpdaterInstalled as J}from"./updater-bootstrap.js";import{UPDATER_PROTOCOL_VERSION as S}from"./updater-protocol.js";function w(e,o){r.mkdirSync(d.dirname(e),{recursive:!0,mode:448});const a=`${e}.tmp-${process.pid}-${y()}`;try{r.writeFileSync(a,`${JSON.stringify(o,null,2)}
|
|
2
|
+
`,{mode:384,flag:"wx"});const n=r.openSync(a,process.platform==="win32"?"r+":"r");try{r.fsyncSync(n)}finally{r.closeSync(n)}if(r.renameSync(a,e),process.platform!=="win32"){const t=r.openSync(d.dirname(e),"r");try{r.fsyncSync(t)}finally{r.closeSync(t)}}}finally{try{r.unlinkSync(a)}catch{}}}function $(e){I();const o=e.root??p("runtime","updater"),a=_(),n=e.npmPrefix??(a?E(a):null);if(!n)throw new Error("Cannot determine the active npm prefix");const t=e.jobId??y();return{protocolVersion:S,jobId:t,fromVersion:e.fromVersion,targetVersion:e.targetVersion,npmPrefix:n,npmExecutable:e.npmExecutable??(process.platform==="win32"?"npm.cmd":"npm"),nodeExecutable:e.nodeExecutable??process.execPath,packageName:"shennian",operation:e.operation??"upgrade",oldDaemonPid:e.oldDaemonPid,platform:process.platform,createdAt:(e.now??new Date).toISOString(),daemonExitTimeoutMs:15e3,commandTimeoutMs:12e4,resultPath:d.join(o,"results",`${t}.json`),attemptPath:p("upgrade-attempt.json"),...e.requireHandoffAck?{requireHandoffAck:!0}:{},...e.rollbackJobId?{rollbackJobId:e.rollbackJobId}:{},...e.isolatedDaemonStart?{isolatedDaemonStart:!0}:{}}}function O(e,o=p("runtime","updater")){const a=d.join(o,"jobs");r.mkdirSync(a,{recursive:!0,mode:448});const n=d.join(a,`${e.jobId}.json`),t=`${n}.tmp-${process.pid}-${y()}`;try{r.writeFileSync(t,`${JSON.stringify(e,null,2)}
|
|
3
|
+
`,{mode:384,flag:"wx"});const c=r.openSync(t,process.platform==="win32"?"r+":"r");try{r.fsyncSync(c)}finally{r.closeSync(c)}if(r.renameSync(t,n),process.platform!=="win32"){const s=r.openSync(a,"r");try{r.fsyncSync(s)}finally{r.closeSync(s)}}return n}finally{try{r.unlinkSync(t)}catch{}}}function F(e,o){return e==="linux"&&/(?:^|\/)shennian\.service(?:\n|$)/m.test(o.trim())}function R(e,o={}){const a=o.root??e.root??p("runtime","updater"),n=o.readiness??J({root:a}),t=$({...e,root:a}),c=O(t,a),s=d.join(a,"updater.lock");r.mkdirSync(a,{recursive:!0,mode:448});let l=null,f=!1;const h=()=>{if(f){try{JSON.parse(r.readFileSync(s,"utf8")).jobId===t.jobId&&r.unlinkSync(s)}catch{}f=!1}};try{try{l=r.openSync(s,"wx",384),f=!0}catch(i){if(i.code!=="EEXIST")throw i;let b=!1;try{const g=JSON.parse(r.readFileSync(s,"utf8")),k=o.isAlive??(x=>{try{return process.kill(x,0),!0}catch{return!1}});b=!!(g.pid&&k(g.pid))}catch{}if(b)throw new Error("upgrade_locked");r.rmSync(s,{force:!0}),l=r.openSync(s,"wx",384),f=!0}r.writeFileSync(l,JSON.stringify({jobId:t.jobId,phase:"handoff",pid:process.pid,createdAt:(o.now??Date.now)()})),r.closeSync(l),l=null;let u=o.serviceCgroup??"";if(process.platform==="linux"&&!o.spawn&&o.serviceCgroup===void 0)try{u=r.readFileSync("/proc/self/cgroup","utf8")}catch{}if(F(o.platform??process.platform,u)){const i=(o.systemdRun??P)("systemd-run",["--user","--quiet","--collect",`--unit=shennian-updater-${t.jobId}`,"--property=Type=exec","--property=KillMode=process",`--setenv=PATH=${process.env.PATH??"/usr/local/bin:/usr/bin:/bin"}`,"--",t.nodeExecutable,n.workerPath,c],{encoding:"utf8",stdio:"pipe",timeout:1e4});if(i.error||i.status!==0)throw new Error("updater_service_launch_failed");return{job:t,process:null}}const m=(o.spawn??j)(t.nodeExecutable,[n.workerPath,c],{detached:!0,stdio:"ignore",windowsHide:!0});return m.once?.("error",i=>{w(t.resultPath,{protocolVersion:S,jobId:t.jobId,state:"failed",fromVersion:t.fromVersion,targetVersion:t.targetVersion,errorCode:"internal_error",errorSummary:i instanceof Error?i.message.slice(0,500):"updater_spawn_failed",updatedAt:new Date().toISOString()}),h()}),m.unref(),{job:t,process:m}}catch(u){l!==null&&r.closeSync(l),h();try{r.unlinkSync(c)}catch{}throw u}}async function L(e,o=8e3){const a=Date.now()+o;for(;Date.now()<a;){let n=null;try{n=JSON.parse(r.readFileSync(e.resultPath,"utf8"))}catch{}if(n?.jobId===e.jobId&&n.state==="waiting_for_daemon")return;if(n?.jobId===e.jobId&&n.state==="failed")throw new Error(n.errorCode??"updater_not_ready");await new Promise(t=>setTimeout(t,50))}throw new Error("updater_readiness_timeout")}function q(e){w(`${e.resultPath}.handoff`,{jobId:e.jobId})}function M(e,o,a=p("runtime","updater")){if(!/^[A-Za-z0-9_-]{1,128}$/.test(e))return!1;const n=d.join(a,"results",`${e}.json`);try{const t=r.lstatSync(n);if(t.isSymbolicLink()||!t.isFile())return!1;const c=JSON.parse(r.readFileSync(n,"utf8"));return c.protocolVersion!==S||c.jobId!==e||c.state!=="starting"||c.targetVersion!==o?!1:(w(n,{...c,state:c.operation==="rollback"?"rolled_back":"healthy",effectiveVersion:o,updatedAt:new Date().toISOString()}),!0)}catch{return!1}}export{q as confirmUpdaterHandoff,$ as createUpdaterJob,R as launchUpdater,M as markUpdaterHealthy,F as needsSystemdUpdater,L as waitForUpdaterReady,O as writeUpdaterJob};
|
|
@@ -38,6 +38,8 @@ export type RoomSummary = {
|
|
|
38
38
|
lastReadSequence?: RoomSequence;
|
|
39
39
|
unreadCount?: number;
|
|
40
40
|
membershipRole?: RoomMembershipRole;
|
|
41
|
+
/** The signed-in human's relationship to this Room. Older servers may omit it. */
|
|
42
|
+
membershipStatus?: 'active' | 'left' | 'removed';
|
|
41
43
|
pinnedAt?: string | null;
|
|
42
44
|
updatedAt: string;
|
|
43
45
|
/** Optional list projection used by local-first clients to avoid per-room requests. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Envelope } from './envelope.ts';
|
|
2
|
-
import type { AgentType, ManagerConfig, SessionMode } from './session.ts';
|
|
3
2
|
import type { ProjectFsOpaqueCapability } from './project-fs.ts';
|
|
3
|
+
import type { AgentType, ManagerConfig, SessionMode } from './session.ts';
|
|
4
4
|
export declare const SESSION_SYNC_PROTOCOL_VERSION: 2;
|
|
5
5
|
export type SessionSyncProtocolVersion = typeof SESSION_SYNC_PROTOCOL_VERSION;
|
|
6
6
|
/**
|
|
@@ -320,6 +320,7 @@ export type SessionBodyReplicaErrorEvent = {
|
|
|
320
320
|
* accepted from a daemon payload.
|
|
321
321
|
*/
|
|
322
322
|
export type DaemonSessionIndexProjection = {
|
|
323
|
+
nativeSessionVisibility?: 'user' | 'internal';
|
|
323
324
|
id: string;
|
|
324
325
|
agentType: AgentType;
|
|
325
326
|
sessionMode?: SessionMode;
|