shennian 0.3.72 → 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 +126 -61
- 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 +32 -21
- package/dist/src/room/activation-service.js +5 -5
- 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-context.d.ts +2 -0
- package/dist/src/room/managed-context.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/mirror-sync-service.js +1 -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/room/session-registry.d.ts +4 -0
- package/dist/src/room/session-registry.js +2 -2
- 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 +15 -19
- package/dist/src/session/manager.js +1 -1
- package/dist/src/session/queue.d.ts +26 -3
- package/dist/src/session/queue.js +11 -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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import u from"node:fs/promises";import o from"node:path";import{parseManagedAgentPermissionPolicy as m,resolveManagedAgentEffectivePermissionPolicy as y}from"@shennian/wire";import{getManagedAgentBaseEnvKeys as _,getManagedAgentProviderEnvKeys as f,MANAGED_AGENT_SESSION_ENV_KEYS as d}from"../agent-env.js";import{collectPolicyEnforcementGaps as b,resolveManagedAgentRuntimeProtection as C,resolveManagedAgentSecurityProfile as k}from"./managed-agent-runtime.js";import{resolveProtectedShennianDirectories as w}from"./managed-agent-credential-paths.js";function W(e){return{...e.enforcement,effectivePolicy:{version:e.adapterPolicy.version,fileAccess:e.adapterPolicy.fileAccess,externalCapabilities:{...e.adapterPolicy.externalCapabilities}}}}async function R(e){const r=m(e.policy);if(!r)return{ok:!1,errorCode:"managed_agent_policy_invalid"};if(r.fileAccess==="host-full"&&!e.hostFullAccessConfirmed)return{ok:!1,errorCode:"managed_agent_host_full_confirmation_required"};const t=await A(e.workDir,e.resolveWorkDir);if(!t.ok)return t;if(await E(t.workDir))return{ok:!1,errorCode:"managed_agent_workdir_invalid"};const a=P(e),i=C(a);if(i.level!=="hard")return{ok:!1,errorCode:"managed_agent_capability_unavailable",unsupportedCapabilities:c(["runtime.hardIsolation",...i.unsupportedCapabilities])};const l=k(a),n=b(r,l);if(n.unavailableCapabilities.length>0)return{ok:!1,errorCode:"managed_agent_capability_unavailable",unsupportedCapabilities:n.unavailableCapabilities};const s=c([...i.unsupportedCapabilities,...n.overgrantedCapabilities]);if(n.overgrantedCapabilities.length>0)return{ok:!1,errorCode:"managed_agent_capability_unavailable",unsupportedCapabilities:s};const g=y(r,i),v={level:i.level,effectivePolicy:g,unsupportedCapabilities:s,runtimeVersion:i.runtimeVersion,platform:i.platform,...i.executableFingerprint?{executableFingerprint:i.executableFingerprint}:{}};return{ok:!0,value:{requestedPolicy:x(r),enforcement:v,adapterPolicy:{version:r.version,workDir:t.workDir,fileAccess:r.fileAccess,externalCapabilities:{...r.externalCapabilities},denySensitiveFiles:!0,failClosed:!0},environmentPolicy:{inheritDaemonEnv:!1,loadLoginShell:!1,providerEnvKeys:[...f(e.agentType)],sessionEnvKeys:[...d],shellSubprocess:{inherit:"none",allowedEnvKeys:c([..._(e.platform),...d]),deniedEnvKeys:[...f(e.agentType)]}}}}}function P(e){return{agentType:e.agentType,runtimeVersion:e.runtimeVersion,platform:e.platform,isWsl:e.isWsl,executableFingerprint:e.executableFingerprint,requireExecutableFingerprint:e.requireExecutableFingerprint,securityDeclaration:e.securityDeclaration}}async function A(e,r){const t=e.trim();if(!t||!o.isAbsolute(t))return{ok:!1,errorCode:"managed_agent_workdir_invalid"};try{const a=await(r??h)(t);return!a||!o.isAbsolute(a)?{ok:!1,errorCode:"managed_agent_workdir_invalid"}:{ok:!0,workDir:o.normalize(a)}}catch{return{ok:!1,errorCode:"managed_agent_workdir_unavailable"}}}async function h(e){const r=await u.realpath(e);if(!(await u.stat(r)).isDirectory())throw new Error("managed_agent_workdir_not_directory");return r}async function E(e){return w().some(t=>D(e,t,process.platform))}function D(e,r,t){const a=n=>{const s=o.resolve(n);return t==="win32"?s.toLowerCase():s},i=a(e),l=a(r);return p(i,l)||p(l,i)}function p(e,r){const t=o.relative(e,r);return t===""||!t.startsWith("..")&&!o.isAbsolute(t)}function x(e){return{...e,externalCapabilities:{...e.externalCapabilities}}}function c(e){return[...new Set(e)]}export{R as compileManagedAgentPolicy,W as resolveManagedAgentAdapterEnforcement};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AgentType, ManagedAgentEnforcementLevel, ManagedAgentExternalCapabilities, ManagedAgentFileAccess, ManagedAgentPermissionPolicy, ManagedAgentRuntimeProtection } from '@shennian/wire';
|
|
2
|
+
export type ManagedAgentRuntimePlatform = 'darwin' | 'linux' | 'win32' | 'wsl' | 'unsupported';
|
|
3
|
+
export type ManagedAgentExternalCapability = keyof ManagedAgentExternalCapabilities;
|
|
4
|
+
export type ManagedAgentRuntimeSecurityProfile = {
|
|
5
|
+
level: Exclude<ManagedAgentEnforcementLevel, 'unverified'>;
|
|
6
|
+
fileAccess: Record<ManagedAgentFileAccess, boolean>;
|
|
7
|
+
externalCapabilities: Record<ManagedAgentExternalCapability, {
|
|
8
|
+
allow: boolean;
|
|
9
|
+
deny: boolean;
|
|
10
|
+
}>;
|
|
11
|
+
/** Known gaps that are not directly representable by the product policy. */
|
|
12
|
+
knownGaps?: readonly string[];
|
|
13
|
+
};
|
|
14
|
+
export type ManagedAgentRuntimeSecurityDeclaration = {
|
|
15
|
+
/** Lowest runtime version covered by this adapter's conformance evidence. */
|
|
16
|
+
minimumRuntimeVersion?: string;
|
|
17
|
+
/** First runtime version not covered by this declaration (normally a new major). */
|
|
18
|
+
maximumRuntimeVersionExclusive?: string;
|
|
19
|
+
platforms: Partial<Record<ManagedAgentRuntimePlatform, ManagedAgentRuntimeSecurityProfile>>;
|
|
20
|
+
};
|
|
21
|
+
export type ManagedAgentRuntimeResolutionInput = {
|
|
22
|
+
agentType: AgentType;
|
|
23
|
+
runtimeVersion?: string;
|
|
24
|
+
platform?: NodeJS.Platform;
|
|
25
|
+
isWsl?: boolean;
|
|
26
|
+
executableFingerprint?: string;
|
|
27
|
+
/** Current managed Agent entry points set this to prevent identity-less hard claims. */
|
|
28
|
+
requireExecutableFingerprint?: boolean;
|
|
29
|
+
/** Primarily supplied by the Agent adapter that owns the native security mapping. */
|
|
30
|
+
securityDeclaration?: ManagedAgentRuntimeSecurityDeclaration;
|
|
31
|
+
};
|
|
32
|
+
export declare const ALL_MANAGED_AGENT_POLICY_CAPABILITIES: readonly string[];
|
|
33
|
+
/**
|
|
34
|
+
* Registers evidence-backed native enforcement owned by an Agent adapter.
|
|
35
|
+
* Merely being installed or recognized must never register a declaration.
|
|
36
|
+
*/
|
|
37
|
+
export declare function registerManagedAgentRuntimeSecurity(agentType: AgentType, declaration: ManagedAgentRuntimeSecurityDeclaration): void;
|
|
38
|
+
export declare function unregisterManagedAgentRuntimeSecurity(agentType: AgentType): void;
|
|
39
|
+
export declare function resolveManagedAgentRuntimeProtection(input: ManagedAgentRuntimeResolutionInput): ManagedAgentRuntimeProtection;
|
|
40
|
+
export declare function resolveManagedAgentSecurityProfile(input: ManagedAgentRuntimeResolutionInput): ManagedAgentRuntimeSecurityProfile | null;
|
|
41
|
+
export declare function collectPolicyEnforcementGaps(policy: ManagedAgentPermissionPolicy, profile: ManagedAgentRuntimeSecurityProfile | null): {
|
|
42
|
+
overgrantedCapabilities: string[];
|
|
43
|
+
unavailableCapabilities: string[];
|
|
44
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import g from"node:fs";import p from"node:os";const l={"workspace-read":"fileAccess.workspaceRead","workspace-write":"fileAccess.workspaceWrite","host-full":"fileAccess.hostFull"},o={commandNetwork:"externalCapabilities.commandNetwork",webSearch:"externalCapabilities.webSearch",browser:"externalCapabilities.browser",computerUse:"externalCapabilities.computerUse",mcp:"externalCapabilities.mcp"},b=[...Object.values(l),...Object.values(o)],c=new Map;function h(e,t){c.set(e,t)}function E(e){c.delete(e)}function R(e){const t=e.runtimeVersion?.trim()||"unknown",i=x(e),r=e.securityDeclaration??c.get(e.agentType),n=r?.platforms[i];return!r||!n||t==="unknown"||e.requireExecutableFingerprint&&!e.executableFingerprint||r.minimumRuntimeVersion&&!u(t,r.minimumRuntimeVersion)||r.maximumRuntimeVersionExclusive&&u(t,r.maximumRuntimeVersionExclusive)?{level:"unverified",runtimeVersion:t,platform:i,...e.executableFingerprint?{executableFingerprint:e.executableFingerprint}:{},unsupportedCapabilities:[...b]}:{level:n.level,runtimeVersion:t,platform:i,...e.executableFingerprint?{executableFingerprint:e.executableFingerprint}:{},unsupportedCapabilities:A(n),capabilityMatrix:{fileAccess:{...n.fileAccess},externalCapabilities:Object.fromEntries(Object.entries(n.externalCapabilities).map(([s,a])=>[s,{...a}]))}}}function S(e){const t=e.runtimeVersion?.trim();if(!t)return null;const i=e.securityDeclaration??c.get(e.agentType);return!i||i.minimumRuntimeVersion&&!u(t,i.minimumRuntimeVersion)||i.maximumRuntimeVersionExclusive&&u(t,i.maximumRuntimeVersionExclusive)?null:i.platforms[x(e)]??null}function P(e,t){if(!t)return{overgrantedCapabilities:[...b],unavailableCapabilities:[]};const i=[...t.knownGaps??[]],r=[];t.fileAccess[e.fileAccess]||(e.fileAccess!=="host-full"&&t.fileAccess["host-full"]?i.push(l[e.fileAccess]):r.push(l[e.fileAccess]));for(const[n,s]of Object.entries(e.externalCapabilities)){const a=t.externalCapabilities[n];s&&!a.allow&&r.push(o[n]),!s&&!a.deny&&i.push(o[n])}return{overgrantedCapabilities:m(i),unavailableCapabilities:m(r)}}function A(e){const t=[...e.knownGaps??[]];for(const[i,r]of Object.entries(e.fileAccess))r||t.push(l[i]);for(const[i,r]of Object.entries(e.externalCapabilities))(!r.allow||!r.deny)&&t.push(o[i]);return m(t)}function x(e){const t=e.platform??process.platform;return t==="linux"&&(e.isWsl??C())?"wsl":t==="darwin"||t==="linux"||t==="win32"?t:"unsupported"}function C(){if(process.platform!=="linux")return!1;if(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP)return!0;try{return/microsoft|wsl/i.test(`${p.release()} ${g.readFileSync("/proc/version","utf-8")}`)}catch{return/microsoft|wsl/i.test(p.release())}}function u(e,t){const i=d(e),r=d(t);if(!i||!r)return!1;const n=Math.max(i.length,r.length);for(let s=0;s<n;s+=1){const a=i[s]??0,f=r[s]??0;if(a!==f)return a>f}return!0}function d(e){const t=e.trim().match(/(?:^|\s|v)(\d+(?:\.\d+){0,3})(?:[-+\s]|$)/i);return t?.[1]?t[1].split(".").map(Number):null}function m(e){return[...new Set(e)]}export{b as ALL_MANAGED_AGENT_POLICY_CAPABILITIES,P as collectPolicyEnforcementGaps,h as registerManagedAgentRuntimeSecurity,R as resolveManagedAgentRuntimeProtection,S as resolveManagedAgentSecurityProfile,E as unregisterManagedAgentRuntimeSecurity};
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { AgentSendContext } from '../../agents/adapter.js';
|
|
2
2
|
import { type ReqFrame, type AgentType, type SessionMode } from '@shennian/wire';
|
|
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';
|
|
6
|
+
export type ChatSendExecutionContext = AgentSendContext & {
|
|
7
|
+
/** Local-only snapshot produced by RoomActivationService; never accepted from Relay input. */
|
|
8
|
+
managedAgentPolicy: CompiledManagedAgentPolicy;
|
|
9
|
+
managedRoomPromptSnapshot: ManagedRoomPromptSnapshot;
|
|
10
|
+
};
|
|
4
11
|
export declare function sendSessionUpdateEvent(runtime: SessionManagerRuntime, input: {
|
|
5
12
|
sessionId: string;
|
|
6
13
|
agentType: AgentType;
|
|
@@ -12,5 +19,5 @@ export declare function sendSessionUpdateEvent(runtime: SessionManagerRuntime, i
|
|
|
12
19
|
managerDefaultWorkerAgentType?: AgentType | null;
|
|
13
20
|
managerDefaultWorkerModelId?: string | null;
|
|
14
21
|
}): void;
|
|
15
|
-
export declare function handleChatSend(runtime: SessionManagerRuntime, req: ReqFrame, context?:
|
|
22
|
+
export declare function handleChatSend(runtime: SessionManagerRuntime, req: ReqFrame, context?: ChatSendExecutionContext): Promise<void>;
|
|
16
23
|
export declare function handleChatAbort(runtime: SessionManagerRuntime, req: ReqFrame): Promise<void>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
`),s=n>0?Math.min(n,80):Math.min(e.length,80);return e.slice(0,s)}function X(e,n,s){return`${e}:${le("sha256").update(`${n}\0${s??""}`).digest("hex")}`}function xe(e,n,s={}){if(e.state==="tool-call"||e.state==="tool-result"){const a={runId:e.runId,sourceSeq:e.seq};return{state:e.state,runId:e.runId,seq:e.seq,sessionId:n,detailRef:a,status:e.state==="tool-call"?"running":"completed",detailAvailable:!0,...e.durationMs!==void 0?{durationMs:e.durationMs}:{},...e.name?{name:e.name}:{},...e.source?{source:e.source}:{},...e.agentSessionId?{agentSessionId:e.agentSessionId}:{},...s}}return{...e,sessionId:n,...s}}const Re=3e4;function ke(e){return e.state==="heartbeat"?e.runPhase??null:e.state==="tool-call"||e.state==="tool-result"?"tool_running":e.state==="approval-pending"?"waiting_approval":e.state==="delta"?e.thinking?"thinking":"streaming_text":e.state==="init"||e.state==="start"?"thinking":null}function Y(e,n){return`Agent send failed: ${n instanceof Error?n.message:String(n)}`}function Z(e,n){return e!=="manager"?e:n==="claude"?"claude":"codex"}function Me(e,n){P({sessionId:n.sessionId,agentType:n.agentType,sessionMode:n.sessionMode,managerConfig:n.managerConfig,workDir:n.workDir,agentSessionId:n.agentSessionId??null,modelId:n.modelId??null,managerDefaultWorkerAgentType:n.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:n.managerDefaultWorkerModelId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:n.sessionId,agentType:n.agentType,sessionMode:n.sessionMode,managerConfig:n.managerConfig,agentSessionId:n.agentSessionId??null,modelId:n.modelId??null,workDir:n.workDir,status:"active",externalChannel:null}}})}function ee(e){return he(e)}function te(e,n,s){return{}}function ne(e,n,s,a,i,c){e.configure?.({sessionId:n,externalChannel:a??null,env:{...ee(s),...te(n,a,i),...c?{SHENNIAN_MANAGED_ROOM_CONTEXT:c}:{}}})}function Te(e,n,s){if(e!=="claude"||!s)return n;const a=n.trim();return!!a&&a.startsWith("-")&&!a.includes("/")?pe(s)??n:n}function be(e,n,s,a){let i=null;function c(t,r={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:xe(t,n,r),seq:t.seq,id:`agent-evt-${t.runId}-${t.seq}`})}function I(t){t?.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null)}function S(t){if(!t.currentRunId||!t.currentRunPhase)return;const r=t.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:n,runId:t.currentRunId,seq:r,runPhase:t.currentRunPhase},seq:r,id:`agent-heartbeat-${t.currentRunId}-${r}-${Date.now()}`})}function x(t){!t||t.heartbeatTimer||(t.heartbeatTimer=setInterval(()=>{S(t)},Re),t.heartbeatTimer.unref?.())}function l(t){const r=t?.pendingTextEvent;!t||!r||!r.text||(q(n,{id:`agent-${r.runId}-${r.seq}`,sessionId:n,role:"agent",ts:Date.now(),payload:r.text}),E(e.client),c({state:"delta",runId:r.runId,seq:r.seq,text:r.text,thinking:r.thinking||void 0}),t.pendingTextEvent=null)}a.on("agentEvent",t=>{const r=e.sessions.get(n),T=ke(t),R=t.state==="final"||t.state==="error"||t.state==="aborted";if(r&&(r.nextEventSeq=t.seq+1,R||(r.currentRunId=t.runId),!R&&T&&(r.currentRunPhase=T,x(r)),t.agentSessionId&&(r.agentSessionId=t.agentSessionId)),e.managerRuntime?.noteAgentEvent(n,t),t.state==="delta"&&t.thinking)return;t.state!=="delta"&&b({level:"info",sessionId:n,wsEvent:`agent.${t.state}`,wsDirection:"out",metadata:{runId:t.runId,seq:t.seq,agentType:s}});let A=!1;if(t.state==="tool-call"||t.state==="tool-result"){const m=`agent-${t.runId}-${t.seq}`;we({sessionId:n,toolCallId:m,detailRevision:t.seq,ts:Date.now(),name:t.name??"tool",...t.args!==void 0?{args:t.args}:{},...t.result!==void 0?{result:t.result}:{},status:t.state==="tool-call"?"running":"completed",...t.durationMs!==void 0?{durationMs:t.durationMs}:{},detailRef:{runId:t.runId,sourceSeq:t.seq}}),q(n,{id:m,sessionId:n,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:t.state==="tool-call"?"tool_use":"tool_result",name:t.name,toolCallId:m,status:t.state==="tool-call"?"running":"completed",...t.durationMs!==void 0?{durationMs:t.durationMs}:{},detailRef:{runId:t.runId,sourceSeq:t.seq},detailRevision:t.seq,detailAvailable:!0,resultOmitted:t.state==="tool-result"})}),A=!0}else t.state==="approval-pending"?(q(n,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:n,role:"agent",ts:Date.now(),payload:ue(t.approval)}),A=!0,r&&P({sessionId:n,agentType:s,workDir:r.workDir,agentSessionId:r.agentSessionId,attentionId:X("approval",t.runId,t.seq)})):(t.state==="error"||t.state==="aborted")&&t.message&&(q(n,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:n,role:"agent",ts:Date.now(),payload:t.message}),A=!0);A&&E(e.client);const k=`${n}:${t.runId}`;if(t.state==="delta"&&!t.thinking&&t.text&&e.runTextAcc.set(k,(e.runTextAcc.get(k)??"")+t.text),t.agentSessionId&&t.agentSessionId!==i&&(i=t.agentSessionId,e.nativeFusion?.noteManagedSourceSession(n,Z(s,t.source),t.agentSessionId),r&&P({sessionId:n,agentType:s,workDir:r.workDir,agentSessionId:t.agentSessionId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:n,agentType:s,agentSessionId:t.agentSessionId}}})),t.state==="delta"){const m=t.text??"";if(!m)return;r?.pendingTextEvent&&r.pendingTextEvent.runId!==t.runId&&l(r),r&&!r.pendingTextEvent&&(r.pendingTextEvent={runId:t.runId,seq:t.seq,text:"",thinking:!1}),r?.pendingTextEvent&&(r.pendingTextEvent.text+=m,r.pendingTextEvent.seq=t.seq);return}let $={};if(t.state==="final"){l(r);const m=e.runTextAcc.get(k)??"";m&&($={messageSummary:Ee(m)}),e.runTextAcc.delete(k)}else t.state==="error"||t.state==="aborted"?(l(r),e.runTextAcc.delete(k)):(t.state==="tool-call"||t.state==="tool-result"||t.state==="approval-pending")&&l(r);R&&r?.currentRunId===t.runId&&(r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,I(r),e.chatQueue?.noteTerminal(n),P({sessionId:n,agentType:s,workDir:r.workDir,agentSessionId:r.agentSessionId,status:t.state==="final"?"completed":"failed",attentionId:X("terminal",t.runId),lastActivityAt:new Date().toISOString()}),E(e.client)),c(t,$)}),a.on("error",t=>{console.error(`[chat.send] adapter error sessionId=${n} agentType=${s}: ${t.message}`),e.sessions.delete(n),e.chatQueue?.noteTerminal(n),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:n,message:t.message,runId:"",seq:0}})})}function qe(e,n){if(e.processedReqIds.add(n),e.processedReqIds.size>1e3){const s=e.processedReqIds.values().next().value;e.processedReqIds.delete(s)}}async function v(e){e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{})}function ae(e,n,s,a,i,c,I){return I?e.send(n,s,a,i,c,I):c?.trim()?e.send(n,s,a,i,c):i?.length?e.send(n,s,a,i):a?e.send(n,s,a):e.send(n,s)}async function De(e,n,s,a,i,c,I,S){e.evictIdleSessions();const x=ce(s);if(!x)throw new Error(`Unsupported agent: ${s}`);const l=a==="manager"&&s!=="manager"?new fe(x,s==="claude"?"claude":"codex"):x;ne(l,n,s,I,S),await l.start(n,i,c);const t={adapter:l,workDir:i,agentType:s,sessionMode:a,agentSessionId:c??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:I??null,externalReplyTarget:S??null,externalChannelEnv:{...ee(s),...te(n,I,S)}};return e.sessions.set(n,t),be(e,n,s,l),t}function $e(e,n){const s=e.sessions.get(n),a=s?.currentRunId;if(!s||!a)return;const i=s.nextEventSeq;e.runTextAcc.delete(`${n}:${a}`),s.pendingTextEvent=null,s.currentRunId=null,s.currentRunPhase=null,s.nextEventSeq=0,s.heartbeatTimer&&(clearInterval(s.heartbeatTimer),s.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:n,runId:a,seq:i},seq:i,id:`agent-evt-${a}-${i}`}),e.chatQueue?.noteTerminal(n)}async function je(e,n,s){if(e.processedReqIds.has(n.id)){e.client.sendRes({type:"res",id:n.id,ok:!0});return}qe(e,n.id);const{sessionId:a,text:i,agentType:c,sessionMode:I,managerConfig:S,workDir:x,agentSessionId:l,modelId:t,systemPrompt:r,managerDefaultWorkerAgentType:T,managerDefaultWorkerModelId:R,reasoningEffort:A,clientMessageId:k,sessionListProjection:$,waitForDispatch:m,responseId:se,managedRoomContextToken:C,suppressExternalOwnerEcho:re}=n.params,h=se||n.id;Ie($);const W=null,H="";if(!a||!i){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:"sessionId and text are required"});return}const g=c,D=g==="manager"||I==="manager"?"manager":"standard";D==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(a,T??null,R??null);const O=(g==="claude"||g==="codex"||g==="workbuddy")&&typeof A=="string"&&A.trim()?A.trim():void 0,M=e.resolvePath(Te(g,x||j.homedir(),l)||j.homedir()),oe=M,_=ye(n.params.attachments);_?.length&&Se(M,_);const y={text:i,attachments:_,localized:!1};let d=e.sessions.get(a);if(d){if(d.lastActiveAt=Date.now(),d.agentType!==g||(d.sessionMode??(d.agentType==="manager"?"manager":"standard"))!==D||d.workDir!==M||JSON.stringify(d.externalChannel??null)!==JSON.stringify(W??null)){e.sessions.delete(a);try{await v(d)}catch{e.processedReqIds.delete(n.id)}d=void 0}else if(l&&d.agentSessionId!==l)try{await d.adapter.resume(l),d.agentSessionId=l}catch{e.sessions.delete(a);try{await v(d)}catch{e.processedReqIds.delete(n.id)}d=void 0}}if(!d)try{d=await De(e,a,g,D,M,l,W,H)}catch(o){const f=o instanceof Error&&o.message.startsWith("Unsupported agent:")?o.message:`Failed to start ${c}: ${o instanceof Error?o.message:String(o)}`;console.error(`[chat.send] start failed reqId=${n.id} sessionId=${a} agentType=${c} workDir=${M} agentSessionId=${l??""}: ${f}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:a,message:f,runId:"",seq:0}}),e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:f});return}C&&/^mrc_[A-Za-z0-9_-]{43}$/.test(C)&&ne(d.adapter,a,g,W,H,C);const p={id:k??`user-${n.id}`,sessionId:a,role:"user",ts:Date.now(),payload:ge(y.text,y.attachments)},w=me(a,p.id),L=w.state==="queued";if(w.state!=="not_found"&&!L){e.client.sendRes({type:"res",id:h,ok:!0,payload:w});return}b({level:"info",sessionId:a,wsEvent:"chat.send.start",metadata:{reqId:n.id,agentType:g,sessionMode:D,managerConfig:S??null,modelId:t,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:R??null,reasoningEffort:O}});const F=(o,f)=>({...G(K()),sessionId:a,clientMessageId:p.id,messageId:p.id,deliveryState:o,bodyRevision:f,committedAt:new Date(p.ts).toISOString()}),U=o=>{e.nativeFusion?.registerManagedSend({sessionId:a,agentType:g,sourceAgentType:Z(g,t),canonicalMessageId:p.id,sourceSessionKey:d.agentSessionId??l??null,text:y.text,managedEchoPolicy:o?.deliveryMode==="external_owner_queue"&&!re?"import_following":"suppress_following",externalRunId:o?.deliveryMode==="external_owner_queue"?`codex-external-${o.queuedSubmissionId??p.id}`:null})},B=()=>{if(U(),L)return{...G(K()),sessionId:a,clientMessageId:w.clientMessageId,messageId:w.messageId,deliveryState:w.deliveryState,bodyRevision:w.bodyRevision,committedAt:w.committedAt};try{Me(e,{sessionId:a,agentType:g,sessionMode:D,managerConfig:S??null,workDir:oe,agentSessionId:d.agentSessionId??l??null,modelId:t,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:R??null});const o=q(a,p,"committed");return Ae(M,_??[]),E(e.client),F("committed",o)}catch(o){throw e.nativeFusion?.cancelManagedSend?.(p.id),o}},Q=o=>{d.currentRunId||(d.nextEventSeq=0),U(o);const f=o?.deliveryMode==="external_owner_queue"?"queued":"executing",u=z(a,p,f);return E(e.client),F(f,u)},J=async(o,f)=>{e.nativeFusion?.cancelManagedSend?.(p.id);const u=Y(g,o);console.error(`[chat.send] send failed reqId=${n.id} sessionId=${a} agentType=${c} workDir=${M} agentSessionId=${d.agentSessionId??l??""}: ${u}`),e.sessions.delete(a);try{await v(d)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:a,message:u,runId:"",seq:0}}),!f){const N={id:`agent-error-${n.id}-${Date.now()}`,sessionId:a,role:"agent",ts:Date.now(),payload:u};q(a,N),E(e.client)}f&&(e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:u}))};if(m){try{B()}catch(u){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:`Durable message commit failed: ${u instanceof Error?u.message:String(u)}`});return}let o;try{const u=y.attachments;o=await ae(d.adapter,y.text,t,O,u,r,s),b({level:"info",sessionId:a,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}catch(u){const N=z(a,p,"failed");E(e.client);const ie=Y(g,u);await J(u,!1),e.client.sendRes({type:"res",id:h,ok:!0,payload:{...F("failed",N),failureMessage:ie}});return}const f=Q(o??void 0);e.client.sendRes({type:"res",id:h,ok:!0,payload:{...f,...y.localized?{localizedAttachments:!0}:{},...o?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:o.deliveryMode,queuedSubmissionId:o.queuedSubmissionId}:{}}}),b({level:"info",sessionId:a,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});return}let V;try{V=B()}catch(o){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:h,ok:!1,error:`Durable message commit failed: ${o instanceof Error?o.message:String(o)}`});return}Q(),e.client.sendRes({type:"res",id:h,ok:!0,payload:{...V,...y.localized?{localizedAttachments:!0}:{}}}),b({level:"info",sessionId:a,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});const de=y.attachments;ae(d.adapter,y.text,t,O,de,r,s).then(()=>{b({level:"info",sessionId:a,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}).catch(o=>{z(a,p,"failed"),E(e.client),J(o,!1)})}async function Ke(e,n){const{sessionId:s}=n.params,a=e.sessions.get(s);if(a){try{await a.adapter.stop()}catch{}$e(e,s),e.activityPublisher?.publish(s,null)}e.client.sendRes({type:"res",id:n.id,ok:!0})}export{Ke as handleChatAbort,je as handleChatSend,Me as sendSessionUpdateEvent};
|
|
1
|
+
import ae from"node:os";import{createHash as Q}from"node:crypto";import{createAgent as Ae}from"../../agents/adapter.js";import{buildApprovalPendingPayload as ke,buildUserMessagePayload as re}from"@shennian/wire";import{ManagerModeAdapter as Ee}from"../../agents/manager.js";import{reportLog as D}from"../../log-reporter.js";import{lookupClaudeTranscriptCwd as we}from"../../native-fusion/parsers.js";import{appendMessage as b,getDaemonInstallationId as oe,getMessageDeliveryStatus as xe,recordSession as W,upsertMessage as N}from"../store.js";import{mergeProjectedSessions as Te}from"../projection.js";import{buildManagedAgentEnv as Me}from"../../agents/config-status.js";import{parsePersonalAttachmentReferences as _e}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as De,markAttachmentReferences as be}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as x}from"../index-publisher.js";import{writeToolDetail as qe}from"../tool-detail-store.js";import{getPersonalSyncIdentity as se}from"../../personal-sync/epoch.js";import{getManagedRoomIpcRoot as $e,MANAGED_ROOM_IPC_ROOT_ENV as Pe}from"../../daemon-ipc/paths.js";function Ce(e,t,a){if(!e.managedAgentPolicy)throw new Error("managed_agent_policy_missing");const s=e.managedRoomPromptSnapshot;if(!s)throw new Error("managed_room_prompt_snapshot_missing");if(!t?.trim())throw new Error("managed_room_prompt_incomplete");if(!a||!/^mrc_[A-Za-z0-9_-]{43}$/.test(a))throw new Error("managed_room_context_unavailable");const o=Q("sha256").update(JSON.stringify({requestedPolicy:e.managedAgentPolicy.requestedPolicy,enforcement:e.managedAgentPolicy.enforcement})).digest("hex"),c=Q("sha256").update(e.managedAgentPolicy.adapterPolicy.workDir).digest("hex");if(s.runtimePermissionPolicyHash!==o||s.resolvedWorkDirHash!==c||s.enforcementLevel!==e.managedAgentPolicy.enforcement.level)throw new Error("managed_room_prompt_snapshot_mismatch")}function ve(e){const t=e.indexOf(`
|
|
2
|
+
`),a=t>0?Math.min(t,80):Math.min(e.length,80);return e.slice(0,a)}function de(e,t,a){return`${e}:${Q("sha256").update(`${t}\0${a??""}`).digest("hex")}`}function Oe(e,t,a={}){if(e.state==="tool-call"||e.state==="tool-result"){const s={runId:e.runId,sourceSeq:e.seq};return{state:e.state,runId:e.runId,seq:e.seq,sessionId:t,detailRef:s,status:e.state==="tool-call"?"running":"completed",detailAvailable:!0,...e.durationMs!==void 0?{durationMs:e.durationMs}:{},...e.name?{name:e.name}:{},...e.source?{source:e.source}:{},...e.agentSessionId?{agentSessionId:e.agentSessionId}:{},...a}}return{...e,sessionId:t,...a}}const We=3e4;function Ne(e){return e.state==="heartbeat"?e.runPhase??null:e.state==="tool-call"||e.state==="tool-result"?"tool_running":e.state==="approval-pending"?"waiting_approval":e.state==="delta"?e.thinking?"thinking":"streaming_text":e.state==="init"||e.state==="start"?"thinking":null}function ie(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function le(e,t){return e!=="manager"?e:t==="claude"?"claude":"codex"}function Fe(e,t){W({sessionId:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,workDir:t.workDir,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:t.managerDefaultWorkerModelId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,workDir:t.workDir,status:"active",externalChannel:null}}})}function ce(e){return Me(e)}function ue(e,t,a){return{}}function V(e,t,a,s,o,c,u,p){const I=a==="codex";e.configure?.({sessionId:t,externalChannel:s??null,env:{...ce(a),...ue(t,s,o),...c&&!I?{SHENNIAN_MANAGED_ROOM_CONTEXT:c,[Pe]:$e()}:{}},...u?{managedAgentPolicy:u}:{},...I?{managedRoomContextToken:c??null,managedRoomExecutor:p??null}:{}})}function ge(e,t,a){const s=t.managedRoomContextToken;s&&s!==a&&e.revokeManagedRoomContextToken?.(s),t.managedRoomContextToken=a}function P(e,t){const a=t?.managedRoomContextToken;!t||!a||(t.managedRoomContextToken=null,e.revokeManagedRoomContextToken?.(a))}function He(e,t,a){if(e!=="claude"||!a)return t;const s=t.trim();return!!s&&s.startsWith("-")&&!s.includes("/")?we(a)??t:t}function ze(e,t,a,s){let o=null;function c(n,r={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:Oe(n,t,r),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function u(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function p(n){if(!n.currentRunId||!n.currentRunPhase)return;const r=n.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:t,runId:n.currentRunId,seq:r,runPhase:n.currentRunPhase},seq:r,id:`agent-heartbeat-${n.currentRunId}-${r}-${Date.now()}`})}function I(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{p(n)},We),n.heartbeatTimer.unref?.())}function h(n){const r=n?.pendingTextEvent;!n||!r||!r.text||(b(t,{id:`agent-${r.runId}-${r.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:r.text}),x(e.client),c({state:"delta",runId:r.runId,seq:r.seq,text:r.text,thinking:r.thinking||void 0}),n.pendingTextEvent=null)}s.on("agentEvent",n=>{const r=e.sessions.get(t),_=Ne(n),T=n.state==="final"||n.state==="error"||n.state==="aborted";if(r&&(r.nextEventSeq=n.seq+1,T||(r.currentRunId=n.runId),!T&&_&&(r.currentRunPhase=_,I(r)),n.agentSessionId&&(r.agentSessionId=n.agentSessionId)),e.managerRuntime?.noteAgentEvent(t,n),n.state==="delta"&&n.thinking)return;n.state!=="delta"&&D({level:"info",sessionId:t,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:a}});let k=!1;if(n.state==="tool-call"||n.state==="tool-result"){const m=`agent-${n.runId}-${n.seq}`;qe({sessionId:t,toolCallId:m,detailRevision:n.seq,ts:Date.now(),name:n.name??"tool",...n.args!==void 0?{args:n.args}:{},...n.result!==void 0?{result:n.result}:{},status:n.state==="tool-call"?"running":"completed",...n.durationMs!==void 0?{durationMs:n.durationMs}:{},detailRef:{runId:n.runId,sourceSeq:n.seq}}),b(t,{id:m,sessionId:t,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:n.state==="tool-call"?"tool_use":"tool_result",name:n.name,toolCallId:m,status:n.state==="tool-call"?"running":"completed",...n.durationMs!==void 0?{durationMs:n.durationMs}:{},detailRef:{runId:n.runId,sourceSeq:n.seq},detailRevision:n.seq,detailAvailable:!0,resultOmitted:n.state==="tool-result"})}),k=!0}else n.state==="approval-pending"?(b(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:ke(n.approval)}),k=!0,r&&W({sessionId:t,agentType:a,workDir:r.workDir,agentSessionId:r.agentSessionId,attentionId:de("approval",n.runId,n.seq)})):(n.state==="error"||n.state==="aborted")&&n.message&&(b(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.message}),k=!0);k&&x(e.client);const R=`${t}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&e.runTextAcc.set(R,(e.runTextAcc.get(R)??"")+n.text),n.agentSessionId&&n.agentSessionId!==o&&(o=n.agentSessionId,e.nativeFusion?.noteManagedSourceSession(t,le(a,n.source),n.agentSessionId),r&&W({sessionId:t,agentType:a,workDir:r.workDir,agentSessionId:n.agentSessionId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t,agentType:a,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const m=n.text??"";if(!m)return;r?.pendingTextEvent&&r.pendingTextEvent.runId!==n.runId&&h(r),r&&!r.pendingTextEvent&&(r.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:!1}),r?.pendingTextEvent&&(r.pendingTextEvent.text+=m,r.pendingTextEvent.seq=n.seq);return}let C={};if(n.state==="final"){h(r);const m=e.runTextAcc.get(R)??"";m&&(C={messageSummary:ve(m)}),e.runTextAcc.delete(R)}else n.state==="error"||n.state==="aborted"?(h(r),e.runTextAcc.delete(R)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&h(r);T&&r?.currentRunId===n.runId&&(P(e,r),r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,u(r),e.chatQueue?.noteTerminal(t),W({sessionId:t,agentType:a,workDir:r.workDir,agentSessionId:r.agentSessionId,status:n.state==="final"?"completed":"failed",attentionId:de("terminal",n.runId),lastActivityAt:new Date().toISOString()}),x(e.client)),c(n,C)}),s.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${t} agentType=${a}: ${n.message}`),P(e,e.sessions.get(t)),e.sessions.delete(t),e.chatQueue?.noteTerminal(t),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:t,message:n.message,runId:"",seq:0}})})}function Be(e,t){if(e.processedReqIds.add(t),e.processedReqIds.size>1e3){const a=e.processedReqIds.values().next().value;e.processedReqIds.delete(a)}}async function j(e,t){P(e,t),t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})}function fe(e,t,a,s,o,c,u){return u?e.send(t,a,s,o,c,u):c?.trim()?e.send(t,a,s,o,c):o?.length?e.send(t,a,s,o):s?e.send(t,a,s):e.send(t,a)}async function Le(e,t,a,s,o,c,u,p,I){e.evictIdleSessions();const h=Ae(a);if(!h)throw new Error(`Unsupported agent: ${a}`);const n=s==="manager"&&a!=="manager"?new Ee(h,a==="claude"?"claude":"codex"):h;V(n,t,a,u,p,null,I,e.managedRoomExecutor),await n.start(t,o,c);const r={adapter:n,workDir:o,agentType:a,sessionMode:s,agentSessionId:c??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:u??null,externalReplyTarget:p??null,externalChannelEnv:{...ce(a),...ue(t,u,p)},managedRoomContextToken:null};return e.sessions.set(t,r),ze(e,t,a,n),r}function Ue(e,t){const a=e.sessions.get(t),s=a?.currentRunId;if(!a||!s)return;const o=a.nextEventSeq;e.runTextAcc.delete(`${t}:${s}`),P(e,a),a.pendingTextEvent=null,a.currentRunId=null,a.currentRunPhase=null,a.nextEventSeq=0,a.heartbeatTimer&&(clearInterval(a.heartbeatTimer),a.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:t,runId:s,seq:o},seq:o,id:`agent-evt-${s}-${o}`}),e.chatQueue?.noteTerminal(t)}async function lt(e,t,a){const s=a?t.params.managedRoomContextToken??null:null;let o=!1;try{await Je(e,t,a,()=>{o=!0})}finally{s&&!o&&e.revokeManagedRoomContextToken?.(s)}}async function Je(e,t,a,s){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}const{sessionId:o,text:c,agentType:u,sessionMode:p,managerConfig:I,workDir:h,agentSessionId:n,modelId:r,systemPrompt:_,managerDefaultWorkerAgentType:T,managerDefaultWorkerModelId:k,reasoningEffort:R,clientMessageId:C,sessionListProjection:m,waitForDispatch:me,responseId:pe,managedRoomContextToken:q,suppressExternalOwnerEcho:he,queuedBatchItems:F}=t.params,S=pe||t.id;Te(m);const v=null,H="";if(a?.source==="room_activation"&&(Ce(a,_,q),u==="manager"||p!=="standard"))throw new Error("managed_agent_manager_mode_unsupported");if(Be(e,t.id),!o||!c){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:S,ok:!1,error:"sessionId and text are required"});return}const g=u,$=g==="manager"||p==="manager"?"manager":"standard";$==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(o,T??null,k??null);const z=(g==="claude"||g==="codex"||g==="workbuddy")&&typeof R=="string"&&R.trim()?R.trim():void 0,M=e.resolvePath(He(g,h||ae.homedir(),n)||ae.homedir()),ye=M,O=_e(t.params.attachments);O?.length&&De(M,O);const A={text:c,attachments:O,localized:!1};let i=e.sessions.get(o);if(i){if(i.lastActiveAt=Date.now(),i.agentType!==g||(i.sessionMode??(i.agentType==="manager"?"manager":"standard"))!==$||i.workDir!==M||JSON.stringify(i.externalChannel??null)!==JSON.stringify(v??null)){e.sessions.delete(o);try{await j(e,i)}catch{e.processedReqIds.delete(t.id)}i=void 0}else if(n&&i.agentSessionId!==n)try{await i.adapter.resume(n),i.agentSessionId=n}catch{e.sessions.delete(o);try{await j(e,i)}catch{e.processedReqIds.delete(t.id)}i=void 0}}if(!i)try{i=await Le(e,o,g,$,M,n,v,H,a?.managedAgentPolicy)}catch(d){const f=d instanceof Error&&d.message.startsWith("Unsupported agent:")?d.message:`Failed to start ${u}: ${d instanceof Error?d.message:String(d)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${o} agentType=${u} workDir=${M} agentSessionId=${n??""}: ${f}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:f,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:S,ok:!1,error:f});return}const y={id:C??`user-${t.id}`,sessionId:o,role:"user",ts:Date.now(),payload:re(A.text,A.attachments)},G=Array.isArray(F)&&F.length>0?F.map(d=>({id:d.clientMessageId??d.id,sessionId:o,role:"user",ts:Date.parse(d.createdAt),payload:re(d.text,d.attachments)})):null,B=G??[y],E=xe(o,y.id),K=E.state==="queued";if(E.state!=="not_found"&&!K){e.client.sendRes({type:"res",id:S,ok:!0,payload:E});return}D({level:"info",sessionId:o,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:g,sessionMode:$,managerConfig:I??null,modelId:r,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:k??null,reasoningEffort:z,...a?.source==="room_activation"?{managedRoomPromptSnapshot:a.managedRoomPromptSnapshot}:{}}});const L=(d,f)=>({...se(oe()),sessionId:o,clientMessageId:y.id,messageId:y.id,deliveryState:d,bodyRevision:f,committedAt:new Date(y.ts).toISOString()}),X=d=>{e.nativeFusion?.registerManagedSend({sessionId:o,agentType:g,sourceAgentType:le(g,r),canonicalMessageId:y.id,sourceSessionKey:i.agentSessionId??n??null,text:A.text,managedEchoPolicy:d?.deliveryMode==="external_owner_queue"&&!he?"import_following":"suppress_following",externalRunId:d?.deliveryMode==="external_owner_queue"?`codex-external-${d.queuedSubmissionId??y.id}`:null})},Y=()=>{i.currentRunId||(i.currentRunId=`dispatch:${t.id}`,i.currentRunPhase="thinking",i.nextEventSeq=0)},Z=()=>{if(X(),K)return{...se(oe()),sessionId:o,clientMessageId:E.clientMessageId,messageId:E.messageId,deliveryState:E.deliveryState,bodyRevision:E.bodyRevision,committedAt:E.committedAt};try{Fe(e,{sessionId:o,agentType:g,sessionMode:$,managerConfig:I??null,workDir:ye,agentSessionId:i.agentSessionId??n??null,modelId:r,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:k??null});let d=0;for(const[f,l]of B.entries()){const w=G?N(o,l,"committed"):b(o,l,"committed");f===0&&(d=w)}return be(M,O??[]),x(e.client),L("committed",d)}catch(d){throw e.nativeFusion?.cancelManagedSend?.(y.id),d}},ee=d=>{i.currentRunId||(i.nextEventSeq=0),X(d);const f=d?.deliveryMode==="external_owner_queue"?"queued":"executing";let l=0;for(const[w,U]of B.entries()){const J=N(o,U,f);w===0&&(l=J)}return x(e.client),L(f,l)},te=async(d,f)=>{e.nativeFusion?.cancelManagedSend?.(y.id);const l=ie(g,d);console.error(`[chat.send] send failed reqId=${t.id} sessionId=${o} agentType=${u} workDir=${M} agentSessionId=${i.agentSessionId??n??""}: ${l}`),e.sessions.delete(o);try{await j(e,i)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:l,runId:"",seq:0}}),!f){const w={id:`agent-error-${t.id}-${Date.now()}`,sessionId:o,role:"agent",ts:Date.now(),payload:l};b(o,w),x(e.client)}f&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:S,ok:!1,error:l}))};if(me){try{Z()}catch(l){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:S,ok:!1,error:`Durable message commit failed: ${l instanceof Error?l.message:String(l)}`});return}Y();let d;try{a?.source==="room_activation"&&(V(i.adapter,o,g,v,H,q,a.managedAgentPolicy,e.managedRoomExecutor),ge(e,i,q),s());const l=A.attachments;d=await fe(i.adapter,A.text,r,z,l,_,a?{source:a.source}:void 0),D({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(l){let w=0;for(const[J,Re]of B.entries()){const Se=N(o,Re,"failed");J===0&&(w=Se)}x(e.client);const U=ie(g,l);await te(l,!1),e.client.sendRes({type:"res",id:S,ok:!0,payload:{...L("failed",w),failureMessage:U}});return}const f=ee(d??void 0);e.client.sendRes({type:"res",id:S,ok:!0,payload:{...f,...A.localized?{localizedAttachments:!0}:{},...d?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:d.deliveryMode,queuedSubmissionId:d.queuedSubmissionId}:{}}}),D({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}let ne;try{ne=Z()}catch(d){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:S,ok:!1,error:`Durable message commit failed: ${d instanceof Error?d.message:String(d)}`});return}Y(),a?.source==="room_activation"&&(V(i.adapter,o,g,v,H,q,a.managedAgentPolicy,e.managedRoomExecutor),ge(e,i,q),s()),ee(),e.client.sendRes({type:"res",id:S,ok:!0,payload:{...ne,...A.localized?{localizedAttachments:!0}:{}}}),D({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const Ie=A.attachments;fe(i.adapter,A.text,r,z,Ie,_,a?{source:a.source}:void 0).then(()=>{D({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(d=>{N(o,y,"failed"),x(e.client),te(d,!1)})}async function ct(e,t){const{sessionId:a}=t.params,s=e.sessions.get(a);if(s){P(e,s);try{await s.adapter.stop()}catch{e.client.sendRes({type:"res",id:t.id,ok:!1,error:"agent_stop_failed"});return}Ue(e,a),e.activityPublisher?.publish(a,null)}e.client.sendRes({type:"res",id:t.id,ok:!0})}export{ct as handleChatAbort,lt as handleChatSend,Fe as sendSessionUpdateEvent};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReqFrame } from '@shennian/wire';
|
|
2
|
+
import type { CliRelayClient } from '../../relay/client.js';
|
|
3
|
+
import type { RemoteAccessPauseController } from '../types.js';
|
|
4
|
+
export declare function handleRemoteAccessPause(client: CliRelayClient, controller: RemoteAccessPauseController | null, req: ReqFrame): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
async function i(a,t,s){if(!t){await a.sendResAndWait({type:"res",id:s.id,ok:!1,error:"upgrade_required"}).catch(()=>{});return}try{const e=t.prepare();if(!e.autoStartDisabled)throw new Error(e.autoStartError?`remote_access_autostart_disable_failed: ${e.autoStartError}`:"remote_access_autostart_disable_failed");const r=await a.sendBufferedEvent({type:"event",id:`remote-access-paused-${s.id}`,event:"machine.remote-access.paused",payload:{disabledAt:e.disabledAt}},3e4),d=r&&typeof r.payload=="object"?r.payload:{remoteAccessDisabledAt:e.disabledAt};await a.sendResAndWait({type:"res",id:s.id,ok:!0,payload:d})}catch(e){await a.sendResAndWait({type:"res",id:s.id,ok:!1,error:e instanceof Error?e.message:String(e)}).catch(()=>{})}finally{t.isDisabled()&&t.shutdownAfterResponse()}}export{i as handleRemoteAccessPause};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ReqFrame } from '@shennian/wire';
|
|
2
2
|
import type { ManagedRoomBinder } from '../../room/managed-room-binding.js';
|
|
3
3
|
import type { SessionManagerRuntime } from '../types.js';
|
|
4
4
|
export declare function handleManagedRoomBind(runtime: SessionManagerRuntime, binder: ManagedRoomBinder | null, req: ReqFrame): Promise<void>;
|
|
@@ -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,8 +1,9 @@
|
|
|
1
|
-
import { type
|
|
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
|
+
import { type ManagedRoomEnqueueParams, type ManagedRoomQueueContext, type PreparedManagedRoomTurn } from './queue.js';
|
|
6
7
|
import '../agents/claude.js';
|
|
7
8
|
import '../agents/codex.js';
|
|
8
9
|
import '../agents/workbuddy.js';
|
|
@@ -11,6 +12,7 @@ import '../agents/cursor.js';
|
|
|
11
12
|
import '../agents/opencode.js';
|
|
12
13
|
import '../agents/manager.js';
|
|
13
14
|
import type { ManagedRoomBinder } from '../room/managed-room-binding.js';
|
|
15
|
+
import type { ManagedRoomExecutor } from '../room/managed-room-ipc.js';
|
|
14
16
|
export { resolveSessionWorkDir } from '../fs/boundary.js';
|
|
15
17
|
export declare class SessionManager {
|
|
16
18
|
private client;
|
|
@@ -18,6 +20,9 @@ export declare class SessionManager {
|
|
|
18
20
|
private cliVersion?;
|
|
19
21
|
private upgradePolicyController;
|
|
20
22
|
private managedRoomBinder;
|
|
23
|
+
private remoteAccessPauseController;
|
|
24
|
+
private managedRoomContextRevoker;
|
|
25
|
+
private managedRoomExecutor;
|
|
21
26
|
private sessions;
|
|
22
27
|
/** Track processed request IDs to deduplicate replayed offline messages */
|
|
23
28
|
private processedReqIds;
|
|
@@ -32,28 +37,18 @@ export declare class SessionManager {
|
|
|
32
37
|
private activityProbeTimer;
|
|
33
38
|
private attachmentCleanupTimer;
|
|
34
39
|
private activeRequests;
|
|
35
|
-
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);
|
|
36
41
|
private getRuntime;
|
|
37
42
|
getUpgradeIdleState(): UpgradeIdleState;
|
|
43
|
+
setManagedRoomTurnPreparer(prepare: (contexts: ManagedRoomQueueContext[]) => Promise<PreparedManagedRoomTurn>): void;
|
|
44
|
+
enqueueManagedRoomBatch(inputs: Array<{
|
|
45
|
+
params: ManagedRoomEnqueueParams;
|
|
46
|
+
context: ManagedRoomQueueContext;
|
|
47
|
+
}>): Promise<void>;
|
|
48
|
+
private dispatchQueuedChat;
|
|
38
49
|
synchronizeSessionDeletions(): Promise<void>;
|
|
39
50
|
private synchronizeSessionDeletionsOnce;
|
|
40
51
|
private applySessionDeletion;
|
|
41
|
-
activateRoomSession(input: {
|
|
42
|
-
sessionId: string;
|
|
43
|
-
agentType: AgentType;
|
|
44
|
-
agentSessionId: string;
|
|
45
|
-
workDir: string;
|
|
46
|
-
text: string;
|
|
47
|
-
clientMessageId: string;
|
|
48
|
-
systemPrompt: string;
|
|
49
|
-
roomContextRegistryKey: string;
|
|
50
|
-
managedRoomContextToken?: string;
|
|
51
|
-
}): Promise<{
|
|
52
|
-
externalOwnerQueued: boolean;
|
|
53
|
-
}>;
|
|
54
|
-
abortRoomSession(input: {
|
|
55
|
-
sessionId: string;
|
|
56
|
-
}): Promise<void>;
|
|
57
52
|
private publishSessionActivity;
|
|
58
53
|
private handleExternalTerminal;
|
|
59
54
|
private publishManagedActivitySnapshots;
|
|
@@ -63,4 +58,5 @@ export declare class SessionManager {
|
|
|
63
58
|
private resolvePath;
|
|
64
59
|
private resolveAuthorizedPath;
|
|
65
60
|
cleanup(): Promise<void>;
|
|
61
|
+
private revokeManagedRoomToken;
|
|
66
62
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SESSION_SYNC_PROTOCOL_VERSION as u,SESSION_SYNC_DATA_EPOCH as m,isAvailableAgentType as S}from"@shennian/wire";import h from"node:crypto";import{getRegisteredAgents as R,unregisterAgent as k}from"../agents/adapter.js";import{loadConfig as I}from"../config/index.js";import{handleUpgradeStart as A,handleUpgradeStatus as T}from"../commands/upgrade.js";import{handleAgentsRefresh as C,handleModelsRefresh as P}from"./handlers/agents.js";import{handleAgentCapabilitiesList as D}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as E,handleAgentConfigGet as x,handleAgentConfigTest as O,handleAgentConfigUpsert as F}from"./handlers/agent-config.js";import{handleChatAbort as p,handleChatSend as f}from"./handlers/chat.js";import{handleSessionRefresh as M}from"./handlers/session-refresh.js";import{handleSessionToolDetail as _}from"./handlers/tool-detail.js";import{handleSessionTitleSet as N}from"./handlers/title.js";import{handleSessionMessageStatus as U}from"./handlers/message-status.js";import{handleVoiceSessionBodyWindow as Q}from"./handlers/voice-window.js";import{cleanupPendingTransfers as z,handleFsLs as V,handleFsRead as W,handleFsRename as $,handleFsWrite as j,handleFsTransferAbort as B,handleFsTransferChunk as L,handleFsTransferFinish as H,handleFsTransferStart as Y,handleFsExportMarkdownPdf as G,handleFsExportMarkdownPdfSetup as K,handleFsArchiveZip as X}from"./handlers/fs.js";import{handleRegionProbe as Z,handleRegionSwitch as J,handleUpgradeSetPolicy as q}from"./handlers/control.js";import{ManagerRuntimeService as ee,setManagerRuntimeService as g}from"../manager/runtime.js";import{ChatQueueManager as se}from"./queue.js";import{createAuthorizedFsRoot as te,resolveAuthorizedPath as ie,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 ne}from"../agents/custom.js";import{handleManagedRoomBind as re,handleManagedRoomRestart as oe}from"./handlers/room-managed.js";import{getDaemonInstallationId as v,listSessionRecords as b,purgeCanonicalSession as ce,purgeSessionRecord as le}from"./store.js";import{PersonalHistorySubscriptions as he}from"./history-subscriptions.js";import{sweepAttachmentTransfers as de}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as ue}from"../native-fusion/native-source-deletions.js";import{purgeToolDetails as me}from"./tool-detail-store.js";import{readDeletionSyncRevision as pe,writeDeletionSyncRevision as y}from"./deletion-sync-state.js";import{assertDeletionSyncRunnable as fe,clearDeletionSyncFailure as ge,DeletionSyncRetryWaitError as ve,DeletionSyncTerminalError as be,recordDeletionSyncFailure as d}from"./deletion-sync-state.js";import{NativeSessionCleanupOutbox as ye}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as c}from"../personal-sync/epoch.js";const we=50;function Se(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 i of t.sources)if(!i?.sourceSessionKey||!S(i.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 cs}from"../fs/boundary.js";class ns{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,i=null,a=null){this.client=e,this.nativeFusion=s,this.cliVersion=t,this.upgradePolicyController=i,this.managedRoomBinder=a,this.managerRuntime=new ee({getRuntime:()=>this.getRuntime(),dispatchReq:n=>this.handleReq(n)}),this.chatQueue=new se({getRuntime:()=>this.getRuntime(),dispatchReq:n=>this.handleReq(n)}),this.historySubscriptions=new he(this.client),this.nativeCleanupOutbox=new ye({logger:console.info}),this.nativeFusion?.setExternalTerminalHandler?.(n=>{this.handleExternalTerminal(n)}),this.nativeCleanupOutbox.start(),g(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([...b().map(r=>r.workDir),...Array.from(this.pendingTransfers.values()).map(r=>r.rootPath)]);for(const r of n)try{de(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}}async synchronizeSessionDeletions(){fe();try{await this.synchronizeSessionDeletionsOnce(),ge()}catch(e){if(e instanceof ve||e instanceof be)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 d({code:t?"transport_unavailable":"invalid_deletion_sync_response",retryable:t}),e}}async synchronizeSessionDeletionsOnce(){const e=v(),s=pe(),t=await this.client.sendReq({type:"req",id:`session-deletions-sync-${h.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:u,dataEpoch:m,daemonInstallationId:e,afterRevision:s}});if(!t.ok){const r=t.rejection;throw r?(d({code:r.code,retryable:r.retryable&&r.requiredAction==="retry"}),new Error(r.code)):(d({code:"unstructured_rejection",retryable:!1}),new Error(t.error??"unstructured_rejection"))}const i=t.payload;c(i),Se(i,s);let a=i.baselineRevision;a>s&&y(a);for(const r of i.deletions)r.deletionRevision<=a||(await this.applySessionDeletion(r),a=r.deletionRevision,y(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-${h.randomUUID()}`,method:"session.deletions.ack",params:{protocolVersion:u,dataEpoch:m,daemonInstallationId:e,deletionRevision:a}});if(!n.ok)throw new Error(n.error??"session deletion acknowledgement failed")}async applySessionDeletion(e){const s=new ue({daemonInstallationId:v()});s.recordSession(e);for(const i of e.sources)s.record({...e,...i});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),me(e.sessionId),ce(e.sessionId),le(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null),this.nativeCleanupOutbox.enqueue(e),this.nativeCleanupOutbox.kick()}async activateRoomSession(e){const s=b().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 w=>{i.value=w};const l=Reflect.get(n,r,n);return typeof l=="function"?l.bind(n):l}});return await f({...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 p({...this.getRuntime(),client:s},{type:"req",id:`room-activation-abort-${h.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 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 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 R())s.startsWith("custom:")&&k(s);const e=I();for(const[s,t]of Object.entries(e.customAgents??{}))ne(s,t)}async handleReq(e){const s=this.getRuntime();this.activeRequests++;try{switch(e.method){case"chat.send":c(e.params),await f(s,e);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 p(s,e);break;case"session.refresh":c(e.params),await M(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":U(s,e);break;case"session.tool.detail":await _(s,e);break;case"voice.session.body.window":Q(s,e);break;case"session.title.set":await N(s,e);break;case"room.agent.bind-managed":await re(s,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await oe(s,this.managedRoomBinder,e);break;case"fs.ls":await V(s,e);break;case"fs.read":await W(s,e);break;case"fs.write":await j(s,e);break;case"fs.export.markdown-pdf":await G(s,e);break;case"fs.export.markdown-pdf.setup":await K(s,e);break;case"fs.archive.zip":await X(s,e);break;case"fs.rename":await $(s,e);break;case"fs.transfer.start":await Y(s,e);break;case"fs.transfer.chunk":await L(s,e);break;case"fs.transfer.finish":await H(s,e);break;case"fs.transfer.abort":await B(s,e);break;case"region.probe":await Z(s,e);break;case"region.switch":await J(s,e);break;case"upgrade.start":await A(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0});break;case"upgrade.status":await T(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await q(s,e);break;case"agents.refresh":await C(s,e);break;case"models.refresh":await P(s,e);break;case"agent.config.get":await x(s,e);break;case"agent.config.upsert":await F(s,e);break;case"agent.config.clear":await E(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<we)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 ie(e,te(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(),z(this.getRuntime()),await this.managerRuntime.stop(),g(null)}}export{ns as SessionManager,cs 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,14 +1,38 @@
|
|
|
1
|
-
import { type ChatQueueSnapshot, type ReqFrame } from '@shennian/wire';
|
|
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';
|
|
6
|
+
export type ManagedRoomQueueContext = {
|
|
7
|
+
registryKey: string;
|
|
8
|
+
causeMessageId: string;
|
|
9
|
+
rootCauseMessageId: string;
|
|
10
|
+
sequence: string;
|
|
11
|
+
hopCount: number;
|
|
12
|
+
};
|
|
13
|
+
export type PreparedManagedRoomTurn = {
|
|
14
|
+
systemPrompt: string;
|
|
15
|
+
roomContextRegistryKey: string;
|
|
16
|
+
managedRoomContextToken: string;
|
|
17
|
+
managedAgentPolicy: CompiledManagedAgentPolicy;
|
|
18
|
+
managedRoomPromptSnapshot: ManagedRoomPromptSnapshot;
|
|
19
|
+
};
|
|
20
|
+
export type ManagedRoomEnqueueParams = Omit<ChatEnqueueParams, 'protocolVersion' | 'dataEpoch' | 'daemonInstallationId'>;
|
|
4
21
|
export declare class ChatQueueManager {
|
|
5
22
|
private opts;
|
|
6
23
|
private draining;
|
|
24
|
+
private prepareManagedRoomTurn;
|
|
7
25
|
constructor(opts: {
|
|
8
26
|
getRuntime: () => SessionManagerRuntime;
|
|
9
27
|
dispatchReq: LocalReqDispatcher;
|
|
28
|
+
dispatchQueuedReq: LocalReqDispatcher;
|
|
10
29
|
});
|
|
11
30
|
getSnapshot(sessionId: string): ChatQueueSnapshot;
|
|
31
|
+
setManagedRoomTurnPreparer(prepare: (contexts: ManagedRoomQueueContext[]) => Promise<PreparedManagedRoomTurn>): void;
|
|
32
|
+
enqueueManagedRoomBatch(inputs: Array<{
|
|
33
|
+
params: ManagedRoomEnqueueParams;
|
|
34
|
+
context: ManagedRoomQueueContext;
|
|
35
|
+
}>): Promise<void>;
|
|
12
36
|
handleEnqueue(req: ReqFrame): Promise<void>;
|
|
13
37
|
handleGet(req: ReqFrame): Promise<void>;
|
|
14
38
|
handleEdit(req: ReqFrame): Promise<void>;
|
|
@@ -17,8 +41,7 @@ export declare class ChatQueueManager {
|
|
|
17
41
|
purgeSession(sessionId: string): void;
|
|
18
42
|
private drainIdleQueues;
|
|
19
43
|
private drainNext;
|
|
20
|
-
private
|
|
21
|
-
private dispatchQueuedMessage;
|
|
44
|
+
private dispatchQueuedMessages;
|
|
22
45
|
private isSessionBusy;
|
|
23
46
|
private broadcast;
|
|
24
47
|
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
`,{encoding:"utf8",mode:384,flag:"wx"});const i=
|
|
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
|
+
${s.map(e=>G(e.text)).filter(Boolean).join(`
|
|
3
4
|
|
|
4
|
-
`)
|
|
5
|
-
|
|
5
|
+
`)}`:`[\u8FDE\u7EED\u7528\u6237\u6D88\u606F]
|
|
6
|
+
${s.map((e,n)=>`<\u6D88\u606F ${n+1}>
|
|
7
|
+
${e.text.trim()}`).join(`
|
|
8
|
+
|
|
9
|
+
`)}`}function G(s){const e=s.trim(),n=e.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);return n?`<${n[1]}>
|
|
10
|
+
${n[2]}`:e.startsWith(`[\u7FA4\u6D88\u606F]
|
|
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
|
};
|