shennian 0.3.73 → 0.3.74
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 +122 -57
- package/dist/src/agent-env.d.ts +30 -0
- package/dist/src/agent-env.js +4 -4
- package/dist/src/agents/adapter.d.ts +5 -0
- package/dist/src/agents/claude-managed-permissions.d.ts +33 -0
- package/dist/src/agents/claude-managed-permissions.js +1 -0
- package/dist/src/agents/claude.d.ts +11 -2
- package/dist/src/agents/claude.js +5 -5
- package/dist/src/agents/codex-managed-permissions.d.ts +32 -0
- package/dist/src/agents/codex-managed-permissions.js +1 -0
- package/dist/src/agents/codex-managed-room-tools.d.ts +40 -0
- package/dist/src/agents/codex-managed-room-tools.js +1 -0
- package/dist/src/agents/codex.d.ts +20 -2
- package/dist/src/agents/codex.js +11 -10
- package/dist/src/agents/command-spec.d.ts +8 -0
- package/dist/src/agents/command-spec.js +3 -2
- package/dist/src/agents/cursor-managed-permissions.d.ts +28 -0
- package/dist/src/agents/cursor-managed-permissions.js +2 -0
- package/dist/src/agents/cursor.d.ts +8 -2
- package/dist/src/agents/cursor.js +1 -1
- package/dist/src/agents/custom-managed-runtime.d.ts +37 -0
- package/dist/src/agents/custom-managed-runtime.js +2 -0
- package/dist/src/agents/custom.d.ts +12 -2
- package/dist/src/agents/custom.js +4 -4
- package/dist/src/agents/detect.d.ts +3 -0
- package/dist/src/agents/detect.js +1 -1
- package/dist/src/agents/model-registry/service.js +1 -1
- package/dist/src/agents/opencode-managed-permissions.d.ts +25 -0
- package/dist/src/agents/opencode-managed-permissions.js +2 -0
- package/dist/src/agents/opencode.d.ts +8 -2
- package/dist/src/agents/opencode.js +1 -1
- package/dist/src/agents/workbuddy-managed-permissions.d.ts +37 -0
- package/dist/src/agents/workbuddy-managed-permissions.js +2 -0
- package/dist/src/agents/workbuddy.d.ts +8 -0
- package/dist/src/agents/workbuddy.js +2 -2
- package/dist/src/commands/daemon.d.ts +29 -0
- package/dist/src/commands/daemon.js +14 -14
- package/dist/src/commands/room.js +3 -3
- package/dist/src/commands/runtime.js +5 -5
- package/dist/src/config/index.d.ts +22 -0
- package/dist/src/daemon-ipc/paths.d.ts +8 -0
- package/dist/src/daemon-ipc/paths.js +1 -1
- package/dist/src/index.js +3 -3
- package/dist/src/log-redaction.d.ts +4 -0
- package/dist/src/log-redaction.js +1 -0
- package/dist/src/relay/client.d.ts +1 -0
- package/dist/src/relay/client.js +1 -1
- package/dist/src/room/activation-service.d.ts +23 -1
- package/dist/src/room/activation-service.js +7 -7
- package/dist/src/room/attachment-cache.d.ts +3 -0
- package/dist/src/room/attachment-cache.js +2 -2
- package/dist/src/room/attachment-upload.js +1 -1
- package/dist/src/room/managed-prompt-composer.d.ts +16 -3
- package/dist/src/room/managed-prompt-composer.js +26 -13
- package/dist/src/room/managed-room-binding.d.ts +3 -3
- package/dist/src/room/managed-room-binding.js +1 -1
- package/dist/src/room/managed-room-ipc.d.ts +32 -0
- package/dist/src/room/managed-room-ipc.js +3 -1
- package/dist/src/room/room-action-client.d.ts +5 -1
- package/dist/src/room/room-action-client.js +1 -1
- package/dist/src/security/managed-agent-credential-paths.d.ts +6 -0
- package/dist/src/security/managed-agent-credential-paths.js +1 -0
- package/dist/src/security/managed-agent-executable.d.ts +19 -0
- package/dist/src/security/managed-agent-executable.js +1 -0
- package/dist/src/security/managed-agent-policy.d.ts +55 -0
- package/dist/src/security/managed-agent-policy.js +1 -0
- package/dist/src/security/managed-agent-runtime.d.ts +44 -0
- package/dist/src/security/managed-agent-runtime.js +1 -0
- package/dist/src/session/handlers/chat.d.ts +8 -1
- package/dist/src/session/handlers/chat.js +2 -2
- package/dist/src/session/handlers/remote-access.d.ts +4 -0
- package/dist/src/session/handlers/remote-access.js +1 -0
- package/dist/src/session/handlers/room-managed.d.ts +1 -1
- package/dist/src/session/handlers/room-managed.js +1 -1
- package/dist/src/session/history-subscriptions.d.ts +6 -0
- package/dist/src/session/history-subscriptions.js +1 -1
- package/dist/src/session/manager.d.ts +7 -2
- package/dist/src/session/manager.js +1 -1
- package/dist/src/session/queue.d.ts +4 -0
- package/dist/src/session/queue.js +4 -4
- package/dist/src/session/types.d.ts +14 -0
- package/node_modules/@shennian/wire/dist/agent-workspace.d.ts +70 -0
- package/node_modules/@shennian/wire/dist/agent-workspace.js +111 -1
- package/node_modules/@shennian/wire/dist/machine.d.ts +2 -0
- package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
- package/node_modules/@shennian/wire/dist/session-sync.d.ts +53 -3
- package/node_modules/@shennian/wire/dist/session-sync.js +5 -1
- package/node_modules/@shennian/wire/dist/session.d.ts +2 -0
- package/package.json +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
import{isAvailableAgentType as S}from"@shennian/wire";const w=/^[A-Za-z0-9_.:@/-]{1,191}$/,f=new Set(["roomId","agentIdentityId","nianSessionId","assignmentIntentId","agentType"]),R=new Set(["roomId","bindingId","agentIdentityId","previousNianSessionId","nianSessionId","assignmentIntentId","agentType"]);async function T(e,t,n){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(n.params).some(m=>!f.has(m)))throw new Error("Managed Room binding contains unsupported parameters.");const i=a(n.params.roomId,"roomId"),d=a(n.params.agentIdentityId,"agentIdentityId"),s=a(n.params.nianSessionId,"nianSessionId"),r=a(n.params.assignmentIntentId,"assignmentIntentId"),o=g(n.params.agentType);await c(e,s);const I=await t.bind({roomId:i,agentIdentityId:d,nianSessionId:s,assignmentIntentId:r,sourceSessionKey:p(s),agentType:o});e.client.sendRes({type:"res",id:n.id,ok:!0,payload:I})}async function b(e,t,n){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(n.params).some(u=>!R.has(u)))throw new Error("Managed Room restart contains unsupported parameters.");const i=a(n.params.roomId,"roomId"),d=a(n.params.bindingId,"bindingId"),s=a(n.params.agentIdentityId,"agentIdentityId"),r=a(n.params.previousNianSessionId,"previousNianSessionId"),o=a(n.params.nianSessionId,"nianSessionId"),I=a(n.params.assignmentIntentId,"assignmentIntentId"),m=g(n.params.agentType);await c(e,o);const y=await t.restart({roomId:i,bindingId:d,agentIdentityId:s,previousNianSessionId:r,nianSessionId:o,assignmentIntentId:I,sourceSessionKey:p(o),agentType:m});e.client.sendRes({type:"res",id:n.id,ok:!0,payload:y})}function a(e,t){if(typeof e!="string"||!w.test(e))throw new Error(`Invalid ${t}.`);return e}function g(e){if(typeof e!="string"||!S(e)||e==="manager"||e==="pi"||e==="openclaw")throw new Error("managed_agent_runtime_unsupported");return e}function p(e){return`managed:${e}`}async function c(e,t){const n=e.sessions.get(t);n&&(e.sessions.delete(t),n.managedRoomContextToken&&(e.revokeManagedRoomContextToken?.(n.managedRoomContextToken),n.managedRoomContextToken=null),n.heartbeatTimer&&clearInterval(n.heartbeatTimer),n.adapter.removeAllListeners(),await n.adapter.stop())}export{T as handleManagedRoomBind,b as handleManagedRoomRestart};
|
|
@@ -8,16 +8,22 @@ export declare class PersonalHistorySubscriptions {
|
|
|
8
8
|
private store;
|
|
9
9
|
private hasLocalSession;
|
|
10
10
|
private subscriptions;
|
|
11
|
+
private replicaStreams;
|
|
11
12
|
private unsubscribeStore;
|
|
12
13
|
constructor(client: Pick<CliRelayClient, 'sendRes' | 'sendEvent'>, store?: CanonicalSessionStore, hasLocalSession?: (sessionId: string) => boolean);
|
|
13
14
|
handleOpen(req: ReqFrame): void;
|
|
14
15
|
handleSync(req: ReqFrame): void;
|
|
16
|
+
handleReplicaSubscribe(req: ReqFrame): void;
|
|
17
|
+
handleReplicaUnsubscribe(req: ReqFrame): void;
|
|
15
18
|
handlePage(req: ReqFrame): void;
|
|
16
19
|
handleClose(req: ReqFrame): void;
|
|
17
20
|
closeAll(): void;
|
|
18
21
|
closeSession(sessionId: string): void;
|
|
19
22
|
private handleMutation;
|
|
23
|
+
private queueReplicaMutation;
|
|
24
|
+
private flushReplicaStream;
|
|
20
25
|
private queueMutation;
|
|
21
26
|
private flushSubscription;
|
|
22
27
|
private requireSubscription;
|
|
28
|
+
private updateSubscriptionMetric;
|
|
23
29
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SESSION_HISTORY_LIMITS as o}from"@shennian/wire";import{getCanonicalSessionStore as v,listSessionRecords as g}from"./store.js";import{recordPersonalSyncLocalMetric as r,setPersonalSyncLocalSubscriptions as l}from"../personal-sync/metrics.js";import{assertPersonalSyncIdentity as c,getPersonalSyncIdentity as u}from"../personal-sync/epoch.js";const R=100,_=o.maxDeltaMutations,A=o.maxMutationBytes-4096,y=o.maxReplicaPageBytes-16384;class L{client;store;hasLocalSession;subscriptions=new Map;unsubscribeStore;constructor(e,s=v(),i=t=>g().some(n=>n.sessionId===t)){this.client=e,this.store=s,this.hasLocalSession=i,this.unsubscribeStore=s.subscribe(t=>this.handleMutation(t))}handleOpen(e){const s=e.params;if(c(s),h(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(n=>n.sessionId===s.sessionId).length>=o.maxSubscriptionsPerSession){this.client.sendRes({type:"res",id:e.id,ok:!1,error:"subscription_limit_exceeded",rejection:{code:"subscription_limit_exceeded",retryable:!0,requiredAction:"retry"}});return}const t={sessionId:s.sessionId,subscriptionId:s.subscriptionId,snapshotRevision:0,lastSentRevision:0,active:!1,buffered:[],pending:[],pendingBytes:0,flushTimer:null,lastQueuedRevision:0};this.subscriptions.set(s.subscriptionId,t),l(this.subscriptions.size);try{const n=this.store.readPage(s.sessionId,{limit:S(s.limit,o.defaultSnapshotMessages),...s.beforeSequence===void 0?{}:{beforeSequence:s.beforeSequence}});t.snapshotRevision=n.bodyRevision,t.lastSentRevision=n.bodyRevision,t.lastQueuedRevision=n.bodyRevision,this.client.sendRes({type:"res",id:e.id,ok:!0,payload:{...u(this.store.daemonInstallationId),sessionId:s.sessionId,subscriptionId:s.subscriptionId,storageMode:"machine_local",snapshotRevision:n.bodyRevision,firstAvailableSeq:n.firstAvailableSeq,lastAvailableSeq:n.lastAvailableSeq,hasOlder:n.hasOlder,messages:n.entries}}),r("historySnapshots"),r("historyBytesServed",Buffer.byteLength(JSON.stringify(n.entries),"utf8")),t.active=!0;for(const d of t.buffered)this.queueMutation(t,d);t.buffered=[]}catch(n){throw this.subscriptions.delete(s.subscriptionId),l(this.subscriptions.size),n}}handleSync(e){const s=e.params;if(c(s),h(s.sessionId,s.syncId),!Number.isSafeInteger(s.afterRevision)||s.afterRevision<0)throw new Error("invalid history sync revision");if(!this.hasLocalSession(s.sessionId)&&!this.store.getManifest(s.sessionId))throw new Error("history_unavailable");const i=this.store.getManifest(s.sessionId)?.bodyRevision??0;if(s.afterRevision>i)throw new Error("revision_gap");const t=M(s.limit),n=this.store.readDeltas(s.sessionId,s.afterRevision,t),d=[];let f=0;for(const m of n){const b={...m,subscriptionId:s.syncId},p=Buffer.byteLength(JSON.stringify(b),"utf8")+1;if(d.length>0&&f+p>y)break;if(p>y){this.client.sendRes({type:"res",id:e.id,ok:!1,error:"response_too_large"});return}d.push(b),f+=p}const I=d.at(-1)?.revision??s.afterRevision;this.client.sendRes({type:"res",id:e.id,ok:!0,payload:{...u(this.store.daemonInstallationId),sessionId:s.sessionId,syncId:s.syncId,fromRevision:s.afterRevision,toRevision:I,targetRevision:i,hasMore:I<i,mutations:d}}),r("historyDeltas"),r("historyBytesServed",f)}handlePage(e){const s=e.params;c(s);const i=this.requireSubscription(s.sessionId,s.subscriptionId);let t;try{t=this.store.readPageAtRevision(s.sessionId,{beforeSequence:s.beforeSequence,limit:S(s.limit,o.defaultPageMessages),revision:i.snapshotRevision})}catch{throw new Error("snapshot_compacted")}this.client.sendRes({type:"res",id:e.id,ok:!0,payload:{...u(this.store.daemonInstallationId),sessionId:s.sessionId,subscriptionId:s.subscriptionId,snapshotRevision:i.snapshotRevision,firstAvailableSeq:t.firstAvailableSeq,hasOlder:t.hasOlder,messages:t.entries}}),r("historyPages"),r("historyBytesServed",Buffer.byteLength(JSON.stringify(t.entries),"utf8"))}handleClose(e){const s=e.params;c(s),h(s.sessionId,s.subscriptionId);const i=this.subscriptions.get(s.subscriptionId);i?.sessionId===s.sessionId&&(this.flushSubscription(i),this.subscriptions.delete(s.subscriptionId),l(this.subscriptions.size)),this.client.sendRes({type:"res",id:e.id,ok:!0,payload:{...u(this.store.daemonInstallationId),sessionId:s.sessionId,subscriptionId:s.subscriptionId,closed:!0}})}closeAll(){for(const e of this.subscriptions.values())this.flushSubscription(e);this.subscriptions.clear(),l(0),this.unsubscribeStore()}closeSession(e){for(const[s,i]of this.subscriptions.entries())i.sessionId===e&&(this.flushSubscription(i),this.subscriptions.delete(s));l(this.subscriptions.size)}handleMutation(e){for(const s of this.subscriptions.values())s.sessionId===e.sessionId&&(s.active?this.queueMutation(s,e):s.buffered.push(e))}queueMutation(e,s){if(s.revision<=e.lastQueuedRevision)return;const i=Buffer.byteLength(JSON.stringify({...s,subscriptionId:e.subscriptionId}),"utf8")+1;if(e.pending.length>0&&e.pendingBytes+i>A&&this.flushSubscription(e),e.pending.push(s),e.pendingBytes+=i,e.lastQueuedRevision=s.revision,e.pending.length>=_){this.flushSubscription(e);return}e.flushTimer||(e.flushTimer=setTimeout(()=>this.flushSubscription(e),R))}flushSubscription(e){if(e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null),e.pending.length===0)return;const s=e.pending.filter(t=>t.revision>e.lastSentRevision);if(e.pending=[],e.pendingBytes=0,s.length===0)return;const i=s[s.length-1].revision;this.client.sendEvent({type:"event",event:"session.body.delta",payload:{...u(this.store.daemonInstallationId),sessionId:e.sessionId,subscriptionId:e.subscriptionId,fromRevision:e.lastSentRevision,toRevision:i,mutations:s.map(t=>({...t,subscriptionId:e.subscriptionId}))}}),r("historyDeltas"),r("historyBytesServed",Buffer.byteLength(JSON.stringify(s),"utf8")),e.lastSentRevision=i}requireSubscription(e,s){h(e,s);const i=this.subscriptions.get(s);if(!i||i.sessionId!==e)throw new Error("subscription_not_found");return i}}function h(a,e){if(typeof a!="string"||!a||typeof e!="string"||!e)throw new Error("invalid history subscription identity")}function S(a,e){const s=a??e;if(!Number.isSafeInteger(s)||s<1||s>o.maxPageMessages)throw new Error("invalid history limit");return s}function M(a){const e=a??o.maxDeltaMutations;if(!Number.isSafeInteger(e)||e<1||e>o.maxDeltaMutations)throw new Error("invalid history sync limit");return e}export{_ as PERSONAL_HISTORY_DELTA_BATCH_MAX_MUTATIONS,R as PERSONAL_HISTORY_DELTA_BATCH_WINDOW_MS,L as PersonalHistorySubscriptions};
|
|
1
|
+
import{SESSION_HISTORY_LIMITS as l}from"@shennian/wire";import{getCanonicalSessionStore as M,listSessionRecords as _}from"./store.js";import{recordPersonalSyncLocalMetric as d,setPersonalSyncLocalSubscriptions as I}from"../personal-sync/metrics.js";import{assertPersonalSyncIdentity as p,getPersonalSyncIdentity as u}from"../personal-sync/epoch.js";const y=100,b=l.maxDeltaMutations,g=l.maxMutationBytes-4096,m=l.maxReplicaPageBytes-16384;class O{client;store;hasLocalSession;subscriptions=new Map;replicaStreams=new Map;unsubscribeStore;constructor(s,e=M(),i=t=>_().some(n=>n.sessionId===t)){this.client=s,this.store=e,this.hasLocalSession=i,this.unsubscribeStore=e.subscribe(t=>this.handleMutation(t))}handleOpen(s){const e=s.params;if(p(e),h(e.sessionId,e.subscriptionId),!this.hasLocalSession(e.sessionId)&&!this.store.getManifest(e.sessionId))throw new Error("history_unavailable");if(this.subscriptions.has(e.subscriptionId))throw new Error("subscription already exists");if([...this.subscriptions.values()].filter(n=>n.sessionId===e.sessionId).length>=l.maxSubscriptionsPerSession){this.client.sendRes({type:"res",id:s.id,ok:!1,error:"subscription_limit_exceeded",rejection:{code:"subscription_limit_exceeded",retryable:!0,requiredAction:"retry"}});return}const t={sessionId:e.sessionId,subscriptionId:e.subscriptionId,snapshotRevision:0,lastSentRevision:0,active:!1,buffered:[],pending:[],pendingBytes:0,flushTimer:null,lastQueuedRevision:0};this.subscriptions.set(e.subscriptionId,t),this.updateSubscriptionMetric();try{const n=this.store.readPage(e.sessionId,{limit:R(e.limit,l.defaultSnapshotMessages),...e.beforeSequence===void 0?{}:{beforeSequence:e.beforeSequence}});t.snapshotRevision=n.bodyRevision,t.lastSentRevision=n.bodyRevision,t.lastQueuedRevision=n.bodyRevision,this.client.sendRes({type:"res",id:s.id,ok:!0,payload:{...u(this.store.daemonInstallationId),sessionId:e.sessionId,subscriptionId:e.subscriptionId,storageMode:"machine_local",snapshotRevision:n.bodyRevision,firstAvailableSeq:n.firstAvailableSeq,lastAvailableSeq:n.lastAvailableSeq,hasOlder:n.hasOlder,messages:n.entries}}),d("historySnapshots"),d("historyBytesServed",Buffer.byteLength(JSON.stringify(n.entries),"utf8")),t.active=!0;for(const o of t.buffered)this.queueMutation(t,o);t.buffered=[]}catch(n){throw this.subscriptions.delete(e.subscriptionId),this.updateSubscriptionMetric(),n}}handleSync(s){const e=s.params;if(p(e),h(e.sessionId,e.syncId),!Number.isSafeInteger(e.afterRevision)||e.afterRevision<0)throw new Error("invalid history sync revision");if(!this.hasLocalSession(e.sessionId)&&!this.store.getManifest(e.sessionId))throw new Error("history_unavailable");const i=this.store.getManifest(e.sessionId)?.bodyRevision??0;if(e.afterRevision>i)throw new Error("revision_gap");const t=T(e.limit),n=this.store.readDeltas(e.sessionId,e.afterRevision,t),o=[];let r=0;for(const B of n){const S={...B,subscriptionId:e.syncId},f=Buffer.byteLength(JSON.stringify(S),"utf8")+1;if(o.length>0&&r+f>m)break;if(f>m){this.client.sendRes({type:"res",id:s.id,ok:!1,error:"response_too_large"});return}o.push(S),r+=f}const c=o.at(-1)?.revision??e.afterRevision;this.client.sendRes({type:"res",id:s.id,ok:!0,payload:{...u(this.store.daemonInstallationId),sessionId:e.sessionId,syncId:e.syncId,fromRevision:e.afterRevision,toRevision:c,targetRevision:i,hasMore:c<i,mutations:o}}),d("historyDeltas"),d("historyBytesServed",r)}handleReplicaSubscribe(s){const e=s.params;if(p(e),v(e.appInstallationId,e.streamId,e.generation),!this.replicaStreams.has(e.streamId)&&this.replicaStreams.size>=l.maxReplicaStreamsPerDaemon){this.client.sendRes({type:"res",id:s.id,ok:!1,error:"subscription_limit_exceeded",rejection:{code:"subscription_limit_exceeded",retryable:!0,requiredAction:"retry"}});return}const i=this.replicaStreams.get(e.streamId);if(i&&e.generation<i.generation)throw new Error("invalid body replica generation");i&&this.flushReplicaStream(i);const t={appInstallationId:e.appInstallationId,streamId:e.streamId,generation:e.generation,active:!1,buffered:[],highWaterBySession:new Map,lastSentBySession:new Map,lastQueuedBySession:new Map,pendingBySession:new Map,pendingCount:0,pendingBytes:0,flushTimer:null};this.replicaStreams.set(e.streamId,t),this.updateSubscriptionMetric();const n=this.store.listSessionIds().map(o=>{const r=this.store.getManifest(o)?.bodyRevision??0;return t.highWaterBySession.set(o,r),t.lastSentBySession.set(o,r),t.lastQueuedBySession.set(o,r),{sessionId:o,bodyRevision:r}});this.client.sendRes({type:"res",id:s.id,ok:!0,payload:{...u(this.store.daemonInstallationId),appInstallationId:e.appInstallationId,streamId:e.streamId,generation:e.generation,highWatermarks:n}}),t.active=!0;for(const o of t.buffered)this.queueReplicaMutation(t,o);t.buffered=[]}handleReplicaUnsubscribe(s){const e=s.params;p(e),v(e.appInstallationId,e.streamId,e.generation);const i=this.replicaStreams.get(e.streamId);i&&i.generation===e.generation&&(this.flushReplicaStream(i),this.replicaStreams.delete(e.streamId),this.updateSubscriptionMetric()),this.client.sendRes({type:"res",id:s.id,ok:!0,payload:{...u(this.store.daemonInstallationId),appInstallationId:e.appInstallationId,streamId:e.streamId,generation:e.generation,closed:!0}})}handlePage(s){const e=s.params;p(e);const i=this.requireSubscription(e.sessionId,e.subscriptionId);let t;try{t=this.store.readPageAtRevision(e.sessionId,{beforeSequence:e.beforeSequence,limit:R(e.limit,l.defaultPageMessages),revision:i.snapshotRevision})}catch{throw new Error("snapshot_compacted")}this.client.sendRes({type:"res",id:s.id,ok:!0,payload:{...u(this.store.daemonInstallationId),sessionId:e.sessionId,subscriptionId:e.subscriptionId,snapshotRevision:i.snapshotRevision,firstAvailableSeq:t.firstAvailableSeq,hasOlder:t.hasOlder,messages:t.entries}}),d("historyPages"),d("historyBytesServed",Buffer.byteLength(JSON.stringify(t.entries),"utf8"))}handleClose(s){const e=s.params;p(e),h(e.sessionId,e.subscriptionId);const i=this.subscriptions.get(e.subscriptionId);i?.sessionId===e.sessionId&&(this.flushSubscription(i),this.subscriptions.delete(e.subscriptionId),this.updateSubscriptionMetric()),this.client.sendRes({type:"res",id:s.id,ok:!0,payload:{...u(this.store.daemonInstallationId),sessionId:e.sessionId,subscriptionId:e.subscriptionId,closed:!0}})}closeAll(){for(const s of this.subscriptions.values())this.flushSubscription(s);for(const s of this.replicaStreams.values())this.flushReplicaStream(s);this.subscriptions.clear(),this.replicaStreams.clear(),I(0),this.unsubscribeStore()}closeSession(s){for(const[e,i]of this.subscriptions.entries())i.sessionId===s&&(this.flushSubscription(i),this.subscriptions.delete(e));this.updateSubscriptionMetric()}handleMutation(s){for(const e of this.subscriptions.values())e.sessionId===s.sessionId&&(e.active?this.queueMutation(e,s):e.buffered.push(s));for(const e of this.replicaStreams.values())e.active?this.queueReplicaMutation(e,s):e.buffered.push(s)}queueReplicaMutation(s,e){const i=s.lastQueuedBySession.get(e.sessionId)??0;if(e.revision<=i)return;const t=Buffer.byteLength(JSON.stringify({...e,subscriptionId:s.streamId}),"utf8")+1;s.pendingCount>0&&s.pendingBytes+t>g&&this.flushReplicaStream(s);const n=s.pendingBySession.get(e.sessionId)??[];if(n.push(e),s.pendingBySession.set(e.sessionId,n),s.pendingCount++,s.pendingBytes+=t,s.lastQueuedBySession.set(e.sessionId,e.revision),s.pendingCount>=b){this.flushReplicaStream(s);return}s.flushTimer||(s.flushTimer=setTimeout(()=>this.flushReplicaStream(s),y))}flushReplicaStream(s){if(s.flushTimer&&(clearTimeout(s.flushTimer),s.flushTimer=null),s.pendingCount===0)return;const e=[...s.pendingBySession.entries()].flatMap(([i,t])=>{const n=s.lastSentBySession.get(i)??0,o=t.filter(c=>c.revision>n);if(o.length===0)return[];const r=o[o.length-1].revision;return s.lastSentBySession.set(i,r),[{sessionId:i,fromRevision:n,toRevision:r,mutations:o.map(c=>({...c,subscriptionId:s.streamId}))}]});s.pendingBySession.clear(),s.pendingCount=0,s.pendingBytes=0,e.length!==0&&(this.client.sendEvent({type:"event",event:"session.body.replica.delta",payload:{...u(this.store.daemonInstallationId),appInstallationId:s.appInstallationId,streamId:s.streamId,generation:s.generation,batches:e}}),d("historyDeltas"),d("historyBytesServed",Buffer.byteLength(JSON.stringify(e),"utf8")))}queueMutation(s,e){if(e.revision<=s.lastQueuedRevision)return;const i=Buffer.byteLength(JSON.stringify({...e,subscriptionId:s.subscriptionId}),"utf8")+1;if(s.pending.length>0&&s.pendingBytes+i>g&&this.flushSubscription(s),s.pending.push(e),s.pendingBytes+=i,s.lastQueuedRevision=e.revision,s.pending.length>=b){this.flushSubscription(s);return}s.flushTimer||(s.flushTimer=setTimeout(()=>this.flushSubscription(s),y))}flushSubscription(s){if(s.flushTimer&&(clearTimeout(s.flushTimer),s.flushTimer=null),s.pending.length===0)return;const e=s.pending.filter(t=>t.revision>s.lastSentRevision);if(s.pending=[],s.pendingBytes=0,e.length===0)return;const i=e[e.length-1].revision;this.client.sendEvent({type:"event",event:"session.body.delta",payload:{...u(this.store.daemonInstallationId),sessionId:s.sessionId,subscriptionId:s.subscriptionId,fromRevision:s.lastSentRevision,toRevision:i,mutations:e.map(t=>({...t,subscriptionId:s.subscriptionId}))}}),d("historyDeltas"),d("historyBytesServed",Buffer.byteLength(JSON.stringify(e),"utf8")),s.lastSentRevision=i}requireSubscription(s,e){h(s,e);const i=this.subscriptions.get(e);if(!i||i.sessionId!==s)throw new Error("subscription_not_found");return i}updateSubscriptionMetric(){I(this.subscriptions.size+this.replicaStreams.size)}}function h(a,s){if(typeof a!="string"||!a||typeof s!="string"||!s)throw new Error("invalid history subscription identity")}function v(a,s,e){if(typeof a!="string"||!a||typeof s!="string"||!s||!Number.isSafeInteger(e)||e<1)throw new Error("invalid body replica stream identity")}function R(a,s){const e=a??s;if(!Number.isSafeInteger(e)||e<1||e>l.maxPageMessages)throw new Error("invalid history limit");return e}function T(a){const s=a??l.maxDeltaMutations;if(!Number.isSafeInteger(s)||s<1||s>l.maxDeltaMutations)throw new Error("invalid history sync limit");return s}export{b as PERSONAL_HISTORY_DELTA_BATCH_MAX_MUTATIONS,y as PERSONAL_HISTORY_DELTA_BATCH_WINDOW_MS,O as PersonalHistorySubscriptions};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ReqFrame } from '@shennian/wire';
|
|
2
2
|
import type { CliRelayClient } from '../relay/client.js';
|
|
3
|
-
import type { UpgradePolicyController } from './types.js';
|
|
3
|
+
import type { RemoteAccessPauseController, UpgradePolicyController } from './types.js';
|
|
4
4
|
import type { UpgradeIdleState } from '../upgrade/scheduler.js';
|
|
5
5
|
import type { NativeSessionFusionService } from '../native-fusion/service.js';
|
|
6
6
|
import { type ManagedRoomEnqueueParams, type ManagedRoomQueueContext, type PreparedManagedRoomTurn } from './queue.js';
|
|
@@ -12,6 +12,7 @@ import '../agents/cursor.js';
|
|
|
12
12
|
import '../agents/opencode.js';
|
|
13
13
|
import '../agents/manager.js';
|
|
14
14
|
import type { ManagedRoomBinder } from '../room/managed-room-binding.js';
|
|
15
|
+
import type { ManagedRoomExecutor } from '../room/managed-room-ipc.js';
|
|
15
16
|
export { resolveSessionWorkDir } from '../fs/boundary.js';
|
|
16
17
|
export declare class SessionManager {
|
|
17
18
|
private client;
|
|
@@ -19,6 +20,9 @@ export declare class SessionManager {
|
|
|
19
20
|
private cliVersion?;
|
|
20
21
|
private upgradePolicyController;
|
|
21
22
|
private managedRoomBinder;
|
|
23
|
+
private remoteAccessPauseController;
|
|
24
|
+
private managedRoomContextRevoker;
|
|
25
|
+
private managedRoomExecutor;
|
|
22
26
|
private sessions;
|
|
23
27
|
/** Track processed request IDs to deduplicate replayed offline messages */
|
|
24
28
|
private processedReqIds;
|
|
@@ -33,7 +37,7 @@ export declare class SessionManager {
|
|
|
33
37
|
private activityProbeTimer;
|
|
34
38
|
private attachmentCleanupTimer;
|
|
35
39
|
private activeRequests;
|
|
36
|
-
constructor(client: CliRelayClient, nativeFusion?: NativeSessionFusionService | null, cliVersion?: string | undefined, upgradePolicyController?: UpgradePolicyController | null, managedRoomBinder?: ManagedRoomBinder | null);
|
|
40
|
+
constructor(client: CliRelayClient, nativeFusion?: NativeSessionFusionService | null, cliVersion?: string | undefined, upgradePolicyController?: UpgradePolicyController | null, managedRoomBinder?: ManagedRoomBinder | null, remoteAccessPauseController?: RemoteAccessPauseController | null, managedRoomContextRevoker?: ((token: string) => void) | null, managedRoomExecutor?: ManagedRoomExecutor | null);
|
|
37
41
|
private getRuntime;
|
|
38
42
|
getUpgradeIdleState(): UpgradeIdleState;
|
|
39
43
|
setManagedRoomTurnPreparer(prepare: (contexts: ManagedRoomQueueContext[]) => Promise<PreparedManagedRoomTurn>): void;
|
|
@@ -54,4 +58,5 @@ export declare class SessionManager {
|
|
|
54
58
|
private resolvePath;
|
|
55
59
|
private resolveAuthorizedPath;
|
|
56
60
|
cleanup(): Promise<void>;
|
|
61
|
+
private revokeManagedRoomToken;
|
|
57
62
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SESSION_SYNC_PROTOCOL_VERSION as d,SESSION_SYNC_DATA_EPOCH as u,isAvailableAgentType as y}from"@shennian/wire";import m from"node:crypto";import{getRegisteredAgents as w,unregisterAgent as S}from"../agents/adapter.js";import{loadConfig as R}from"../config/index.js";import{handleUpgradeStart as k,handleUpgradeStatus as A}from"../commands/upgrade.js";import{handleAgentsRefresh as I,handleModelsRefresh as T}from"./handlers/agents.js";import{handleAgentCapabilitiesList as C}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as P,handleAgentConfigGet as D,handleAgentConfigTest as E,handleAgentConfigUpsert as M}from"./handlers/agent-config.js";import{handleChatAbort as x,handleChatSend as p}from"./handlers/chat.js";import{handleSessionRefresh as O}from"./handlers/session-refresh.js";import{handleSessionToolDetail as F}from"./handlers/tool-detail.js";import{handleSessionTitleSet as _}from"./handlers/title.js";import{handleSessionMessageStatus as N}from"./handlers/message-status.js";import{handleVoiceSessionBodyWindow as Q}from"./handlers/voice-window.js";import{cleanupPendingTransfers as U,handleFsLs as W,handleFsRead as z,handleFsRename as V,handleFsWrite as B,handleFsTransferAbort as j,handleFsTransferChunk as $,handleFsTransferFinish as L,handleFsTransferStart as H,handleFsExportMarkdownPdf as Y,handleFsExportMarkdownPdfSetup as G,handleFsArchiveZip as K}from"./handlers/fs.js";import{handleRegionProbe as X,handleRegionSwitch as Z,handleUpgradeSetPolicy as J}from"./handlers/control.js";import{ManagerRuntimeService as q,setManagerRuntimeService as f}from"../manager/runtime.js";import{ChatQueueManager as ee}from"./queue.js";import{createAuthorizedFsRoot as se,resolveAuthorizedPath as te,resolveSessionWorkDir as ae}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 ie}from"../agents/custom.js";import{handleManagedRoomBind as ne,handleManagedRoomRestart as re}from"./handlers/room-managed.js";import{getDaemonInstallationId as g,listSessionRecords as v,purgeCanonicalSession as oe,purgeSessionRecord as ce}from"./store.js";import{PersonalHistorySubscriptions as le}from"./history-subscriptions.js";import{sweepAttachmentTransfers as he}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as de}from"../native-fusion/native-source-deletions.js";import{purgeToolDetails as ue}from"./tool-detail-store.js";import{readDeletionSyncRevision as me,writeDeletionSyncRevision as b}from"./deletion-sync-state.js";import{assertDeletionSyncRunnable as pe,clearDeletionSyncFailure as fe,DeletionSyncRetryWaitError as ge,DeletionSyncTerminalError as ve,recordDeletionSyncFailure as h}from"./deletion-sync-state.js";import{NativeSessionCleanupOutbox as be}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as c}from"../personal-sync/epoch.js";const ye=50;function we(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))||t.nativeCleanupPolicy!==void 0&&t.nativeCleanupPolicy!=="none"&&t.nativeCleanupPolicy!=="auto")throw new Error("invalid session deletion command");for(const a of t.sources)if(!a?.sourceSessionKey||!y(a.agentType))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 os}from"../fs/boundary.js";class is{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;historySubscriptions;nativeCleanupOutbox;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 q({getRuntime:()=>this.getRuntime(),dispatchReq:n=>this.handleReq(n)}),this.chatQueue=new ee({getRuntime:()=>this.getRuntime(),dispatchReq:n=>this.handleReq(n),dispatchQueuedReq:n=>this.dispatchQueuedChat(n)}),this.historySubscriptions=new le(this.client),this.nativeCleanupOutbox=new be({logger:console.info}),this.nativeFusion?.setExternalTerminalHandler?.(n=>{this.handleExternalTerminal(n)}),this.nativeCleanupOutbox.start(),f(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([...v().map(r=>r.workDir),...Array.from(this.pendingTransfers.values()).map(r=>r.rootPath)]);for(const r of n)try{he(this.resolvePath(r))}catch(l){console.error("[attachment.cleanup] failed",l instanceof Error?l.message:String(l))}},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}}setManagedRoomTurnPreparer(e){this.chatQueue.setManagedRoomTurnPreparer(e)}async enqueueManagedRoomBatch(e){const s=e.map(t=>{const a=v().find(i=>i.sessionId===t.params.sessionId);return{context:t.context,params:{...t.params,modelId:a?.modelId??t.params.modelId,sessionMode:a?.sessionMode??t.params.sessionMode,managerConfig:a?.managerConfig??t.params.managerConfig,managerDefaultWorkerAgentType:a?.managerDefaultWorkerAgentType??t.params.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:a?.managerDefaultWorkerModelId??t.params.managerDefaultWorkerModelId}}});await this.chatQueue.enqueueManagedRoomBatch(s)}async dispatchQueuedChat(e){const s={value:null},t=new Proxy(this.client,{get:(a,i)=>{if(i==="sendRes")return r=>{s.value=r};const n=Reflect.get(a,i,a);return typeof n=="function"?n.bind(a):n}});if(await p({...this.getRuntime(),client:t},e,e.params.roomActivation===!0?{source:"room_activation"}:void 0),!s.value?.ok||s.value.payload?.deliveryState==="failed")throw new Error(s.value?.error??"queued_chat_dispatch_failed")}async synchronizeSessionDeletions(){pe();try{await this.synchronizeSessionDeletionsOnce(),fe()}catch(e){if(e instanceof ge||e instanceof ve)throw e;const s=e instanceof Error?e.message:String(e),t=/offline|not connected|disconnect|timed out|timeout|temporar|unavailable|ECONN|ENOTFOUND|EIO|EBUSY/i.test(s);throw h({code:t?"transport_unavailable":"invalid_deletion_sync_response",retryable:t}),e}}async synchronizeSessionDeletionsOnce(){const e=g(),s=me(),t=await this.client.sendReq({type:"req",id:`session-deletions-sync-${m.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:d,dataEpoch:u,daemonInstallationId:e,afterRevision:s}});if(!t.ok){const r=t.rejection;throw r?(h({code:r.code,retryable:r.retryable&&r.requiredAction==="retry"}),new Error(r.code)):(h({code:"unstructured_rejection",retryable:!1}),new Error(t.error??"unstructured_rejection"))}const a=t.payload;c(a),we(a,s);let i=a.baselineRevision;i>s&&b(i);for(const r of a.deletions)r.deletionRevision<=i||(await this.applySessionDeletion(r),i=r.deletionRevision,b(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-${m.randomUUID()}`,method:"session.deletions.ack",params:{protocolVersion:d,dataEpoch:u,daemonInstallationId:e,deletionRevision:i}});if(!n.ok)throw new Error(n.error??"session deletion acknowledgement failed")}async applySessionDeletion(e){const s=new de({daemonInstallationId:g()});s.recordSession(e);for(const a of e.sources)s.record({...e,...a});const t=this.sessions.get(e.sessionId);if(t){try{await t.adapter.stop()}catch{}this.sessions.delete(e.sessionId)}this.historySubscriptions.closeSession(e.sessionId),this.nativeFusion?.handleSessionDeleted(e.sessionId),this.chatQueue.purgeSession(e.sessionId),ue(e.sessionId),oe(e.sessionId),ce(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null),this.nativeCleanupOutbox.enqueue(e),this.nativeCleanupOutbox.kick()}publishSessionActivity(e,s){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:s}})}async handleExternalTerminal(e){const s=this.sessions.get(e);s&&(this.sessions.delete(e),s.heartbeatTimer&&clearInterval(s.heartbeatTimer),s.adapter.removeAllListeners(),await s.adapter.stop().catch(()=>{})),this.chatQueue.noteTerminal(e)}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 w())s.startsWith("custom:")&&S(s);const e=R();for(const[s,t]of Object.entries(e.customAgents??{}))ie(s,t)}async handleReq(e){const s=this.getRuntime();this.activeRequests++;try{switch(e.method){case"chat.send":c(e.params),await p(s,e,e.params.roomActivation===!0?{source:"room_activation"}:void 0);break;case"chat.enqueue":c(e.params),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.abort":c(e.params),await x(s,e);break;case"session.refresh":c(e.params),await O(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.history":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"upgrade_required"});break;case"session.message.status":N(s,e);break;case"session.tool.detail":await F(s,e);break;case"voice.session.body.window":Q(s,e);break;case"session.title.set":await _(s,e);break;case"room.agent.bind-managed":await ne(s,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await re(s,this.managedRoomBinder,e);break;case"fs.ls":await W(s,e);break;case"fs.read":await z(s,e);break;case"fs.write":await B(s,e);break;case"fs.export.markdown-pdf":await Y(s,e);break;case"fs.export.markdown-pdf.setup":await G(s,e);break;case"fs.archive.zip":await K(s,e);break;case"fs.rename":await V(s,e);break;case"fs.transfer.start":await H(s,e);break;case"fs.transfer.chunk":await $(s,e);break;case"fs.transfer.finish":await L(s,e);break;case"fs.transfer.abort":await j(s,e);break;case"region.probe":await X(s,e);break;case"region.switch":await Z(s,e);break;case"upgrade.start":await k(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0});break;case"upgrade.status":await A(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await J(s,e);break;case"agents.refresh":await I(s,e);break;case"models.refresh":await T(s,e);break;case"agent.config.get":await D(s,e);break;case"agent.config.upsert":await M(s,e);break;case"agent.config.clear":await P(s,e);break;case"agent.config.test":await E(s,e);break;case"agent.capabilities.list":await C(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<ye)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 ae(e)}resolveAuthorizedPath(e,s){return te(e,se(s))}async cleanup(){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)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(),f(null)}}export{is as SessionManager,os as resolveSessionWorkDir};
|
|
1
|
+
import{SESSION_SYNC_PROTOCOL_VERSION as p,SESSION_SYNC_DATA_EPOCH as f,isAvailableAgentType as k}from"@shennian/wire";import g from"node:crypto";import{getRegisteredAgents as S,unregisterAgent as A}from"../agents/adapter.js";import{loadConfig as T}from"../config/index.js";import{handleUpgradeStart as I,handleUpgradeStatus as C}from"../commands/upgrade.js";import{handleAgentsRefresh as P,handleModelsRefresh as E}from"./handlers/agents.js";import{handleAgentCapabilitiesList as D}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as x,handleAgentConfigGet as M,handleAgentConfigTest as O,handleAgentConfigUpsert as F}from"./handlers/agent-config.js";import{handleChatAbort as _,handleChatSend as v}from"./handlers/chat.js";import{handleSessionRefresh as N}from"./handlers/session-refresh.js";import{handleSessionToolDetail as Q}from"./handlers/tool-detail.js";import{handleSessionTitleSet as U}from"./handlers/title.js";import{handleSessionMessageStatus as W}from"./handlers/message-status.js";import{handleVoiceSessionBodyWindow as z}from"./handlers/voice-window.js";import{handleRemoteAccessPause as V}from"./handlers/remote-access.js";import{cleanupPendingTransfers as B,handleFsLs as j,handleFsRead as $,handleFsRename as L,handleFsWrite as H,handleFsTransferAbort as Y,handleFsTransferChunk as G,handleFsTransferFinish as K,handleFsTransferStart as X,handleFsExportMarkdownPdf as Z,handleFsExportMarkdownPdfSetup as J,handleFsArchiveZip as q}from"./handlers/fs.js";import{handleRegionProbe as ee,handleRegionSwitch as se,handleUpgradeSetPolicy as te}from"./handlers/control.js";import{ManagerRuntimeService as ae,setManagerRuntimeService as b}from"../manager/runtime.js";import{ChatQueueManager as ie}from"./queue.js";import{createAuthorizedFsRoot as ne,resolveAuthorizedPath as oe,resolveSessionWorkDir as re}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 ce}from"../agents/custom.js";import{handleManagedRoomBind as le,handleManagedRoomRestart as he}from"./handlers/room-managed.js";import{getDaemonInstallationId as R,listSessionRecords as y,purgeCanonicalSession as de,purgeSessionRecord as ue}from"./store.js";import{PersonalHistorySubscriptions as me}from"./history-subscriptions.js";import{sweepAttachmentTransfers as pe}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as fe}from"../native-fusion/native-source-deletions.js";import{purgeToolDetails as ge}from"./tool-detail-store.js";import{readDeletionSyncRevision as ve,writeDeletionSyncRevision as w}from"./deletion-sync-state.js";import{assertDeletionSyncRunnable as be,clearDeletionSyncFailure as Re,DeletionSyncRetryWaitError as ye,DeletionSyncTerminalError as we,recordDeletionSyncFailure as m}from"./deletion-sync-state.js";import{NativeSessionCleanupOutbox as ke}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as l}from"../personal-sync/epoch.js";const Se=50;function Ae(r,e){if(!r||!Number.isSafeInteger(r.baselineRevision)||r.baselineRevision<e||!Number.isSafeInteger(r.latestRevision)||r.latestRevision<r.baselineRevision||!Array.isArray(r.deletions))throw new Error("invalid deletion sync response");let s=r.baselineRevision;for(const t of r.deletions){if(!t?.sessionId||!Number.isSafeInteger(t.deletionRevision)||t.deletionRevision!==s+1||!Array.isArray(t.sources)||Number.isNaN(Date.parse(t.deletedAt))||t.nativeCleanupPolicy!==void 0&&t.nativeCleanupPolicy!=="none"&&t.nativeCleanupPolicy!=="auto")throw new Error("invalid session deletion command");for(const a of t.sources)if(!a?.sourceSessionKey||!k(a.agentType))throw new Error("invalid native source deletion command");s=t.deletionRevision}if(s!==r.latestRevision)throw new Error("invalid deletion sync revision range")}import{resolveSessionWorkDir as us}from"../fs/boundary.js";class ls{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;remoteAccessPauseController;managedRoomContextRevoker;managedRoomExecutor;sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;historySubscriptions;nativeCleanupOutbox;activityProbeTimer=null;attachmentCleanupTimer=null;activeRequests=0;constructor(e,s=null,t,a=null,i=null,c=null,n=null,h=null){this.client=e,this.nativeFusion=s,this.cliVersion=t,this.upgradePolicyController=a,this.managedRoomBinder=i,this.remoteAccessPauseController=c,this.managedRoomContextRevoker=n,this.managedRoomExecutor=h,this.managerRuntime=new ae({getRuntime:()=>this.getRuntime(),dispatchReq:o=>this.handleReq(o)}),this.chatQueue=new ie({getRuntime:()=>this.getRuntime(),dispatchReq:o=>this.handleReq(o),dispatchQueuedReq:o=>this.dispatchQueuedChat(o)}),this.historySubscriptions=new me(this.client),this.nativeCleanupOutbox=new ke({logger:console.info}),this.nativeFusion?.setExternalTerminalHandler?.(o=>{this.handleExternalTerminal(o)}),this.nativeCleanupOutbox.start(),b(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents(),this.activityProbeTimer=setInterval(()=>{this.publishManagedActivitySnapshots().catch(o=>{console.error("[session.activity] managed probe failed",o)})},15e3),this.activityProbeTimer.unref?.(),this.attachmentCleanupTimer=setInterval(()=>{const o=new Set([...y().map(d=>d.workDir),...Array.from(this.pendingTransfers.values()).map(d=>d.rootPath)]);for(const d of o)try{pe(this.resolvePath(d))}catch(u){console.error("[attachment.cleanup] failed",u instanceof Error?u.message:String(u))}},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,revokeManagedRoomContextToken:this.managedRoomContextRevoker,managedRoomExecutor:this.managedRoomExecutor}}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}}setManagedRoomTurnPreparer(e){this.chatQueue.setManagedRoomTurnPreparer(e)}async enqueueManagedRoomBatch(e){const s=e.map(t=>{const a=y().find(i=>i.sessionId===t.params.sessionId);return{context:t.context,params:{...t.params,modelId:a?.modelId??t.params.modelId,sessionMode:a?.sessionMode??t.params.sessionMode,managerConfig:a?.managerConfig??t.params.managerConfig,managerDefaultWorkerAgentType:a?.managerDefaultWorkerAgentType??t.params.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:a?.managerDefaultWorkerModelId??t.params.managerDefaultWorkerModelId}}});await this.chatQueue.enqueueManagedRoomBatch(s)}async dispatchQueuedChat(e){const s={value:null},t=new Proxy(this.client,{get:(c,n)=>{if(n==="sendRes")return o=>{s.value=o};const h=Reflect.get(c,n,c);return typeof h=="function"?h.bind(c):h}}),a=e.params,i=a.roomActivation===!0?{source:"room_activation",managedAgentPolicy:a.managedAgentPolicy,managedRoomPromptSnapshot:a.managedRoomPromptSnapshot}:void 0;if(await v({...this.getRuntime(),client:t},e,i),!s.value?.ok||s.value.payload?.deliveryState==="failed")throw new Error(s.value?.error??"queued_chat_dispatch_failed")}async synchronizeSessionDeletions(){be();try{await this.synchronizeSessionDeletionsOnce(),Re()}catch(e){if(e instanceof ye||e instanceof we)throw e;const s=e instanceof Error?e.message:String(e),t=/offline|not connected|disconnect|timed out|timeout|temporar|unavailable|ECONN|ENOTFOUND|EIO|EBUSY/i.test(s);throw m({code:t?"transport_unavailable":"invalid_deletion_sync_response",retryable:t}),e}}async synchronizeSessionDeletionsOnce(){const e=R(),s=ve(),t=await this.client.sendReq({type:"req",id:`session-deletions-sync-${g.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:p,dataEpoch:f,daemonInstallationId:e,afterRevision:s}});if(!t.ok){const n=t.rejection;throw n?(m({code:n.code,retryable:n.retryable&&n.requiredAction==="retry"}),new Error(n.code)):(m({code:"unstructured_rejection",retryable:!1}),new Error(t.error??"unstructured_rejection"))}const a=t.payload;l(a),Ae(a,s);let i=a.baselineRevision;i>s&&w(i);for(const n of a.deletions)n.deletionRevision<=i||(await this.applySessionDeletion(n),i=n.deletionRevision,w(i));if(i!==a.latestRevision)throw new Error(`deletion revision gap: applied ${i}, latest ${a.latestRevision}`);if(i===0)return;const c=await this.client.sendReq({type:"req",id:`session-deletions-ack-${g.randomUUID()}`,method:"session.deletions.ack",params:{protocolVersion:p,dataEpoch:f,daemonInstallationId:e,deletionRevision:i}});if(!c.ok)throw new Error(c.error??"session deletion acknowledgement failed")}async applySessionDeletion(e){const s=new fe({daemonInstallationId:R()});s.recordSession(e);for(const a of e.sources)s.record({...e,...a});const t=this.sessions.get(e.sessionId);if(t){this.revokeManagedRoomToken(t);try{await t.adapter.stop()}catch{}this.sessions.delete(e.sessionId)}this.historySubscriptions.closeSession(e.sessionId),this.nativeFusion?.handleSessionDeleted(e.sessionId),this.chatQueue.purgeSession(e.sessionId),ge(e.sessionId),de(e.sessionId),ue(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null),this.nativeCleanupOutbox.enqueue(e),this.nativeCleanupOutbox.kick()}publishSessionActivity(e,s){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:s}})}async handleExternalTerminal(e){const s=this.sessions.get(e);s&&(this.sessions.delete(e),this.revokeManagedRoomToken(s),s.heartbeatTimer&&clearInterval(s.heartbeatTimer),s.adapter.removeAllListeners(),await s.adapter.stop().catch(()=>{})),this.chatQueue.noteTerminal(e)}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 S())s.startsWith("custom:")&&A(s);const e=T();for(const[s,t]of Object.entries(e.customAgents??{}))ce(s,t)}async handleReq(e){const s=this.getRuntime();this.activeRequests++;try{switch(e.method){case"chat.send":l(e.params),await v(s,e);break;case"chat.enqueue":l(e.params),await this.chatQueue.handleEnqueue(e);break;case"chat.queue.get":l(e.params),await this.chatQueue.handleGet(e);break;case"chat.queue.edit":l(e.params),await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":l(e.params),await this.chatQueue.handleDelete(e);break;case"chat.abort":l(e.params),await _(s,e);break;case"session.refresh":l(e.params),await N(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":W(s,e);break;case"session.tool.detail":await Q(s,e);break;case"voice.session.body.window":z(s,e);break;case"session.title.set":await U(s,e);break;case"room.agent.bind-managed":await le(s,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await he(s,this.managedRoomBinder,e);break;case"fs.ls":await j(s,e);break;case"fs.read":await $(s,e);break;case"fs.write":await H(s,e);break;case"fs.export.markdown-pdf":await Z(s,e);break;case"fs.export.markdown-pdf.setup":await J(s,e);break;case"fs.archive.zip":await q(s,e);break;case"fs.rename":await L(s,e);break;case"fs.transfer.start":await X(s,e);break;case"fs.transfer.chunk":await G(s,e);break;case"fs.transfer.finish":await K(s,e);break;case"fs.transfer.abort":await Y(s,e);break;case"region.probe":await ee(s,e);break;case"region.switch":await se(s,e);break;case"upgrade.start":await I(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0});break;case"upgrade.status":await C(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await te(s,e);break;case"machine.remote-access.pause":await V(this.client,this.remoteAccessPauseController,e);break;case"agents.refresh":await P(s,e);break;case"models.refresh":await E(s,e);break;case"agent.config.get":await M(s,e);break;case"agent.config.upsert":await F(s,e);break;case"agent.config.clear":await x(s,e);break;case"agent.config.test":await O(s,e);break;case"agent.capabilities.list":await D(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<Se)return;let e=null;for(const[s,t]of this.sessions)(!e||t.lastActiveAt<e.session.lastActiveAt)&&(e={key:s,session:t});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 re(e)}resolveAuthorizedPath(e,s){return oe(e,ne(s))}async cleanup(){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),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),B(this.getRuntime()),await this.managerRuntime.stop(),b(null)}revokeManagedRoomToken(e){const s=e.managedRoomContextToken;e.managedRoomContextToken=null,s&&this.managedRoomContextRevoker?.(s)}}export{ls as SessionManager,us as resolveSessionWorkDir};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type ChatEnqueueParams, type ChatQueueSnapshot, type ReqFrame } from '@shennian/wire';
|
|
2
2
|
import type { LocalReqDispatcher } from '../manager/runtime.js';
|
|
3
3
|
import type { SessionManagerRuntime } from './types.js';
|
|
4
|
+
import type { CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
5
|
+
import type { ManagedRoomPromptSnapshot } from '../room/managed-prompt-composer.js';
|
|
4
6
|
export type ManagedRoomQueueContext = {
|
|
5
7
|
registryKey: string;
|
|
6
8
|
causeMessageId: string;
|
|
@@ -12,6 +14,8 @@ export type PreparedManagedRoomTurn = {
|
|
|
12
14
|
systemPrompt: string;
|
|
13
15
|
roomContextRegistryKey: string;
|
|
14
16
|
managedRoomContextToken: string;
|
|
17
|
+
managedAgentPolicy: CompiledManagedAgentPolicy;
|
|
18
|
+
managedRoomPromptSnapshot: ManagedRoomPromptSnapshot;
|
|
15
19
|
};
|
|
16
20
|
export type ManagedRoomEnqueueParams = Omit<ChatEnqueueParams, 'protocolVersion' | 'dataEpoch' | 'daemonInstallationId'>;
|
|
17
21
|
export declare class ChatQueueManager {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import p from"node:fs";import{randomUUID as x}from"node:crypto";import{buildUserMessagePayload as j}from"@shennian/wire";import{mergeProjectedSessions as z}from"./projection.js";import{parsePersonalAttachmentReferences as
|
|
2
|
-
`,{encoding:"utf8",mode:384,flag:"wx"});const i=p.openSync(t,"r");if(typeof i=="number")try{p.fsyncSync(i)}finally{p.closeSync(i)}p.renameSync(t,n)}finally{try{p.unlinkSync(t)}catch{}}}function E(){return $("outbound-queue.json")}function A(s){const e=C();return{...y(M()),id:s.queueMessageId||s.clientMessageId||`queue-${x()}`,sessionId:s.sessionId,text:s.text,agentType:s.agentType,sessionMode:s.sessionMode,managerConfig:s.managerConfig,workDir:s.workDir,agentSessionId:s.agentSessionId??null,modelId:s.modelId??null,systemPrompt:s.systemPrompt??null,managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:s.managerDefaultWorkerModelId??null,reasoningEffort:s.reasoningEffort??null,clientMessageId:s.clientMessageId??null,attachments:
|
|
1
|
+
import p from"node:fs";import{randomUUID as x}from"node:crypto";import{buildUserMessagePayload as j}from"@shennian/wire";import{mergeProjectedSessions as z}from"./projection.js";import{parsePersonalAttachmentReferences as R}from"./attachment-reference.js";import{assertFinishedAttachmentReferences as w,markAttachmentReferences as T}from"./attachment-transfer-store.js";import{resolvePersonalSyncV1Path as $}from"../personal-sync/paths.js";import{getPersonalSyncIdentity as y}from"../personal-sync/epoch.js";import{getDaemonInstallationId as M}from"../personal-sync/identity.js";import{appendMessage as v,getMessageDeliveryStatus as q,recordSession as b,tombstoneMessage as F,upsertMessage as P}from"./store.js";import{requestSessionIndexPublish as k}from"./index-publisher.js";function O(){return{sessions:{}}}function C(){return new Date().toISOString()}function K(s){return`enqueue-send-${s}-${x()}`}function m(){try{const s=JSON.parse(p.readFileSync(E(),"utf-8"));return{sessions:s.sessions&&typeof s.sessions=="object"?s.sessions:{}}}catch(s){if(s.code==="ENOENT"||s instanceof Error&&s.message.startsWith("ENOENT:"))return O();throw s}}function I(s){const e=$();p.mkdirSync(e,{recursive:!0,mode:448});const n=E(),t=`${n}.tmp-${process.pid}-${x()}`;try{p.writeFileSync(t,`${JSON.stringify(s,null,2)}
|
|
2
|
+
`,{encoding:"utf8",mode:384,flag:"wx"});const i=p.openSync(t,"r");if(typeof i=="number")try{p.fsyncSync(i)}finally{p.closeSync(i)}p.renameSync(t,n)}finally{try{p.unlinkSync(t)}catch{}}}function E(){return $("outbound-queue.json")}function A(s){const e=C();return{...y(M()),id:s.queueMessageId||s.clientMessageId||`queue-${x()}`,sessionId:s.sessionId,text:s.text,agentType:s.agentType,sessionMode:s.sessionMode,managerConfig:s.managerConfig,workDir:s.workDir,agentSessionId:s.agentSessionId??null,modelId:s.modelId??null,systemPrompt:s.systemPrompt??null,managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:s.managerDefaultWorkerModelId??null,reasoningEffort:s.reasoningEffort??null,clientMessageId:s.clientMessageId??null,attachments:R(s.attachments),externalChannel:s.externalChannel??null,replyTarget:s.replyTarget??null,origin:s.origin,createdAt:e,updatedAt:e}}function S(s){return{id:s.clientMessageId??s.id,sessionId:s.sessionId,role:"user",ts:Date.parse(s.createdAt),payload:j(s.text,s.attachments)}}class de{opts;draining=new Set;prepareManagedRoomTurn=null;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}getSnapshot(e){return{...y(M()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:(m().sessions[e]??[]).map(W)}}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const n=this.opts.getRuntime(),t=m(),i=new Set,a=new Map;let d=!1;for(const o of e){const r=o.params;if(!r.sessionId||!r.text||!r.agentType||!r.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=A({...y(M()),...r});u.origin="room",u.managedRoom=o.context;const l=t.sessions[r.sessionId]??[],g=Q(l,u),h=u.clientMessageId??u.id,f=q(r.sessionId,h);if(!g&&f.state==="not_found")l.push(u),t.sessions[r.sessionId]=l,a.set(`${u.sessionId}\0${h}`,u),i.add(r.sessionId),d=!0;else if(g&&f.state==="not_found")a.set(`${g.sessionId}\0${h}`,g),i.add(r.sessionId);else if(g&&f.state==="queued")i.add(r.sessionId);else if(g){const c=l.filter(B=>B.id!==g.id);c.length?t.sessions[r.sessionId]=c:delete t.sessions[r.sessionId],d=!0}}d&&I(t);for(const o of a.values())v(o.sessionId,S(o),"queued"),b({sessionId:o.sessionId,agentType:o.agentType,sessionMode:o.sessionMode,managerConfig:o.managerConfig,workDir:o.workDir,agentSessionId:o.agentSessionId,modelId:o.modelId});if(i.size!==0){k(n.client);for(const o of i)this.broadcast(o),this.drainNext(o)}}async handleEnqueue(e){const n=this.opts.getRuntime(),t=e.params;if(z(t.sessionListProjection),!t.sessionId||!t.text||!t.agentType||!t.workDir){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"});return}const i=n.resolvePath(t.workDir),a=R(t.attachments);a?.length&&w(i,a);const d={text:t.text,attachments:a,localized:!1},o=await this.isSessionBusy(t.sessionId),r=m(),u=r.sessions[t.sessionId]??[];if(!o&&!u.length){await this.opts.dispatchReq({...e,id:K(e.id),method:"chat.send",params:{...t,text:d.text,responseId:e.id,clientMessageId:t.clientMessageId??t.queueMessageId,waitForDispatch:!0,attachments:d.attachments}});return}const l=A({...t,text:d.text,attachments:d.attachments}),g=l.clientMessageId??l.id,h=q(t.sessionId,g);if(h.state!=="not_found"){n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:h.state==="queued",queueMessageId:l.id,...h,queue:this.getSnapshot(t.sessionId)}});return}const f=Q(u,l);f||(r.sessions[t.sessionId]=[...u,l],I(r));const c=f??l;b({sessionId:c.sessionId,agentType:c.agentType,sessionMode:c.sessionMode,managerConfig:c.managerConfig,workDir:c.workDir,agentSessionId:c.agentSessionId,modelId:c.modelId}),v(c.sessionId,S(c),"queued"),T(n.resolvePath(c.workDir),c.attachments??[]),k(n.client),this.broadcast(t.sessionId),n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:f?.id??l.id,...q(t.sessionId,f?.clientMessageId??f?.id??l.clientMessageId??l.id),queue:this.getSnapshot(t.sessionId),...d.localized?{localizedAttachments:!0}:{}}}),o||this.drainNext(t.sessionId)}async handleGet(e){const n=this.opts.getRuntime(),t=e.params;if(!t.sessionId){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}}),this.getSnapshot(t.sessionId).busy||this.drainNext(t.sessionId)}async handleEdit(e){const n=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const i=m(),a=i.sessions[t.sessionId]??[],d=a.findIndex(u=>u.id===t.queueMessageId);if(d<0){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(a[d].managedRoom){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=R(t.attachments),r=n.resolvePath(a[d].workDir);o?.length&&w(r,o),a[d]={...a[d],text:t.text,attachments:o,updatedAt:C()},i.sessions[t.sessionId]=a,I(i),P(t.sessionId,S(a[d]),"queued"),a[d].attachments?.length&&T(r,a[d].attachments??[]),k(n.client),this.broadcast(t.sessionId),n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const n=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const i=m(),a=i.sessions[t.sessionId]??[],d=a.find(r=>r.id===t.queueMessageId);if(d?.managedRoom){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=a.filter(r=>r.id!==t.queueMessageId);if(o.length===a.length){n.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],I(i),F(t.sessionId,d?.clientMessageId??d?.id??t.queueMessageId),k(n.client),this.broadcast(t.sessionId),n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}noteTerminal(e){this.drainNext(e)}purgeSession(e){J(e),this.draining.delete(e)}drainIdleQueues(){const e=m();for(const n of Object.keys(e.sessions))this.drainNext(n)}async drainNext(e){if(!this.draining.has(e)){this.draining.add(e);try{if(await this.isSessionBusy(e))return;const t=m().sessions[e]??[],i=U(t);if(i.length===0){this.broadcast(e);return}for(const r of i)P(e,S(r),"queued");await this.dispatchQueuedMessages(i);const a=m(),d=new Set(i.map(r=>r.id)),o=(a.sessions[e]??[]).filter(r=>!d.has(r.id));o.length?a.sessions[e]=o:delete a.sessions[e],I(a),this.broadcast(e)}catch(n){console.error(`[chat.queue] dispatch failed sessionId=${e}: ${n instanceof Error?n.message:String(n)}`),this.broadcast(e)}finally{this.draining.delete(e)}}}async dispatchQueuedMessages(e){const n=e[0],t=e.flatMap(o=>o.managedRoom?[o.managedRoom]:[]),i=t.length>0?await this.prepareManagedRoomTurn?.(t):void 0;if(t.length>0&&!i)throw new Error("managed_room_turn_preparer_unavailable");const a=e.length===1?n.text:L(e),d=H(e.flatMap(o=>o.attachments??[]));await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${n.id}-${Date.now()}`,method:"chat.send",params:{...y(M()),sessionId:n.sessionId,text:a,agentType:n.agentType,sessionMode:t.length>0?"standard":n.sessionMode,managerConfig:t.length>0?null:n.managerConfig,workDir:n.workDir,agentSessionId:n.agentSessionId??null,modelId:n.modelId??void 0,systemPrompt:i?.systemPrompt??n.systemPrompt??null,managerDefaultWorkerAgentType:t.length>0?null:n.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:t.length>0?null:n.managerDefaultWorkerModelId,reasoningEffort:n.reasoningEffort??void 0,clientMessageId:n.clientMessageId??n.id,attachments:d,externalChannel:n.externalChannel,replyTarget:n.replyTarget,...i??{},roomActivation:t.length>0||void 0,suppressExternalOwnerEcho:t.length>0||void 0,queuedBatchItems:e.map(W),waitForDispatch:!0}})}async isSessionBusy(e){const n=this.opts.getRuntime().sessions.get(e);if(!n)return!1;if(n.currentRunId)return!0;if(!n.adapter.getStatus)return!1;const t=await n.adapter.getStatus().catch(()=>null);return t?.active?(t.runId&&(n.currentRunId=t.runId,n.currentRunPhase=t.runPhase??n.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}function J(s){const e=m();e.sessions[s]&&(delete e.sessions[s],I(e))}function Q(s,e){const n=D(e);if(n)return s.find(i=>D(i)===n)??null;if(e.origin!=="external")return null;const t=N(e);return s.find(i=>N(i)===t)??null}function W(s){const{origin:e,managedRoom:n,...t}=s;return t}function U(s){const e=s[0];if(!e)return[];const n=_(e),t=[];for(const i of s){if(_(i)!==n)break;t.push(i)}return t}function _(s){return JSON.stringify({agentType:s.agentType,sessionMode:s.sessionMode??null,workDir:s.workDir,agentSessionId:s.agentSessionId??null,modelId:s.modelId??null,systemPrompt:s.systemPrompt??null,reasoningEffort:s.reasoningEffort??null,managerConfig:s.managerConfig??null,managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:s.managerDefaultWorkerModelId??null,externalChannel:s.externalChannel??null,replyTarget:s.replyTarget??null,roomRegistryKey:s.managedRoom?.registryKey??null})}function L(s){return s.every(e=>e.managedRoom)?`[\u7FA4\u6D88\u606F]
|
|
3
3
|
${s.map(e=>G(e.text)).filter(Boolean).join(`
|
|
4
4
|
|
|
5
5
|
`)}`:`[\u8FDE\u7EED\u7528\u6237\u6D88\u606F]
|
|
@@ -8,5 +8,5 @@ ${e.text.trim()}`).join(`
|
|
|
8
8
|
|
|
9
9
|
`)}`}function G(s){const e=s.trim(),n=e.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);return n?`<${n[1]}>
|
|
10
10
|
${n[2]}`:e.startsWith(`[\u7FA4\u6D88\u606F]
|
|
11
|
-
`)?e.slice(6):e}function
|
|
12
|
-
`)}function
|
|
11
|
+
`)?e.slice(6):e}function N(s){const e=D(s);return e?`id:${e}`:["body",V(s.text),...(s.attachments??[]).map(n=>n.path||`${n.name}:${n.mimeType}`)].join(`
|
|
12
|
+
`)}function D(s){const e=typeof s.clientMessageId=="string"?s.clientMessageId.trim():"";if(e)return e;const n=typeof s.id=="string"?s.id.trim():"";return n&&!n.startsWith("queue-")?n:""}function H(s){const e=new Set,n=[];for(const t of s){const i=t.path||`${t.name}:${t.mimeType}`;e.has(i)||(e.add(i),n.push(t))}return n.length?n:void 0}function V(s){return String(s||"").replace(/\s+/g," ").trim()}export{de as ChatQueueManager,J as purgeQueuedSessionData};
|
|
@@ -4,6 +4,7 @@ import type { CliRelayClient } from '../relay/client.js';
|
|
|
4
4
|
import type { NativeSessionFusionService } from '../native-fusion/service.js';
|
|
5
5
|
import type { ManagerRuntimeService } from '../manager/runtime.js';
|
|
6
6
|
import type { ResolveAuthorizedPathResult } from '../fs/boundary.js';
|
|
7
|
+
import type { ManagedRoomExecutor } from '../room/managed-room-ipc.js';
|
|
7
8
|
export type ActiveSession = {
|
|
8
9
|
adapter: AgentAdapter;
|
|
9
10
|
workDir: string;
|
|
@@ -25,6 +26,8 @@ export type ActiveSession = {
|
|
|
25
26
|
externalChannel?: ExternalChannelSessionStatus | null;
|
|
26
27
|
externalChannelEnv?: NodeJS.ProcessEnv;
|
|
27
28
|
externalReplyTarget?: string | null;
|
|
29
|
+
/** Opaque per-turn Room capability; revoked and cleared on every terminal path. */
|
|
30
|
+
managedRoomContextToken?: string | null;
|
|
28
31
|
};
|
|
29
32
|
export type PendingTransfer = {
|
|
30
33
|
transferId: string;
|
|
@@ -47,6 +50,15 @@ export type UpgradePolicyController = {
|
|
|
47
50
|
setPolicy(policy: 'none' | 'patch'): void;
|
|
48
51
|
getPolicy(): 'none' | 'patch';
|
|
49
52
|
};
|
|
53
|
+
export type RemoteAccessPauseController = {
|
|
54
|
+
prepare(): {
|
|
55
|
+
disabledAt: string;
|
|
56
|
+
autoStartDisabled: boolean;
|
|
57
|
+
autoStartError?: string;
|
|
58
|
+
};
|
|
59
|
+
isDisabled(): boolean;
|
|
60
|
+
shutdownAfterResponse(): void;
|
|
61
|
+
};
|
|
50
62
|
export type SessionManagerRuntime = {
|
|
51
63
|
client: CliRelayClient;
|
|
52
64
|
pendingTransfers: Map<string, PendingTransfer>;
|
|
@@ -62,4 +74,6 @@ export type SessionManagerRuntime = {
|
|
|
62
74
|
chatQueue: ChatQueueService | null;
|
|
63
75
|
activityPublisher?: SessionActivityPublisher | null;
|
|
64
76
|
upgradePolicyController?: UpgradePolicyController | null;
|
|
77
|
+
revokeManagedRoomContextToken?: ((token: string) => void) | null;
|
|
78
|
+
managedRoomExecutor?: ManagedRoomExecutor | null;
|
|
65
79
|
};
|
|
@@ -1,5 +1,71 @@
|
|
|
1
1
|
import type { AgentType, ManagerConfig, SessionMode } from './session.ts';
|
|
2
2
|
import type { AgentRepairAction, AgentRuntimeAvailability, RoomActivationMode } from './room.ts';
|
|
3
|
+
export declare const MANAGED_AGENT_PERMISSION_POLICY_VERSION = 1;
|
|
4
|
+
export type ManagedAgentFileAccess = 'workspace-read' | 'workspace-write' | 'host-full';
|
|
5
|
+
export type ManagedAgentExternalCapabilities = {
|
|
6
|
+
commandNetwork: boolean;
|
|
7
|
+
webSearch: boolean;
|
|
8
|
+
browser: boolean;
|
|
9
|
+
computerUse: boolean;
|
|
10
|
+
mcp: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type ManagedAgentPermissionPolicy = {
|
|
13
|
+
/** Monotonic AgentIdentity policy revision, owned by the server. */
|
|
14
|
+
version: number;
|
|
15
|
+
fileAccess: ManagedAgentFileAccess;
|
|
16
|
+
externalCapabilities: ManagedAgentExternalCapabilities;
|
|
17
|
+
};
|
|
18
|
+
export type ManagedAgentEnforcementLevel = 'hard' | 'partial' | 'unverified';
|
|
19
|
+
export type ManagedAgentRuntimeCapabilityMatrix = {
|
|
20
|
+
fileAccess: Record<ManagedAgentFileAccess, boolean>;
|
|
21
|
+
externalCapabilities: {
|
|
22
|
+
[Capability in keyof ManagedAgentExternalCapabilities]: {
|
|
23
|
+
allow: boolean;
|
|
24
|
+
deny: boolean;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export type ManagedAgentRuntimeProtection = {
|
|
29
|
+
level: ManagedAgentEnforcementLevel;
|
|
30
|
+
runtimeVersion: string;
|
|
31
|
+
platform: string;
|
|
32
|
+
/** Stable digest of the exact executable selected by the daemon. Absent on older daemons. */
|
|
33
|
+
executableFingerprint?: string;
|
|
34
|
+
unsupportedCapabilities: string[];
|
|
35
|
+
/** Directional native capability evidence; absent means the runtime is not trusted. */
|
|
36
|
+
capabilityMatrix?: ManagedAgentRuntimeCapabilityMatrix;
|
|
37
|
+
};
|
|
38
|
+
export type ManagedAgentEnforcement = ManagedAgentRuntimeProtection & {
|
|
39
|
+
effectivePolicy: ManagedAgentPermissionPolicy;
|
|
40
|
+
};
|
|
41
|
+
/** One-time request proving that the user accepted the currently displayed runtime risk. */
|
|
42
|
+
export type ManagedAgentPermissionConfirmationRequest = {
|
|
43
|
+
runtimeAdapter: AgentType;
|
|
44
|
+
runtimeVersion: string;
|
|
45
|
+
platform: string;
|
|
46
|
+
executableFingerprint: string;
|
|
47
|
+
hostFullAccess: boolean;
|
|
48
|
+
limitedProtection: boolean;
|
|
49
|
+
};
|
|
50
|
+
/** Server-owned confirmation bound to one policy revision and one exact local runtime. */
|
|
51
|
+
export type ManagedAgentPermissionConfirmation = ManagedAgentPermissionConfirmationRequest & {
|
|
52
|
+
policyVersion: number;
|
|
53
|
+
confirmedAt: string;
|
|
54
|
+
};
|
|
55
|
+
export declare const DEFAULT_MANAGED_AGENT_PERMISSION_POLICY: ManagedAgentPermissionPolicy;
|
|
56
|
+
/**
|
|
57
|
+
* Compatibility projection for payloads created before managed permissions existed.
|
|
58
|
+
* Missing policy data must never be presented as if a restrictive sandbox were active.
|
|
59
|
+
*/
|
|
60
|
+
export declare const LEGACY_MANAGED_AGENT_PERMISSION_POLICY: ManagedAgentPermissionPolicy;
|
|
61
|
+
/**
|
|
62
|
+
* Project the policy the runtime can actually guarantee. Missing directional
|
|
63
|
+
* evidence is treated as possible host-wide file and external access.
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveManagedAgentEffectivePermissionPolicy(requested: ManagedAgentPermissionPolicy, protection?: ManagedAgentRuntimeProtection | null): ManagedAgentPermissionPolicy;
|
|
66
|
+
/** Strictly parse an explicit policy. Invalid or incomplete values are not silently repaired. */
|
|
67
|
+
export declare function parseManagedAgentPermissionPolicy(value: unknown): ManagedAgentPermissionPolicy | null;
|
|
68
|
+
export declare function resolveManagedAgentPermissionPolicy(value: unknown, fallback?: 'default' | 'legacy'): ManagedAgentPermissionPolicy;
|
|
3
69
|
export type AgentWorkspaceRoomLink = {
|
|
4
70
|
bindingId: string;
|
|
5
71
|
status: string;
|
|
@@ -45,6 +111,10 @@ export type AgentWorkspace = {
|
|
|
45
111
|
modelId: string | null;
|
|
46
112
|
sessionMode: SessionMode;
|
|
47
113
|
managerConfig: ManagerConfig | null;
|
|
114
|
+
/** Optional only for wire/cache compatibility. New servers always return an explicit policy. */
|
|
115
|
+
permissionPolicy?: ManagedAgentPermissionPolicy;
|
|
116
|
+
/** Null means this policy/runtime has no persisted high-risk or limited-protection approval. */
|
|
117
|
+
permissionConfirmation?: ManagedAgentPermissionConfirmation | null;
|
|
48
118
|
createdAt: string;
|
|
49
119
|
updatedAt: string;
|
|
50
120
|
sessions: AgentWorkspaceSession[];
|
|
@@ -1,2 +1,112 @@
|
|
|
1
1
|
// @arch docs/features/agent-workspaces.md
|
|
2
|
-
|
|
2
|
+
// @arch docs/features/managed-agent-restricted-runtime.md
|
|
3
|
+
export const MANAGED_AGENT_PERMISSION_POLICY_VERSION = 1;
|
|
4
|
+
export const DEFAULT_MANAGED_AGENT_PERMISSION_POLICY = {
|
|
5
|
+
version: MANAGED_AGENT_PERMISSION_POLICY_VERSION,
|
|
6
|
+
fileAccess: 'workspace-write',
|
|
7
|
+
externalCapabilities: {
|
|
8
|
+
commandNetwork: false,
|
|
9
|
+
webSearch: false,
|
|
10
|
+
browser: false,
|
|
11
|
+
computerUse: false,
|
|
12
|
+
mcp: false,
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Compatibility projection for payloads created before managed permissions existed.
|
|
17
|
+
* Missing policy data must never be presented as if a restrictive sandbox were active.
|
|
18
|
+
*/
|
|
19
|
+
export const LEGACY_MANAGED_AGENT_PERMISSION_POLICY = {
|
|
20
|
+
version: MANAGED_AGENT_PERMISSION_POLICY_VERSION,
|
|
21
|
+
fileAccess: 'host-full',
|
|
22
|
+
externalCapabilities: {
|
|
23
|
+
commandNetwork: true,
|
|
24
|
+
webSearch: true,
|
|
25
|
+
browser: true,
|
|
26
|
+
computerUse: true,
|
|
27
|
+
mcp: true,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Project the policy the runtime can actually guarantee. Missing directional
|
|
32
|
+
* evidence is treated as possible host-wide file and external access.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveManagedAgentEffectivePermissionPolicy(requested, protection) {
|
|
35
|
+
const matrix = protection?.capabilityMatrix;
|
|
36
|
+
if (!matrix) {
|
|
37
|
+
return {
|
|
38
|
+
version: requested.version,
|
|
39
|
+
fileAccess: 'host-full',
|
|
40
|
+
externalCapabilities: {
|
|
41
|
+
commandNetwork: true,
|
|
42
|
+
webSearch: true,
|
|
43
|
+
browser: true,
|
|
44
|
+
computerUse: true,
|
|
45
|
+
mcp: true,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const fileAccess = matrix.fileAccess[requested.fileAccess]
|
|
50
|
+
? requested.fileAccess
|
|
51
|
+
: matrix.fileAccess['host-full']
|
|
52
|
+
? 'host-full'
|
|
53
|
+
: requested.fileAccess;
|
|
54
|
+
const externalCapabilities = { ...requested.externalCapabilities };
|
|
55
|
+
for (const capability of Object.keys(externalCapabilities)) {
|
|
56
|
+
if (!externalCapabilities[capability] && !matrix.externalCapabilities[capability].deny) {
|
|
57
|
+
externalCapabilities[capability] = true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { version: requested.version, fileAccess, externalCapabilities };
|
|
61
|
+
}
|
|
62
|
+
function copyManagedAgentPermissionPolicy(policy) {
|
|
63
|
+
return {
|
|
64
|
+
...policy,
|
|
65
|
+
externalCapabilities: { ...policy.externalCapabilities },
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Strictly parse an explicit policy. Invalid or incomplete values are not silently repaired. */
|
|
69
|
+
export function parseManagedAgentPermissionPolicy(value) {
|
|
70
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
71
|
+
return null;
|
|
72
|
+
const policy = value;
|
|
73
|
+
if (!Number.isSafeInteger(policy.version) || Number(policy.version) < 1)
|
|
74
|
+
return null;
|
|
75
|
+
if (policy.fileAccess !== 'workspace-read' &&
|
|
76
|
+
policy.fileAccess !== 'workspace-write' &&
|
|
77
|
+
policy.fileAccess !== 'host-full') {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
if (!policy.externalCapabilities ||
|
|
81
|
+
typeof policy.externalCapabilities !== 'object' ||
|
|
82
|
+
Array.isArray(policy.externalCapabilities)) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const capabilities = policy.externalCapabilities;
|
|
86
|
+
if (typeof capabilities.commandNetwork !== 'boolean' ||
|
|
87
|
+
typeof capabilities.webSearch !== 'boolean' ||
|
|
88
|
+
typeof capabilities.browser !== 'boolean' ||
|
|
89
|
+
typeof capabilities.computerUse !== 'boolean' ||
|
|
90
|
+
typeof capabilities.mcp !== 'boolean') {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
version: Number(policy.version),
|
|
95
|
+
fileAccess: policy.fileAccess,
|
|
96
|
+
externalCapabilities: {
|
|
97
|
+
commandNetwork: capabilities.commandNetwork,
|
|
98
|
+
webSearch: capabilities.webSearch,
|
|
99
|
+
browser: capabilities.browser,
|
|
100
|
+
computerUse: capabilities.computerUse,
|
|
101
|
+
mcp: capabilities.mcp,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export function resolveManagedAgentPermissionPolicy(value, fallback = 'legacy') {
|
|
106
|
+
const parsed = parseManagedAgentPermissionPolicy(value);
|
|
107
|
+
if (parsed)
|
|
108
|
+
return parsed;
|
|
109
|
+
return copyManagedAgentPermissionPolicy(fallback === 'default'
|
|
110
|
+
? DEFAULT_MANAGED_AGENT_PERMISSION_POLICY
|
|
111
|
+
: LEGACY_MANAGED_AGENT_PERMISSION_POLICY);
|
|
112
|
+
}
|
|
@@ -24,6 +24,8 @@ export type Machine = {
|
|
|
24
24
|
sessionSyncCapabilities?: import('./session-sync.ts').SessionSyncCapability[];
|
|
25
25
|
/** Auto-upgrade policy configured on this machine */
|
|
26
26
|
autoUpgrade?: AutoUpgradePolicy;
|
|
27
|
+
/** Set only after the daemon durably disables remote access on the machine. */
|
|
28
|
+
remoteAccessDisabledAt?: string | null;
|
|
27
29
|
/** Ownership role — absent means owner (backward-compatible) */
|
|
28
30
|
role?: 'owner' | 'granted';
|
|
29
31
|
/** Non-null only when role === 'granted' */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type ReqMethod = 'chat.send' | 'chat.enqueue' | 'chat.queue.get' | 'chat.queue.edit' | 'chat.queue.delete' | 'chat.abort' | 'session.resume' | 'session.history' | 'session.history.open' | 'session.history.sync' | 'session.history.page' | 'session.history.close' | 'session.index.update' | 'session.deletions.sync' | 'session.deletions.ack' | 'session.message.status' | 'voice.session.body.window' | 'session.refresh' | 'session.tool.detail' | 'session.title.set' | 'session.title.generate' | 'fs.ls' | 'fs.read' | 'fs.write' | 'fs.export.markdown-pdf' | 'fs.export.markdown-pdf.setup' | 'fs.archive.zip' | 'fs.transfer.start' | 'fs.transfer.chunk' | 'fs.transfer.finish' | 'fs.transfer.abort' | 'fs.rename' | 'skill.list' | 'skill.install' | 'skill.doctor' | 'skill.setup' | 'skill.use' | 'region.probe' | 'region.switch' | 'upgrade.start' | 'upgrade.status' | 'upgrade.set-policy' | 'agents.refresh' | 'models.refresh' | 'agent.config.get' | 'agent.config.upsert' | 'agent.config.clear' | 'agent.config.test' | 'agent.capabilities.list' | 'manager.channel.get' | 'manager.channel.upsert' | 'session.wechat-rpa.channel.get' | 'session.wechat-rpa.channel.upsert' | 'session.wechat-rpa.channel.sync' | 'session.wechat-rpa.outbound.cancel' | 'manager.wechat-rpa.channel.get' | 'manager.wechat-rpa.channel.upsert' | 'manager.wechat-rpa.channel.sync' | 'manager.wechat-rpa.outbound.cancel' | 'wechat-rpa.helper.runtime' | 'wecom.send' | 'external.send' | 'room.sync' | 'room.send' | 'room.bind' | 'room.agent.bind-managed' | 'room.agent.restart-managed' | 'room.unbind';
|
|
1
|
+
export type ReqMethod = 'chat.send' | 'chat.enqueue' | 'chat.queue.get' | 'chat.queue.edit' | 'chat.queue.delete' | 'chat.abort' | 'session.resume' | 'session.history' | 'session.history.open' | 'session.history.sync' | 'session.history.page' | 'session.history.close' | 'session.body.replica.subscribe' | 'session.body.replica.unsubscribe' | 'session.index.update' | 'session.deletions.sync' | 'session.deletions.ack' | 'session.message.status' | 'voice.session.body.window' | 'session.refresh' | 'session.tool.detail' | 'session.title.set' | 'session.title.generate' | 'fs.ls' | 'fs.read' | 'fs.write' | 'fs.export.markdown-pdf' | 'fs.export.markdown-pdf.setup' | 'fs.archive.zip' | 'fs.transfer.start' | 'fs.transfer.chunk' | 'fs.transfer.finish' | 'fs.transfer.abort' | 'fs.rename' | 'skill.list' | 'skill.install' | 'skill.doctor' | 'skill.setup' | 'skill.use' | 'region.probe' | 'region.switch' | 'upgrade.start' | 'upgrade.status' | 'upgrade.set-policy' | 'machine.remote-access.pause' | 'agents.refresh' | 'models.refresh' | 'agent.config.get' | 'agent.config.upsert' | 'agent.config.clear' | 'agent.config.test' | 'agent.capabilities.list' | 'manager.channel.get' | 'manager.channel.upsert' | 'session.wechat-rpa.channel.get' | 'session.wechat-rpa.channel.upsert' | 'session.wechat-rpa.channel.sync' | 'session.wechat-rpa.outbound.cancel' | 'manager.wechat-rpa.channel.get' | 'manager.wechat-rpa.channel.upsert' | 'manager.wechat-rpa.channel.sync' | 'manager.wechat-rpa.outbound.cancel' | 'wechat-rpa.helper.runtime' | 'wecom.send' | 'external.send' | 'room.sync' | 'room.send' | 'room.bind' | 'room.agent.bind-managed' | 'room.agent.restart-managed' | 'room.unbind';
|