shennian 0.3.56 → 0.3.58
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.
|
@@ -576,8 +576,8 @@
|
|
|
576
576
|
},
|
|
577
577
|
{
|
|
578
578
|
"file": "src/session/history-subscriptions.js",
|
|
579
|
-
"beforeBytes":
|
|
580
|
-
"afterBytes":
|
|
579
|
+
"beforeBytes": 8069,
|
|
580
|
+
"afterBytes": 4010
|
|
581
581
|
},
|
|
582
582
|
{
|
|
583
583
|
"file": "src/session/index-publisher.js",
|
|
@@ -586,8 +586,8 @@
|
|
|
586
586
|
},
|
|
587
587
|
{
|
|
588
588
|
"file": "src/session/manager.js",
|
|
589
|
-
"beforeBytes":
|
|
590
|
-
"afterBytes":
|
|
589
|
+
"beforeBytes": 24989,
|
|
590
|
+
"afterBytes": 13016
|
|
591
591
|
},
|
|
592
592
|
{
|
|
593
593
|
"file": "src/session/projection.js",
|
|
@@ -4,9 +4,10 @@ import type { CanonicalSessionStore } from './canonical-store.js';
|
|
|
4
4
|
export declare class PersonalHistorySubscriptions {
|
|
5
5
|
private client;
|
|
6
6
|
private store;
|
|
7
|
+
private hasLocalSession;
|
|
7
8
|
private subscriptions;
|
|
8
9
|
private unsubscribeStore;
|
|
9
|
-
constructor(client: Pick<CliRelayClient, 'sendRes' | 'sendEvent'>, store?: CanonicalSessionStore);
|
|
10
|
+
constructor(client: Pick<CliRelayClient, 'sendRes' | 'sendEvent'>, store?: CanonicalSessionStore, hasLocalSession?: (sessionId: string) => boolean);
|
|
10
11
|
handleOpen(req: ReqFrame): void;
|
|
11
12
|
handlePage(req: ReqFrame): void;
|
|
12
13
|
handleClose(req: ReqFrame): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SESSION_HISTORY_LIMITS as c}from"@shennian/wire";import{getCanonicalSessionStore as
|
|
1
|
+
import{SESSION_HISTORY_LIMITS as c}from"@shennian/wire";import{getCanonicalSessionStore as p,listSessionRecords as h}from"./store.js";import{recordPersonalSyncLocalMetric as n,setPersonalSyncLocalSubscriptions as a}from"../personal-sync/metrics.js";class I{client;store;hasLocalSession;subscriptions=new Map;unsubscribeStore;constructor(i,s=p(),o=e=>h().some(t=>t.sessionId===e)){this.client=i,this.store=s,this.hasLocalSession=o,this.unsubscribeStore=s.subscribe(e=>this.handleMutation(e))}handleOpen(i){const s=i.params;if(d(s.sessionId,s.subscriptionId),!this.hasLocalSession(s.sessionId)&&!this.store.getManifest(s.sessionId))throw new Error("history_unavailable");if(this.subscriptions.has(s.subscriptionId))throw new Error("subscription already exists");if([...this.subscriptions.values()].filter(t=>t.sessionId===s.sessionId).length>=c.maxSubscriptionsPerSession)throw new Error("subscription_limit_exceeded");const e={sessionId:s.sessionId,subscriptionId:s.subscriptionId,snapshotRevision:0,lastSentRevision:0,active:!1,buffered:[]};this.subscriptions.set(s.subscriptionId,e),a(this.subscriptions.size);try{const t=this.store.readPage(s.sessionId,{limit:u(s.limit,c.defaultSnapshotMessages),...s.beforeSequence===void 0?{}:{beforeSequence:s.beforeSequence}});e.snapshotRevision=t.bodyRevision,e.lastSentRevision=t.bodyRevision,this.client.sendRes({type:"res",id:i.id,ok:!0,payload:{sessionId:s.sessionId,subscriptionId:s.subscriptionId,storageMode:"machine_local",snapshotRevision:t.bodyRevision,firstAvailableSeq:t.firstAvailableSeq,lastAvailableSeq:t.lastAvailableSeq,hasOlder:t.hasOlder,messages:t.entries}}),n("historySnapshots"),n("historyBytesServed",Buffer.byteLength(JSON.stringify(t.entries),"utf8")),e.active=!0;for(const l of e.buffered)this.publishMutation(e,l);e.buffered=[]}catch(t){throw this.subscriptions.delete(s.subscriptionId),a(this.subscriptions.size),t}}handlePage(i){const s=i.params,o=this.requireSubscription(s.sessionId,s.subscriptionId);let e;try{e=this.store.readPageAtRevision(s.sessionId,{beforeSequence:s.beforeSequence,limit:u(s.limit,c.defaultPageMessages),revision:o.snapshotRevision})}catch{throw new Error("snapshot_compacted")}this.client.sendRes({type:"res",id:i.id,ok:!0,payload:{sessionId:s.sessionId,subscriptionId:s.subscriptionId,snapshotRevision:o.snapshotRevision,firstAvailableSeq:e.firstAvailableSeq,hasOlder:e.hasOlder,messages:e.entries}}),n("historyPages"),n("historyBytesServed",Buffer.byteLength(JSON.stringify(e.entries),"utf8"))}handleClose(i){const s=i.params;d(s.sessionId,s.subscriptionId),this.subscriptions.get(s.subscriptionId)?.sessionId===s.sessionId&&(this.subscriptions.delete(s.subscriptionId),a(this.subscriptions.size)),this.client.sendRes({type:"res",id:i.id,ok:!0,payload:{sessionId:s.sessionId,subscriptionId:s.subscriptionId,closed:!0}})}closeAll(){this.subscriptions.clear(),a(0),this.unsubscribeStore()}closeSession(i){for(const[s,o]of this.subscriptions.entries())o.sessionId===i&&this.subscriptions.delete(s);a(this.subscriptions.size)}handleMutation(i){for(const s of this.subscriptions.values())s.sessionId===i.sessionId&&(s.active?this.publishMutation(s,i):s.buffered.push(i))}publishMutation(i,s){s.revision<=i.lastSentRevision||(this.client.sendEvent({type:"event",event:"session.body.delta",payload:{sessionId:i.sessionId,subscriptionId:i.subscriptionId,fromRevision:i.lastSentRevision,toRevision:s.revision,mutations:[{...s,subscriptionId:i.subscriptionId}]}}),n("historyDeltas"),n("historyBytesServed",Buffer.byteLength(JSON.stringify(s),"utf8")),i.lastSentRevision=s.revision)}requireSubscription(i,s){d(i,s);const o=this.subscriptions.get(s);if(!o||o.sessionId!==i)throw new Error("subscription_not_found");return o}}function d(r,i){if(typeof r!="string"||!r||typeof i!="string"||!i)throw new Error("invalid history subscription identity")}function u(r,i){const s=r??i;if(!Number.isSafeInteger(s)||s<1||s>c.maxPageMessages)throw new Error("invalid history limit");return s}export{I as PersonalHistorySubscriptions};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SESSION_SYNC_PROTOCOL_VERSION as d}from"@shennian/wire";import l from"node:crypto";import{getRegisteredAgents as v,unregisterAgent as w}from"../agents/adapter.js";import{loadConfig as b}from"../config/index.js";import{handleUpgradeStart as y,handleUpgradeStatus as R}from"../commands/upgrade.js";import{handleAgentsRefresh as S,handleModelsRefresh as k}from"./handlers/agents.js";import{handleAgentCapabilitiesList as I}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as A,handleAgentConfigGet as T,handleAgentConfigTest as P,handleAgentConfigUpsert as C}from"./handlers/agent-config.js";import{handleChatAbort as h,handleChatSend as u}from"./handlers/chat.js";import{handleSessionRefresh as D}from"./handlers/session-refresh.js";import{handleSessionToolDetail as M}from"./handlers/tool-detail.js";import{handleSessionTitleSet as E}from"./handlers/title.js";import{handleSessionMessageStatus as F}from"./handlers/message-status.js";import{handleVoiceSessionBodyWindow as x}from"./handlers/voice-window.js";import{cleanupPendingTransfers as O,handleFsLs as U,handleFsRead as _,handleFsRename as Q,handleFsWrite as N,handleFsTransferAbort as V,handleFsTransferChunk as z,handleFsTransferFinish as W,handleFsTransferStart as $,handleFsExportMarkdownPdf as B,handleFsExportMarkdownPdfSetup as L,handleFsArchiveZip as j}from"./handlers/fs.js";import{handleRegionProbe as G,handleRegionSwitch as H,handleUpgradeSetPolicy as K}from"./handlers/control.js";import{ManagerRuntimeService as X,setManagerRuntimeService as m}from"../manager/runtime.js";import{ChatQueueManager as Y}from"./queue.js";import{createAuthorizedFsRoot as Z,resolveAuthorizedPath as J,resolveSessionWorkDir as q}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/manager.js";import{registerCustomAgent as ee}from"../agents/custom.js";import{handleManagedRoomBind as se,handleManagedRoomRestart as te}from"./handlers/room-managed.js";import{getDaemonInstallationId as p,listSessionRecords as f,purgeCanonicalSession as ae,purgeSessionRecord as ie}from"./store.js";import{PersonalHistorySubscriptions as ne}from"./history-subscriptions.js";import{sweepAttachmentTransfers as re}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as oe}from"../native-fusion/native-source-deletions.js";import{purgeToolDetails as ce}from"./tool-detail-store.js";import{readDeletionSyncRevision as le,writeDeletionSyncRevision as de}from"./deletion-sync-state.js";const he=50;function ue(c,e){if(!c||!Number.isSafeInteger(c.latestRevision)||c.latestRevision<e||!Array.isArray(c.deletions))throw new Error("invalid deletion sync response");let s=e;for(const t of c.deletions){if(!t?.sessionId||!Number.isSafeInteger(t.deletionRevision)||t.deletionRevision<=s||!Array.isArray(t.sources)||Number.isNaN(Date.parse(t.deletedAt)))throw new Error("invalid session deletion command");for(const a of t.sources)if(!a?.sourceSessionKey||a.agentType!=="codex"&&a.agentType!=="claude"&&a.agentType!=="opencode")throw new Error("invalid native source deletion command");s=t.deletionRevision}}import{resolveSessionWorkDir as Ye}from"../fs/boundary.js";class He{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;historySubscriptions;activityProbeTimer=null;attachmentCleanupTimer=null;activeRequests=0;constructor(e,s=null,t,a=null,i=null){this.client=e,this.nativeFusion=s,this.cliVersion=t,this.upgradePolicyController=a,this.managedRoomBinder=i,this.managerRuntime=new X({getRuntime:()=>this.getRuntime(),dispatchReq:n=>this.handleReq(n)}),this.chatQueue=new Y({getRuntime:()=>this.getRuntime(),dispatchReq:n=>this.handleReq(n)}),this.historySubscriptions=new ne(this.client),m(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents(),this.activityProbeTimer=setInterval(()=>{this.publishManagedActivitySnapshots().catch(n=>{console.error("[session.activity] managed probe failed",n)})},15e3),this.activityProbeTimer.unref?.(),this.attachmentCleanupTimer=setInterval(()=>{const n=new Set([...f().map(r=>r.workDir),...Array.from(this.pendingTransfers.values()).map(r=>r.rootPath)]);for(const r of n)try{re(this.resolvePath(r))}catch(o){console.error("[attachment.cleanup] failed",o instanceof Error?o.message:String(o))}},6e4),this.attachmentCleanupTimer.unref?.()}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,s)=>this.resolveAuthorizedPath(e,s),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,s)=>this.publishSessionActivity(e,s)},upgradePolicyController:this.upgradePolicyController}}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(s=>s.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}}async synchronizeSessionDeletions(){const e=p(),s=le(),t=await this.client.sendReq({type:"req",id:`session-deletions-sync-${l.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:d,daemonInstallationId:e,afterRevision:s}});if(!t.ok)throw new Error(t.error??"session deletion sync failed");const a=t.payload;ue(a,s);let i=s;for(const r of a.deletions)r.deletionRevision<=i||(await this.applySessionDeletion(r),i=r.deletionRevision,de(i));if(i!==a.latestRevision)throw new Error(`deletion revision gap: applied ${i}, latest ${a.latestRevision}`);if(i===0)return;const n=await this.client.sendReq({type:"req",id:`session-deletions-ack-${l.randomUUID()}`,method:"session.deletions.ack",params:{protocolVersion:d,daemonInstallationId:e,deletionRevision:i}});if(!n.ok)throw new Error(n.error??"session deletion acknowledgement failed")}async applySessionDeletion(e){const s=this.sessions.get(e.sessionId);if(s){try{await s.adapter.stop()}catch{}this.sessions.delete(e.sessionId)}const t=new oe({daemonInstallationId:p()});t.recordSession(e);for(const a of e.sources)t.record({...e,...a});this.historySubscriptions.closeSession(e.sessionId),this.nativeFusion?.handleSessionDeleted(e.sessionId),this.chatQueue.purgeSession(e.sessionId),ce(e.sessionId),ae(e.sessionId),ie(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null)}async activateRoomSession(e){const s=f().find(n=>n.sessionId===e.sessionId),t=s?.agentType===e.agentType?s.modelId??void 0:void 0,a={value:null},i=new Proxy(this.client,{get:(n,r)=>{if(r==="sendRes")return g=>{a.value=g};const o=Reflect.get(n,r,n);return typeof o=="function"?o.bind(n):o}});return await u({...this.getRuntime(),client:i},{type:"req",id:e.clientMessageId,method:"chat.send",params:{...e,modelId:t,sessionMode:s?.sessionMode??(s?.agentType==="manager"?"manager":"standard"),managerConfig:s?.managerConfig??null,managerDefaultWorkerAgentType:s?.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:s?.managerDefaultWorkerModelId??null,waitForDispatch:!0,suppressExternalOwnerEcho:!0}},{source:"room_activation"}),{externalOwnerQueued:a.value?.ok===!0&&a.value.payload?.deliveryMode==="external_owner_queue"}}async abortRoomSession(e){const s=new Proxy(this.client,{get:(t,a)=>{if(a==="sendRes")return()=>{};const i=Reflect.get(t,a,t);return typeof i=="function"?i.bind(t):i}});await h({...this.getRuntime(),client:s},{type:"req",id:`room-activation-abort-${l.randomUUID()}`,method:"chat.abort",params:{sessionId:e.sessionId}})}publishSessionActivity(e,s){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:s}})}async publishManagedActivitySnapshots(){for(const[e,s]of this.sessions.entries()){if(s.heartbeatTimer||!s.currentRunId||!s.adapter.getStatus)continue;const t=await s.adapter.getStatus().catch(()=>null);if(!t?.active||!t.runPhase)continue;const a=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:t.runId||s.currentRunId,runPhase:t.runPhase,startedAt:new Date(s.lastActiveAt).toISOString(),updatedAt:a,canStop:t.canStop??!0});const i=s.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:t.runId||s.currentRunId,seq:i,runPhase:t.runPhase,canStop:t.canStop??!0},seq:i,id:`agent-status-${t.runId||s.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const s of v())s.startsWith("custom:")&&w(s);const e=b();for(const[s,t]of Object.entries(e.customAgents??{}))ee(s,t)}async handleReq(e){const s=this.getRuntime();this.activeRequests++;try{switch(e.method){case"chat.send":await u(s,e);break;case"chat.enqueue":await this.chatQueue.handleEnqueue(e);break;case"chat.queue.get":await this.chatQueue.handleGet(e);break;case"chat.queue.edit":await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":await this.chatQueue.handleDelete(e);break;case"chat.abort":await h(s,e);break;case"session.refresh":await D(s,e);break;case"session.history.open":this.historySubscriptions.handleOpen(e);break;case"session.history.page":this.historySubscriptions.handlePage(e);break;case"session.history.close":this.historySubscriptions.handleClose(e);break;case"session.history":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"upgrade_required"});break;case"session.message.status":F(s,e);break;case"session.tool.detail":await M(s,e);break;case"voice.session.body.window":x(s,e);break;case"session.title.set":await E(s,e);break;case"room.agent.bind-managed":await se(s,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await te(s,this.managedRoomBinder,e);break;case"fs.ls":await U(s,e);break;case"fs.read":await _(s,e);break;case"fs.write":await N(s,e);break;case"fs.export.markdown-pdf":await B(s,e);break;case"fs.export.markdown-pdf.setup":await L(s,e);break;case"fs.archive.zip":await j(s,e);break;case"fs.rename":await Q(s,e);break;case"fs.transfer.start":await $(s,e);break;case"fs.transfer.chunk":await z(s,e);break;case"fs.transfer.finish":await W(s,e);break;case"fs.transfer.abort":await V(s,e);break;case"region.probe":await G(s,e);break;case"region.switch":await H(s,e);break;case"upgrade.start":await y(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0});break;case"upgrade.status":await R(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await K(s,e);break;case"agents.refresh":await S(s,e);break;case"models.refresh":await k(s,e);break;case"agent.config.get":await T(s,e);break;case"agent.config.upsert":await C(s,e);break;case"agent.config.clear":await A(s,e);break;case"agent.config.test":await P(s,e);break;case"agent.capabilities.list":await I(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(t){this.client.sendRes({type:"res",id:e.id,ok:!1,error:t instanceof Error?t.message:String(t)})}finally{this.activeRequests--}}evictIdleSessions(){if(this.sessions.size<he)return;let e=null;for(const[s,t]of this.sessions)(!e||t.lastActiveAt<e.session.lastActiveAt)&&(e={key:s,session:t});e&&(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 q(e)}resolveAuthorizedPath(e,s){return J(e,Z(s))}async cleanup(){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)e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),O(this.getRuntime()),await this.managerRuntime.stop(),m(null)}}export{He as SessionManager,Ye as resolveSessionWorkDir};
|
|
1
|
+
import{SESSION_SYNC_PROTOCOL_VERSION as d}from"@shennian/wire";import l from"node:crypto";import{getRegisteredAgents as b,unregisterAgent as w}from"../agents/adapter.js";import{loadConfig as R}from"../config/index.js";import{handleUpgradeStart as y,handleUpgradeStatus as S}from"../commands/upgrade.js";import{handleAgentsRefresh as k,handleModelsRefresh as I}from"./handlers/agents.js";import{handleAgentCapabilitiesList as A}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as T,handleAgentConfigGet as P,handleAgentConfigTest as C,handleAgentConfigUpsert as D}from"./handlers/agent-config.js";import{handleChatAbort as h,handleChatSend as u}from"./handlers/chat.js";import{handleSessionRefresh as M}from"./handlers/session-refresh.js";import{handleSessionToolDetail as E}from"./handlers/tool-detail.js";import{handleSessionTitleSet as F}from"./handlers/title.js";import{handleSessionMessageStatus as x}from"./handlers/message-status.js";import{handleVoiceSessionBodyWindow as O}from"./handlers/voice-window.js";import{cleanupPendingTransfers as U,handleFsLs as _,handleFsRead as N,handleFsRename as Q,handleFsWrite as V,handleFsTransferAbort as z,handleFsTransferChunk as W,handleFsTransferFinish as $,handleFsTransferStart as B,handleFsExportMarkdownPdf as L,handleFsExportMarkdownPdfSetup as j,handleFsArchiveZip as G}from"./handlers/fs.js";import{handleRegionProbe as H,handleRegionSwitch as K,handleUpgradeSetPolicy as X}from"./handlers/control.js";import{ManagerRuntimeService as Y,setManagerRuntimeService as m}from"../manager/runtime.js";import{ChatQueueManager as Z}from"./queue.js";import{createAuthorizedFsRoot as J,resolveAuthorizedPath as q,resolveSessionWorkDir as ee}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/manager.js";import{registerCustomAgent as se}from"../agents/custom.js";import{handleManagedRoomBind as te,handleManagedRoomRestart as ie}from"./handlers/room-managed.js";import{getDaemonInstallationId as p,listSessionRecords as f,purgeCanonicalSession as ae,purgeSessionRecord as ne}from"./store.js";import{PersonalHistorySubscriptions as re}from"./history-subscriptions.js";import{sweepAttachmentTransfers as oe}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as ce}from"../native-fusion/native-source-deletions.js";import{purgeToolDetails as le}from"./tool-detail-store.js";import{readDeletionSyncRevision as de,writeDeletionSyncRevision as g}from"./deletion-sync-state.js";const he=50;function ue(o,e){if(!o||!Number.isSafeInteger(o.baselineRevision)||o.baselineRevision<e||!Number.isSafeInteger(o.latestRevision)||o.latestRevision<o.baselineRevision||!Array.isArray(o.deletions))throw new Error("invalid deletion sync response");let s=o.baselineRevision;for(const t of o.deletions){if(!t?.sessionId||!Number.isSafeInteger(t.deletionRevision)||t.deletionRevision!==s+1||!Array.isArray(t.sources)||Number.isNaN(Date.parse(t.deletedAt)))throw new Error("invalid session deletion command");for(const i of t.sources)if(!i?.sourceSessionKey||i.agentType!=="codex"&&i.agentType!=="claude"&&i.agentType!=="opencode")throw new Error("invalid native source deletion command");s=t.deletionRevision}if(s!==o.latestRevision)throw new Error("invalid deletion sync revision range")}import{resolveSessionWorkDir as Ye}from"../fs/boundary.js";class He{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;historySubscriptions;activityProbeTimer=null;attachmentCleanupTimer=null;activeRequests=0;constructor(e,s=null,t,i=null,a=null){this.client=e,this.nativeFusion=s,this.cliVersion=t,this.upgradePolicyController=i,this.managedRoomBinder=a,this.managerRuntime=new Y({getRuntime:()=>this.getRuntime(),dispatchReq:n=>this.handleReq(n)}),this.chatQueue=new Z({getRuntime:()=>this.getRuntime(),dispatchReq:n=>this.handleReq(n)}),this.historySubscriptions=new re(this.client),m(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents(),this.activityProbeTimer=setInterval(()=>{this.publishManagedActivitySnapshots().catch(n=>{console.error("[session.activity] managed probe failed",n)})},15e3),this.activityProbeTimer.unref?.(),this.attachmentCleanupTimer=setInterval(()=>{const n=new Set([...f().map(r=>r.workDir),...Array.from(this.pendingTransfers.values()).map(r=>r.rootPath)]);for(const r of n)try{oe(this.resolvePath(r))}catch(c){console.error("[attachment.cleanup] failed",c instanceof Error?c.message:String(c))}},6e4),this.attachmentCleanupTimer.unref?.()}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,s)=>this.resolveAuthorizedPath(e,s),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,s)=>this.publishSessionActivity(e,s)},upgradePolicyController:this.upgradePolicyController}}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(s=>s.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}}async synchronizeSessionDeletions(){const e=p(),s=de(),t=await this.client.sendReq({type:"req",id:`session-deletions-sync-${l.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:d,daemonInstallationId:e,afterRevision:s}});if(!t.ok)throw new Error(t.error??"session deletion sync failed");const i=t.payload;ue(i,s);let a=i.baselineRevision;a>s&&g(a);for(const r of i.deletions)r.deletionRevision<=a||(await this.applySessionDeletion(r),a=r.deletionRevision,g(a));if(a!==i.latestRevision)throw new Error(`deletion revision gap: applied ${a}, latest ${i.latestRevision}`);if(a===0)return;const n=await this.client.sendReq({type:"req",id:`session-deletions-ack-${l.randomUUID()}`,method:"session.deletions.ack",params:{protocolVersion:d,daemonInstallationId:e,deletionRevision:a}});if(!n.ok)throw new Error(n.error??"session deletion acknowledgement failed")}async applySessionDeletion(e){const s=this.sessions.get(e.sessionId);if(s){try{await s.adapter.stop()}catch{}this.sessions.delete(e.sessionId)}const t=new ce({daemonInstallationId:p()});t.recordSession(e);for(const i of e.sources)t.record({...e,...i});this.historySubscriptions.closeSession(e.sessionId),this.nativeFusion?.handleSessionDeleted(e.sessionId),this.chatQueue.purgeSession(e.sessionId),le(e.sessionId),ae(e.sessionId),ne(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null)}async activateRoomSession(e){const s=f().find(n=>n.sessionId===e.sessionId),t=s?.agentType===e.agentType?s.modelId??void 0:void 0,i={value:null},a=new Proxy(this.client,{get:(n,r)=>{if(r==="sendRes")return v=>{i.value=v};const c=Reflect.get(n,r,n);return typeof c=="function"?c.bind(n):c}});return await u({...this.getRuntime(),client:a},{type:"req",id:e.clientMessageId,method:"chat.send",params:{...e,modelId:t,sessionMode:s?.sessionMode??(s?.agentType==="manager"?"manager":"standard"),managerConfig:s?.managerConfig??null,managerDefaultWorkerAgentType:s?.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:s?.managerDefaultWorkerModelId??null,waitForDispatch:!0,suppressExternalOwnerEcho:!0}},{source:"room_activation"}),{externalOwnerQueued:i.value?.ok===!0&&i.value.payload?.deliveryMode==="external_owner_queue"}}async abortRoomSession(e){const s=new Proxy(this.client,{get:(t,i)=>{if(i==="sendRes")return()=>{};const a=Reflect.get(t,i,t);return typeof a=="function"?a.bind(t):a}});await h({...this.getRuntime(),client:s},{type:"req",id:`room-activation-abort-${l.randomUUID()}`,method:"chat.abort",params:{sessionId:e.sessionId}})}publishSessionActivity(e,s){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:s}})}async publishManagedActivitySnapshots(){for(const[e,s]of this.sessions.entries()){if(s.heartbeatTimer||!s.currentRunId||!s.adapter.getStatus)continue;const t=await s.adapter.getStatus().catch(()=>null);if(!t?.active||!t.runPhase)continue;const i=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:t.runId||s.currentRunId,runPhase:t.runPhase,startedAt:new Date(s.lastActiveAt).toISOString(),updatedAt:i,canStop:t.canStop??!0});const a=s.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:t.runId||s.currentRunId,seq:a,runPhase:t.runPhase,canStop:t.canStop??!0},seq:a,id:`agent-status-${t.runId||s.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const s of b())s.startsWith("custom:")&&w(s);const e=R();for(const[s,t]of Object.entries(e.customAgents??{}))se(s,t)}async handleReq(e){const s=this.getRuntime();this.activeRequests++;try{switch(e.method){case"chat.send":await u(s,e);break;case"chat.enqueue":await this.chatQueue.handleEnqueue(e);break;case"chat.queue.get":await this.chatQueue.handleGet(e);break;case"chat.queue.edit":await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":await this.chatQueue.handleDelete(e);break;case"chat.abort":await h(s,e);break;case"session.refresh":await M(s,e);break;case"session.history.open":this.historySubscriptions.handleOpen(e);break;case"session.history.page":this.historySubscriptions.handlePage(e);break;case"session.history.close":this.historySubscriptions.handleClose(e);break;case"session.history":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"upgrade_required"});break;case"session.message.status":x(s,e);break;case"session.tool.detail":await E(s,e);break;case"voice.session.body.window":O(s,e);break;case"session.title.set":await F(s,e);break;case"room.agent.bind-managed":await te(s,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await ie(s,this.managedRoomBinder,e);break;case"fs.ls":await _(s,e);break;case"fs.read":await N(s,e);break;case"fs.write":await V(s,e);break;case"fs.export.markdown-pdf":await L(s,e);break;case"fs.export.markdown-pdf.setup":await j(s,e);break;case"fs.archive.zip":await G(s,e);break;case"fs.rename":await Q(s,e);break;case"fs.transfer.start":await B(s,e);break;case"fs.transfer.chunk":await W(s,e);break;case"fs.transfer.finish":await $(s,e);break;case"fs.transfer.abort":await z(s,e);break;case"region.probe":await H(s,e);break;case"region.switch":await K(s,e);break;case"upgrade.start":await y(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0});break;case"upgrade.status":await S(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await X(s,e);break;case"agents.refresh":await k(s,e);break;case"models.refresh":await I(s,e);break;case"agent.config.get":await P(s,e);break;case"agent.config.upsert":await D(s,e);break;case"agent.config.clear":await T(s,e);break;case"agent.config.test":await C(s,e);break;case"agent.capabilities.list":await A(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(t){this.client.sendRes({type:"res",id:e.id,ok:!1,error:t instanceof Error?t.message:String(t)})}finally{this.activeRequests--}}evictIdleSessions(){if(this.sessions.size<he)return;let e=null;for(const[s,t]of this.sessions)(!e||t.lastActiveAt<e.session.lastActiveAt)&&(e={key:s,session:t});e&&(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 ee(e)}resolveAuthorizedPath(e,s){return q(e,J(s))}async cleanup(){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)e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),U(this.getRuntime()),await this.managerRuntime.stop(),m(null)}}export{He as SessionManager,Ye as resolveSessionWorkDir};
|
|
@@ -248,6 +248,8 @@ export type SessionDeletionsSyncParams = {
|
|
|
248
248
|
afterRevision: number;
|
|
249
249
|
};
|
|
250
250
|
export type SessionDeletionsSyncResponse = {
|
|
251
|
+
/** Highest pruned revision the daemon may durably baseline before applying returned commands. */
|
|
252
|
+
baselineRevision: number;
|
|
251
253
|
latestRevision: number;
|
|
252
254
|
deletions: SessionDeletionCommand[];
|
|
253
255
|
};
|