shennian 0.4.9 → 0.4.11
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 +12 -12
- package/dist/src/agents/adapter.d.ts +2 -0
- package/dist/src/agents/codex.d.ts +1 -0
- package/dist/src/agents/codex.js +8 -8
- package/dist/src/index.js +3 -3
- package/dist/src/room/activation-service.d.ts +2 -0
- package/dist/src/room/activation-service.js +5 -5
- package/dist/src/session/handlers/chat.js +2 -2
- package/dist/src/session/manager.d.ts +3 -0
- package/dist/src/session/manager.js +1 -1
- package/dist/src/session/queue.d.ts +7 -1
- package/dist/src/session/queue.js +8 -8
- package/dist/src/session/types.d.ts +1 -0
- package/node_modules/@shennian/wire/dist/session.d.ts +2 -0
- package/package.json +1 -1
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
"file": "src/agents/codex.js",
|
|
49
|
-
"beforeBytes":
|
|
50
|
-
"afterBytes":
|
|
49
|
+
"beforeBytes": 65975,
|
|
50
|
+
"afterBytes": 33808
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
53
|
"file": "src/agents/command-spec.js",
|
|
@@ -346,8 +346,8 @@
|
|
|
346
346
|
},
|
|
347
347
|
{
|
|
348
348
|
"file": "src/index.js",
|
|
349
|
-
"beforeBytes":
|
|
350
|
-
"afterBytes":
|
|
349
|
+
"beforeBytes": 27270,
|
|
350
|
+
"afterBytes": 13879
|
|
351
351
|
},
|
|
352
352
|
{
|
|
353
353
|
"file": "src/log-redaction.js",
|
|
@@ -496,8 +496,8 @@
|
|
|
496
496
|
},
|
|
497
497
|
{
|
|
498
498
|
"file": "src/room/activation-service.js",
|
|
499
|
-
"beforeBytes":
|
|
500
|
-
"afterBytes":
|
|
499
|
+
"beforeBytes": 23121,
|
|
500
|
+
"afterBytes": 11545
|
|
501
501
|
},
|
|
502
502
|
{
|
|
503
503
|
"file": "src/room/attachment-cache.js",
|
|
@@ -646,8 +646,8 @@
|
|
|
646
646
|
},
|
|
647
647
|
{
|
|
648
648
|
"file": "src/session/handlers/chat.js",
|
|
649
|
-
"beforeBytes":
|
|
650
|
-
"afterBytes":
|
|
649
|
+
"beforeBytes": 44520,
|
|
650
|
+
"afterBytes": 18490
|
|
651
651
|
},
|
|
652
652
|
{
|
|
653
653
|
"file": "src/session/handlers/control.js",
|
|
@@ -706,8 +706,8 @@
|
|
|
706
706
|
},
|
|
707
707
|
{
|
|
708
708
|
"file": "src/session/manager.js",
|
|
709
|
-
"beforeBytes":
|
|
710
|
-
"afterBytes":
|
|
709
|
+
"beforeBytes": 32004,
|
|
710
|
+
"afterBytes": 16673
|
|
711
711
|
},
|
|
712
712
|
{
|
|
713
713
|
"file": "src/session/native-cleanup-outbox.js",
|
|
@@ -726,8 +726,8 @@
|
|
|
726
726
|
},
|
|
727
727
|
{
|
|
728
728
|
"file": "src/session/queue.js",
|
|
729
|
-
"beforeBytes":
|
|
730
|
-
"afterBytes":
|
|
729
|
+
"beforeBytes": 35970,
|
|
730
|
+
"afterBytes": 16080
|
|
731
731
|
},
|
|
732
732
|
{
|
|
733
733
|
"file": "src/session/store.js",
|
|
@@ -38,6 +38,8 @@ export type AgentSendContext = {
|
|
|
38
38
|
export type AgentStartOptions = {
|
|
39
39
|
/** Test-only callers can disable provider-native history persistence. */
|
|
40
40
|
persistNativeSession?: boolean;
|
|
41
|
+
/** Keep an internal orchestration session out of the provider's user-facing task list. */
|
|
42
|
+
nativeSessionVisibility?: 'user' | 'internal';
|
|
41
43
|
};
|
|
42
44
|
export interface AgentAdapterEvents {
|
|
43
45
|
agentEvent: [event: AgentEvent];
|
|
@@ -50,6 +50,7 @@ export declare class CodexAdapter extends AgentAdapter {
|
|
|
50
50
|
private managedTurnReplay;
|
|
51
51
|
private managedAsyncRecovery;
|
|
52
52
|
private persistNativeSession;
|
|
53
|
+
private nativeSessionVisibility;
|
|
53
54
|
constructor(options?: {
|
|
54
55
|
modelInstructionsFile?: string;
|
|
55
56
|
hidden?: boolean;
|
package/dist/src/agents/codex.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import{spawnSync as
|
|
1
|
+
import{spawnSync as b}from"node:child_process";import{createInterface as S}from"node:readline";import{createHash as w,randomUUID as O}from"node:crypto";import g from"node:path";import{AgentAdapter as q,registerAgent as N}from"./adapter.js";import{resolveBuiltinCommand as T,spawnAgentCommand as x}from"./command-spec.js";import{buildAgentProcessEnv as D,buildManagedAgentProcessEnv as L}from"../agent-env.js";import{ensurePlatformInstructionsFile as F}from"./platform-instructions.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as C}from"@shennian/wire";import{isCodexThreadTailInterrupted as $}from"../native-fusion/codex-files.js";import{resolveManagedAgentAdapterEnforcement as U}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as W}from"../security/managed-agent-runtime.js";import{CODEX_MANAGED_ROOM_DYNAMIC_TOOLS as z,CodexManagedRoomToolBinding as B}from"./codex-managed-room-tools.js";import{assertCodexManagedPermissionProfile as R,CODEX_MANAGED_RUNTIME_SECURITY as G,compileCodexManagedPermissions as V,extractCodexMcpServerNames as J}from"./codex-managed-permissions.js";import{CODEX_APP_SERVER_CLIENT_INFO as H,extractAppServerErrorMessage as E,formatCodexErrorMessage as X,formatTransientCodexStatus as K,isCodexCollabAgentItem as Q,isCodexCollabAgentToolName as v,isCodexLegacyCollabAgentItem as Y,isCodexUnsupportedEffortError as j,isMissingCodexRolloutError as y,isTransientCodexErrorMessage as Z,looksLikeCodexInteractiveAuthPrompt as ee,looksLikeFatalCodexStderr as te,normalizeCodexModelId as I,normalizeCodexReasoningEffort as re,normalizeCodexStderr as _,normalizeTerminalText as se,safeStringify as k,stripGitDirectiveArtifacts as ne}from"./codex-utils.js";import{isCodexUnsupportedEffortError as Oe,isMissingCodexRolloutError as qe,normalizeCodexModelId as Ne,normalizeCodexReasoningEffort as De}from"./codex-utils.js";W("codex",G);function P(o){const e=o instanceof Error?o.message:String(o);return/already has an active writer|thread-store conflict/i.test(e)}function ie(o){const e=o instanceof Error?o.message:String(o);return/paginated(?:_threads|\s+(?:thread\s+)?history)?.*(?:not supported|unsupported|unavailable)/i.test(e)||/(?:not supported|unsupported|unavailable).*paginated(?:_threads|\s+(?:thread\s+)?history)?/i.test(e)}function oe(o){const e=o instanceof Error?o.message:String(o);return/unknown field [`'"]?excludeTurns/i.test(e)||/excludeTurns.*(?:not supported|unsupported|unavailable)/i.test(e)}function ae(o){const e=o instanceof Error?o.message:String(o);return/(?:method not found|unknown method).*(?:project\/list|project\/create)/i.test(e)||/(?:project\/list|project\/create).*(?:not supported|unsupported|unavailable)/i.test(e)}function de(o,e){const t=g.resolve(e);let r;for(const s of o)if(s.id)for(const i of s.roots??[]){if(!i.path)continue;const n=g.resolve(i.path),a=g.relative(n,t);!(a===""||!a.startsWith("..")&&!g.isAbsolute(a))||n.length<=(r?.rootLength??-1)||(r={id:s.id,rootLength:n.length})}return r?.id}function le(o,e){const t=e?.flatMap(s=>ce(s)).filter(s=>!/^https?:\/\//i.test(s.path)).filter(s=>s.path);return{type:"text",text:he(o,e),text_elements:[],...t?.length?{local_images:t}:{}}}function ce(o){if(o.kind==="image")return o.previewData?[{path:o.path,data:o.previewData,mimeType:o.mimeType}]:[{path:o.path}];if(o.mimeType.startsWith("video/")){const e=ue(o.path);return e?[{path:e}]:[]}return[]}function ue(o){if(!o||/^https?:\/\//i.test(o))return null;const e=g.extname(o),r=`${e?o.slice(0,-e.length):o}.preview.png`;return b("ffmpeg",["-y","-ss","0","-i",o,"-frames:v","1","-vf","scale=320:-1",r],{stdio:"ignore",timeout:15e3}).status===0?r:null}function he(o,e){const t=e?.filter(i=>i.kind!=="image").filter(i=>i.path&&!/^https?:\/\//i.test(i.path));if(!t?.length||t.every(i=>o.includes(i.path)))return o;const s=t.map((i,n)=>{const a=i.name||`attachment-${n+1}`,d=i.mimeType?` (${i.mimeType})`:"";return`[${A(a)}${d}](<${i.path}>)`});return[o,"",...s].join(`
|
|
2
2
|
`)}function pe(o,e){const t=e?.filter(s=>s.path&&!/^https?:\/\//i.test(s.path)&&!o.includes(s.path));if(!t?.length)return o;const r=t.map((s,i)=>{const n=A(s.name||`attachment-${i+1}`);return s.kind==="image"?``:`[${n}](<${s.path}>)`});return[o,"",...r].join(`
|
|
3
|
-
`)}function me(o,e,t){return["queue","--thread",o,"--message",pe(e,t)]}function A(o){return o.replace(/[[\]\\]/g,"\\$&")}class ge extends q{options;type="codex";process=null;workDir=null;seq=0;runId="";pendingUsage;agentSessionId=null;hasEmittedText=!1;textTail="";terminalState="open";forceCloseTimer=null;stderrBuf="";rpcSeq=1;pendingRequests=new Map;appServerReady=null;appServerStartupError=null;activeTurnId=null;deltaItemIds=new Set;lastTransientStatus="";agentSystemPrompt="";managedAgentPolicy=null;managedPermissionConfig=null;configurationRestartRequired=!1;managedRoomTools=new B;configuredManagedRoomContextToken=null;configuredManagedRoomExecutor=null;managedRoomToolThreadId=null;preparedExternalOwnerThreadId=null;managedTurnReplay=null;managedAsyncRecovery=null;persistNativeSession=!0;constructor(e={}){super(),this.options=e}externalChannel=null;shennianSessionId=null;extraEnv={};configure(e){const t=this.configurationFingerprint();this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{},"managedAgentPolicy"in e&&(this.managedAgentPolicy=e.managedAgentPolicy??null),("managedRoomContextToken"in e||"managedRoomExecutor"in e)&&(this.managedRoomTools.deactivateTurn(),this.configuredManagedRoomContextToken=e.managedRoomContextToken?.trim()||null,this.configuredManagedRoomExecutor=e.managedRoomExecutor??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir);const r=this.configurationFingerprint();t!==r&&this.process&&(this.configurationRestartRequired=!0)}configurationFingerprint(){const e=Object.keys(this.extraEnv).sort().map(t=>[t,this.extraEnv[t]??null]);return w("sha256").update(JSON.stringify({sessionId:this.shennianSessionId,externalChannel:this.externalChannel,env:e,managedAgentPolicy:this.managedAgentPolicy})).digest("hex")}buildProcessEnv(e={}){if(!this.managedAgentPolicy)return D({...e,...this.extraEnv});const t=Object.fromEntries(Object.entries(this.extraEnv).filter(([r])=>r!=="SHENNIAN_MANAGED_ROOM_CONTEXT"&&r!=="SHENNIAN_MANAGED_ROOM_IPC_ROOT"));return L({agentType:"codex",providerEnv:t,sessionEnv:t,runtimeEnv:e})}async start(e,t,r,s){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,this.seq=0,this.persistNativeSession=s?.persistNativeSession!==!1,r&&(this.agentSessionId=r)}async prepareSend(e,t,r,s,i,n){const a=i?.trim()??"";if(a!==this.agentSystemPrompt&&(this.agentSystemPrompt=a,this.process&&await this.killProcess(!0)),!(!this.agentSessionId||this.activeTurnId)){this.preparedExternalOwnerThreadId=null;try{await this.ensureAppServer(I(t))}catch(d){if(!P(d))throw d;if(n?.source==="room_activation")throw new Error("codex_managed_thread_owned_externally",{cause:d});if(this.isThreadTailInterrupted(this.agentSessionId))throw new Error(C,{cause:d});this.preparedExternalOwnerThreadId=this.agentSessionId}}}async send(e,t,r,s,i,n){if(n?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");if(n?.source==="room_activation"&&(!this.configuredManagedRoomContextToken||!this.configuredManagedRoomExecutor))throw new Error("codex_managed_room_tool_context_unavailable");this.activeTurnId&&(await this.interruptActiveTurn().catch(()=>{}),await this.killProcess(!0)),this.configurationRestartRequired&&this.process&&await this.killProcess(!0),this.configurationRestartRequired=!1;const a=I(t),d=re(r),c=i?.trim()??"";if(c!==this.agentSystemPrompt&&(this.agentSystemPrompt=c,this.process&&await this.killProcess(!0)),n?.source==="room_activation"&&this.managedRoomTools.configure(this.configuredManagedRoomContextToken,this.configuredManagedRoomExecutor),this.runId=
|
|
4
|
-
${l}`.trim();if(u!==0){m(new Error(f||`codex queue exited with code ${u??"unknown"}`));return}const M=f.match(/Queued message\s+([^\s]+)\s+for thread/i)?.[1];m(void 0,M)})})}async resume(e){await this.killProcess(),this.agentSessionId=e,this.resetTextState()}async setTitle(e,t,r){const s=e.trim(),i=t.replace(/\s+/g," ").trim();!s||!i||(this.agentSessionId=s,r?.trim()&&(this.workDir=r),await this.ensureAppServer(),await this.sendRpc("thread/name/set",{threadId:s,name:i}))}async stop(){this.managedRoomTools.deactivateTurn(),await this.interruptActiveTurn().catch(()=>{}),await this.killProcess()}async getStatus(){const e=this.agentSessionId,t=this.activeTurnId;if(!e||!this.process)return{active:!1,runId:t,runPhase:null,canStop:!1};let r=null;try{r=(await this.sendRpc("thread/read",{threadId:e,includeTurns:!1},5e3)).thread?.status??null}catch{}const s=r?.type==="active"||!!t;return{active:s,runId:t,runPhase:s?this.mapThreadStatusToRunPhase(r):null,canStop:!!(s&&t&&this.process)}}spawnCodex(e){const t=y("codex");if(!t){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const r=x(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv()});this.process=r,S({input:r.stdout}).on("line",n=>{if(n.trim())try{const a=JSON.parse(n);this.handleStreamEvent(a)}catch{this.emitEvent({state:"delta",text:n})}});let i="";r.stderr?.on("data",n=>{i+=n.toString()}),r.on("close",n=>{this.process===r&&(this.process=null,this.clearForceCloseTimer(),this.handleProcessClose(n,i))}),r.on("error",n=>{this.process===r&&(this.process=null,n.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",n))})}spawnAppServer(){const e=y("codex");if(!e){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const t=["app-server","--listen","stdio://"],r=this.options.modelInstructionsFile??F(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,this.agentSystemPrompt);r&&t.push("-c",`model_instructions_file=${JSON.stringify(r)}`);const s=x(e,t,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});this.process=s,this.stderrBuf="",this.appServerStartupError=null,S({input:s.stdout}).on("line",n=>{if(!n.trim())return;let a;try{a=JSON.parse(n)}catch{this.handleAppServerNonJsonStdout(n);return}this.handleAppServerMessage(a)}),s.stderr?.on("data",n=>{this.stderrBuf+=n.toString()}),s.on("close",n=>{if(this.process!==s)return;const a=this.activeTurnId!=null;this.process=null,this.appServerReady=null,this.activeTurnId=null,this.managedRoomTools.reset(),this.clearConfiguredManagedRoomContext(),this.rejectAllPending(new Error(_(this.stderrBuf)||`codex app-server exited with code ${n}`)),this.terminalState==="open"&&a&&this.handleProcessClose(n,this.stderrBuf)}),s.on("error",n=>{this.process===s&&(this.process=null,this.managedRoomTools.reset(),this.clearConfiguredManagedRoomContext(),this.rejectAllPending(n),n.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",n))})}async ensureAppServer(e){return this.appServerReady?this.appServerReady:(this.spawnAppServer(),this.appServerReady=this.initializeAppServer(e).catch(async t=>{throw this.appServerReady=null,await this.killProcess(),t}),this.appServerReady)}async initializeAppServer(e){const t=I(e);await this.sendRpc("initialize",{clientInfo:V,capabilities:{experimentalApi:!0}}),this.managedPermissionConfig=this.managedAgentPolicy?await this.buildManagedPermissionConfig():null;const r=!!(this.configuredManagedRoomContextToken||this.configuredManagedRoomExecutor);if(this.managedPermissionConfig&&r&&!this.managedRoomTools.isConfigured()&&(this.managedRoomTools.configure(this.configuredManagedRoomContextToken,this.configuredManagedRoomExecutor),!this.managedRoomTools.isConfigured()))throw new Error("codex_managed_room_tool_context_unavailable");if(this.agentSessionId&&this.managedPermissionConfig&&r&&this.agentSessionId!==this.managedRoomToolThreadId){if(!this.managedRoomTools.isConfigured())throw new Error("codex_managed_room_tool_context_unavailable");this.agentSessionId=null}if(this.agentSessionId){const c={threadId:this.agentSessionId,cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},persistExtendedHistory:!0,...this.options.hidden||!this.persistNativeSession?{threadSource:"subagent"}:{},...t?{model:t}:{}};let l;try{l=await this.sendRpc("thread/resume",{...c,excludeTurns:!0})}catch(p){if(!oe(p))throw p;l=await this.sendRpc("thread/resume",c)}this.agentSessionId=l.thread?.id??this.agentSessionId,this.managedPermissionConfig&&(R(l,this.managedPermissionConfig.profileId),r&&(this.managedRoomTools.bindThread(this.agentSessionId),this.managedRoomToolThreadId=this.agentSessionId));return}const s=this.options.hidden||!this.persistNativeSession,i=s?void 0:await this.resolveCodexProjectId(),n={cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},serviceName:"Shennian",threadSource:s?"subagent":"user",experimentalRawEvents:!1,persistExtendedHistory:!0,...this.managedPermissionConfig&&r?{dynamicTools:z}:{},...i?{projectId:i}:{},...t?{model:t}:{}};let a;if(s&&!this.managedPermissionConfig)a=await this.sendRpc("thread/start",n);else try{a=await this.sendRpc("thread/start",{...n,historyMode:"paginated"})}catch(c){throw ie(c)?new Error("\u5F53\u524D Codex CLI \u4E0D\u652F\u6301\u521B\u5EFA\u53EF\u5728 Codex Desktop \u4E2D\u7EE7\u7EED\u7684\u65B0\u4F1A\u8BDD\uFF0C\u8BF7\u624B\u52A8\u5347\u7EA7 Codex CLI \u540E\u91CD\u8BD5\u3002\u795E\u5FF5\u4E0D\u4F1A\u81EA\u52A8\u5347\u7EA7\uFF0C\u4E5F\u4E0D\u4F1A\u964D\u7EA7\u521B\u5EFA\u4E0D\u53EF\u89C1\u7684 legacy \u4F1A\u8BDD\u3002",{cause:c}):c}const d=a.thread?.id;if(!d)throw new Error("codex app-server thread/start returned no thread id");this.managedPermissionConfig&&(R(a,this.managedPermissionConfig.profileId),r&&(this.managedRoomTools.bindThread(d),this.managedRoomToolThreadId=d)),this.agentSessionId=d}async buildManagedPermissionConfig(){const e=this.managedAgentPolicy;if(!e)throw new Error("managed_agent_policy_missing");const t=e.adapterPolicy.externalCapabilities.mcp?[]:H(await this.sendRpc("config/read",{cwd:e.adapterPolicy.workDir}));return J(U(e),e.adapterPolicy.workDir,{configuredMcpServerNames:t,shellSubprocessPolicy:e.environmentPolicy.shellSubprocess})}async resolveCodexProjectId(){const e=this.workDir?.trim();if(e)try{const t=[];let r;do{const c=await this.sendRpc("project/list",{limit:100,...r?{cursor:r}:{}});t.push(...c.data??[]),r=c.nextCursor}while(r);const s=de(t,e);if(s)return s;const i=g.resolve(e),n=`shennian-project-${w("sha256").update(i).digest("hex")}`,d=(await this.sendRpc("project/create",{idempotencyKey:n,name:g.basename(i)||i,roots:[{path:i}],metadata:{createdBy:"Shennian"}})).project?.id;if(!d)throw new Error("codex app-server project/create returned no project id");return d}catch(t){throw ae(t)?new Error("\u5F53\u524D Codex CLI \u4E0D\u652F\u6301\u521B\u5EFA\u53EF\u5F52\u5C5E\u5230 Codex Desktop \u9879\u76EE\u7684\u65B0\u4F1A\u8BDD\uFF0C\u8BF7\u624B\u52A8\u5347\u7EA7 Codex CLI \u540E\u91CD\u8BD5\u3002\u795E\u5FF5\u4E0D\u4F1A\u81EA\u52A8\u5347\u7EA7\uFF0C\u4E5F\u4E0D\u4F1A\u76F4\u63A5\u4FEE\u6539 Codex Desktop \u7684\u79C1\u6709\u4FA7\u680F\u7D22\u5F15\u3002",{cause:t}):t}}async startTurnWithRecovery(e,t,r,s,i){try{return await this.startTurn(e,t,r,s,i)}catch(n){if(!v(n))throw n;if(this.managedPermissionConfig){this.logManagedThreadLifecycle("rollout.missing",{threadId:e,recovery:"recreate"}),await this.killProcess(!0),this.agentSessionId=null,this.managedRoomToolThreadId=null,await this.ensureAppServer(r);const a=this.agentSessionId;if(!a)throw new Error("codex app-server did not recreate the managed thread",{cause:n});return this.logManagedThreadLifecycle("thread.recreated",{previousThreadId:e,threadId:a}),this.emitEvent({state:"start",agentSessionId:a}),await this.startFreshManagedTurnWithReadinessRetry(a,t,r,s,i)}return await this.killProcess(),await this.ensureAppServer(r),await this.startTurn(e,t,r,s,i)}}async startFreshManagedTurnWithReadinessRetry(e,t,r,s,i){const n=[0,100,300,1e3];let a;for(let d=0;d<n.length;d+=1){const c=n[d];c>0&&await new Promise(l=>setTimeout(l,c));try{const l=await this.startTurn(e,t,r,s,i);return d>0&&this.logManagedThreadLifecycle("rollout.ready",{threadId:e,attempt:d+1}),l}catch(l){if(a=l,!v(l)||d===n.length-1)throw l;this.logManagedThreadLifecycle("rollout.retry",{threadId:e,attempt:d+2,delayMs:n[d+1]})}}throw a}logManagedThreadLifecycle(e,t){console.error(`[codex.managed] ${JSON.stringify({event:e,...t})}`)}async startTurn(e,t,r,s,i){try{return await this.sendRpc("turn/start",{threadId:e,input:[le(t,i)],approvalPolicy:"never",...this.managedPermissionConfig?{}:{sandboxPolicy:{type:"dangerFullAccess"}},...r?{model:r}:{},...s?{effort:s}:{}})}catch(n){throw s&&j(n)?new Error(`Codex app-server does not accept reasoning effort "${s}" for this turn. Refresh models or upgrade Codex CLI, then retry.`,{cause:n}):n}}async interruptActiveTurn(){const e=this.agentSessionId,t=this.activeTurnId;this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),!(!e||!t||!this.process)&&await this.sendRpc("turn/interrupt",{threadId:e,turnId:t},5e3)}sendRpc(e,t,r=6e4){if(this.appServerStartupError)return Promise.reject(this.appServerStartupError);const s=this.process;if(!s?.stdin)return Promise.reject(new Error("codex app-server is not running"));const i=this.rpcSeq++,n=JSON.stringify({id:i,method:e,params:t});return new Promise((a,d)=>{const c=setTimeout(()=>{this.pendingRequests.delete(i),d(new Error(`codex app-server request timed out: ${e}`))},r);this.pendingRequests.set(i,{resolve:a,reject:d,timer:c}),s.stdin.write(`${n}
|
|
5
|
-
`,l=>{l&&(clearTimeout(c),this.pendingRequests.delete(i),d(l))})})}handleAppServerMessage(e){if(e.method==="item/tool/call"&&e.id!=null){this.handleManagedRoomToolCall(e.id,e.params??{});return}if(e.id!=null){const r=typeof e.id=="number"?this.pendingRequests.get(e.id):void 0;r&&(clearTimeout(r.timer),this.pendingRequests.delete(e.id),e.error?r.reject(new Error(e.error.message||`codex app-server error ${e.error.code??""}`.trim())):r.resolve(e.result))}if(!e.method)return;const t=e.params??{};switch(e.method){case"turn/started":{const r=typeof t.turn=="object"&&t.turn!==null?t.turn:null,s=typeof r?.id=="string"?r.id:null,i=typeof t.threadId=="string"?t.threadId:"";s&&(this.activeTurnId=s,this.managedRoomToolThreadId===i&&!this.managedRoomTools.bindTurn(i,s)&&this.emitErrorIfOpen("codex_managed_room_turn_binding_failed"));break}case"thread/status/changed":{const r=typeof t.threadId=="string"?t.threadId:"";if(r&&this.agentSessionId&&r!==this.agentSessionId)break;const s=typeof t.status=="object"&&t.status!==null?t.status:null;s?.type==="active"&&this.emitEvent({state:"heartbeat",runPhase:this.mapThreadStatusToRunPhase(s)});break}case"item/agentMessage/delta":{const r=typeof t.itemId=="string"?t.itemId:"";r&&this.deltaItemIds.add(r),typeof t.delta=="string"&&this.emitText(t.delta);break}case"item/completed":this.handleAppServerCompletedItem(t);break;case"thread/tokenUsage/updated":this.handleAppServerUsage(t);break;case"turn/completed":{const r=typeof t.turn=="object"&&t.turn!==null?t.turn:null,s=typeof r?.id=="string"?r.id:null;(!this.activeTurnId||s===this.activeTurnId)&&(this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),this.emitFinalIfOpen());break}case"turn/failed":{const r=E(t);if(this.scheduleManagedAsyncRolloutRecovery(r))return;this.emitErrorIfOpen(r||"turn failed");break}case"error":{const r=E(t);if(Z(r)){this.emitTransientStatus(r);return}if(this.scheduleManagedAsyncRolloutRecovery(r))return;this.emitErrorIfOpen(r);break}}}scheduleManagedAsyncRolloutRecovery(e){if(!this.managedPermissionConfig||!this.managedTurnReplay||!
|
|
3
|
+
`)}function me(o,e,t){return["queue","--thread",o,"--message",pe(e,t)]}function A(o){return o.replace(/[[\]\\]/g,"\\$&")}class ge extends q{options;type="codex";process=null;workDir=null;seq=0;runId="";pendingUsage;agentSessionId=null;hasEmittedText=!1;textTail="";terminalState="open";forceCloseTimer=null;stderrBuf="";rpcSeq=1;pendingRequests=new Map;appServerReady=null;appServerStartupError=null;activeTurnId=null;deltaItemIds=new Set;lastTransientStatus="";agentSystemPrompt="";managedAgentPolicy=null;managedPermissionConfig=null;configurationRestartRequired=!1;managedRoomTools=new B;configuredManagedRoomContextToken=null;configuredManagedRoomExecutor=null;managedRoomToolThreadId=null;preparedExternalOwnerThreadId=null;managedTurnReplay=null;managedAsyncRecovery=null;persistNativeSession=!0;nativeSessionVisibility="user";constructor(e={}){super(),this.options=e}externalChannel=null;shennianSessionId=null;extraEnv={};configure(e){const t=this.configurationFingerprint();this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{},"managedAgentPolicy"in e&&(this.managedAgentPolicy=e.managedAgentPolicy??null),("managedRoomContextToken"in e||"managedRoomExecutor"in e)&&(this.managedRoomTools.deactivateTurn(),this.configuredManagedRoomContextToken=e.managedRoomContextToken?.trim()||null,this.configuredManagedRoomExecutor=e.managedRoomExecutor??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir);const r=this.configurationFingerprint();t!==r&&this.process&&(this.configurationRestartRequired=!0)}configurationFingerprint(){const e=Object.keys(this.extraEnv).sort().map(t=>[t,this.extraEnv[t]??null]);return w("sha256").update(JSON.stringify({sessionId:this.shennianSessionId,externalChannel:this.externalChannel,env:e,managedAgentPolicy:this.managedAgentPolicy})).digest("hex")}buildProcessEnv(e={}){if(!this.managedAgentPolicy)return D({...e,...this.extraEnv});const t=Object.fromEntries(Object.entries(this.extraEnv).filter(([r])=>r!=="SHENNIAN_MANAGED_ROOM_CONTEXT"&&r!=="SHENNIAN_MANAGED_ROOM_IPC_ROOT"));return L({agentType:"codex",providerEnv:t,sessionEnv:t,runtimeEnv:e})}async start(e,t,r,s){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,this.seq=0,this.persistNativeSession=s?.persistNativeSession!==!1,this.nativeSessionVisibility=s?.nativeSessionVisibility??"user",r&&(this.agentSessionId=r)}async prepareSend(e,t,r,s,i,n){const a=i?.trim()??"";if(a!==this.agentSystemPrompt&&(this.agentSystemPrompt=a,this.process&&await this.killProcess(!0)),!(!this.agentSessionId||this.activeTurnId)){this.preparedExternalOwnerThreadId=null;try{await this.ensureAppServer(I(t))}catch(d){if(!P(d))throw d;if(n?.source==="room_activation")throw new Error("codex_managed_thread_owned_externally",{cause:d});if(this.isThreadTailInterrupted(this.agentSessionId))throw new Error(C,{cause:d});this.preparedExternalOwnerThreadId=this.agentSessionId}}}async send(e,t,r,s,i,n){if(n?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");if(n?.source==="room_activation"&&(!this.configuredManagedRoomContextToken||!this.configuredManagedRoomExecutor))throw new Error("codex_managed_room_tool_context_unavailable");this.activeTurnId&&(await this.interruptActiveTurn().catch(()=>{}),await this.killProcess(!0)),this.configurationRestartRequired&&this.process&&await this.killProcess(!0),this.configurationRestartRequired=!1;const a=I(t),d=re(r),c=i?.trim()??"";if(c!==this.agentSystemPrompt&&(this.agentSystemPrompt=c,this.process&&await this.killProcess(!0)),n?.source==="room_activation"&&this.managedRoomTools.configure(this.configuredManagedRoomContextToken,this.configuredManagedRoomExecutor),this.runId=O(),this.seq=0,this.resetRunState(),this.agentSessionId&&this.preparedExternalOwnerThreadId===this.agentSessionId)return this.preparedExternalOwnerThreadId=null,{deliveryMode:"external_owner_queue",queuedSubmissionId:await this.enqueueForExternalOwner(this.agentSessionId,e,s)};try{await this.ensureAppServer(a)}catch(h){if(!this.agentSessionId||!P(h))throw h;if(n?.source==="room_activation")throw new Error("codex_managed_thread_owned_externally",{cause:h});if(this.isThreadTailInterrupted(this.agentSessionId))throw new Error(C,{cause:h});return{deliveryMode:"external_owner_queue",queuedSubmissionId:await this.enqueueForExternalOwner(this.agentSessionId,e,s)}}const l=this.agentSessionId;if(!l)throw new Error("codex app-server did not create a thread");this.emitEvent({state:"start",agentSessionId:l}),this.managedTurnReplay=this.managedPermissionConfig?{text:e,modelId:a,reasoningEffort:d,attachments:s,recoveryCount:0}:null;const p=await this.startTurnWithRecovery(l,e,a,d,s),m=this.agentSessionId??l;if(this.activeTurnId=p.turn?.id??null,this.managedRoomToolThreadId===m&&this.activeTurnId&&!this.managedRoomTools.bindTurn(m,this.activeTurnId))throw await this.killProcess(),new Error("codex_managed_room_turn_binding_failed");return{deliveryMode:"direct"}}isThreadTailInterrupted(e){return this.options.isThreadTailInterrupted?.(e)??$(e)}async enqueueForExternalOwner(e,t,r){const s=T("codex");if(!s)throw new Error('Command "codex" not found. Is OpenAI Codex CLI installed?');const i=me(e,t,r),n=x(s,i,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});return await new Promise((a,d)=>{let c="",l="",p=!1;const m=(u,f)=>{p||(p=!0,clearTimeout(h),u?d(u):a(f))},h=setTimeout(()=>{n.kill("SIGTERM"),m(new Error("Codex Desktop message queue timed out"))},6e4);h.unref?.(),n.stdout?.on("data",u=>{c+=u.toString()}),n.stderr?.on("data",u=>{l+=u.toString()}),n.once("error",u=>m(u)),n.once("close",u=>{const f=`${c}
|
|
4
|
+
${l}`.trim();if(u!==0){m(new Error(f||`codex queue exited with code ${u??"unknown"}`));return}const M=f.match(/Queued message\s+([^\s]+)\s+for thread/i)?.[1];m(void 0,M)})})}async resume(e){await this.killProcess(),this.agentSessionId=e,this.resetTextState()}async setTitle(e,t,r){const s=e.trim(),i=t.replace(/\s+/g," ").trim();!s||!i||(this.agentSessionId=s,r?.trim()&&(this.workDir=r),await this.ensureAppServer(),await this.sendRpc("thread/name/set",{threadId:s,name:i}))}async stop(){this.managedRoomTools.deactivateTurn(),await this.interruptActiveTurn().catch(()=>{}),await this.killProcess()}async getStatus(){const e=this.agentSessionId,t=this.activeTurnId;if(!e||!this.process)return{active:!1,runId:t,runPhase:null,canStop:!1};let r=null;try{r=(await this.sendRpc("thread/read",{threadId:e,includeTurns:!1},5e3)).thread?.status??null}catch{}const s=r?.type==="active"||!!t;return{active:s,runId:t,runPhase:s?this.mapThreadStatusToRunPhase(r):null,canStop:!!(s&&t&&this.process)}}spawnCodex(e){const t=T("codex");if(!t){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const r=x(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv()});this.process=r,S({input:r.stdout}).on("line",n=>{if(n.trim())try{const a=JSON.parse(n);this.handleStreamEvent(a)}catch{this.emitEvent({state:"delta",text:n})}});let i="";r.stderr?.on("data",n=>{i+=n.toString()}),r.on("close",n=>{this.process===r&&(this.process=null,this.clearForceCloseTimer(),this.handleProcessClose(n,i))}),r.on("error",n=>{this.process===r&&(this.process=null,n.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",n))})}spawnAppServer(){const e=T("codex");if(!e){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const t=["app-server","--listen","stdio://"],r=this.options.modelInstructionsFile??F(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,this.agentSystemPrompt);r&&t.push("-c",`model_instructions_file=${JSON.stringify(r)}`);const s=x(e,t,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});this.process=s,this.stderrBuf="",this.appServerStartupError=null,S({input:s.stdout}).on("line",n=>{if(!n.trim())return;let a;try{a=JSON.parse(n)}catch{this.handleAppServerNonJsonStdout(n);return}this.handleAppServerMessage(a)}),s.stderr?.on("data",n=>{this.stderrBuf+=n.toString()}),s.on("close",n=>{if(this.process!==s)return;const a=this.activeTurnId!=null;this.process=null,this.appServerReady=null,this.activeTurnId=null,this.managedRoomTools.reset(),this.clearConfiguredManagedRoomContext(),this.rejectAllPending(new Error(_(this.stderrBuf)||`codex app-server exited with code ${n}`)),this.terminalState==="open"&&a&&this.handleProcessClose(n,this.stderrBuf)}),s.on("error",n=>{this.process===s&&(this.process=null,this.managedRoomTools.reset(),this.clearConfiguredManagedRoomContext(),this.rejectAllPending(n),n.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",n))})}async ensureAppServer(e){return this.appServerReady?this.appServerReady:(this.spawnAppServer(),this.appServerReady=this.initializeAppServer(e).catch(async t=>{throw this.appServerReady=null,await this.killProcess(),t}),this.appServerReady)}async initializeAppServer(e){const t=I(e);await this.sendRpc("initialize",{clientInfo:H,capabilities:{experimentalApi:!0}}),this.managedPermissionConfig=this.managedAgentPolicy?await this.buildManagedPermissionConfig():null;const r=!!(this.configuredManagedRoomContextToken||this.configuredManagedRoomExecutor);if(this.managedPermissionConfig&&r&&!this.managedRoomTools.isConfigured()&&(this.managedRoomTools.configure(this.configuredManagedRoomContextToken,this.configuredManagedRoomExecutor),!this.managedRoomTools.isConfigured()))throw new Error("codex_managed_room_tool_context_unavailable");if(this.agentSessionId&&this.managedPermissionConfig&&r&&this.agentSessionId!==this.managedRoomToolThreadId){if(!this.managedRoomTools.isConfigured())throw new Error("codex_managed_room_tool_context_unavailable");this.agentSessionId=null}if(this.agentSessionId){const c={threadId:this.agentSessionId,cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},persistExtendedHistory:!0,...this.options.hidden||this.nativeSessionVisibility==="internal"||!this.persistNativeSession?{threadSource:"subagent"}:{},...t?{model:t}:{}};let l;try{l=await this.sendRpc("thread/resume",{...c,excludeTurns:!0})}catch(p){if(!oe(p))throw p;l=await this.sendRpc("thread/resume",c)}this.agentSessionId=l.thread?.id??this.agentSessionId,this.managedPermissionConfig&&(R(l,this.managedPermissionConfig.profileId),r&&(this.managedRoomTools.bindThread(this.agentSessionId),this.managedRoomToolThreadId=this.agentSessionId));return}const s=this.options.hidden||this.nativeSessionVisibility==="internal"||!this.persistNativeSession,i=s?void 0:await this.resolveCodexProjectId(),n={cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},serviceName:"Shennian",threadSource:s?"subagent":"user",experimentalRawEvents:!1,persistExtendedHistory:!0,...this.managedPermissionConfig&&r?{dynamicTools:z}:{},...i?{projectId:i}:{},...t?{model:t}:{}};let a;if(s&&!this.managedPermissionConfig)a=await this.sendRpc("thread/start",n);else try{a=await this.sendRpc("thread/start",{...n,historyMode:"paginated"})}catch(c){throw ie(c)?new Error("\u5F53\u524D Codex CLI \u4E0D\u652F\u6301\u521B\u5EFA\u53EF\u5728 Codex Desktop \u4E2D\u7EE7\u7EED\u7684\u65B0\u4F1A\u8BDD\uFF0C\u8BF7\u624B\u52A8\u5347\u7EA7 Codex CLI \u540E\u91CD\u8BD5\u3002\u795E\u5FF5\u4E0D\u4F1A\u81EA\u52A8\u5347\u7EA7\uFF0C\u4E5F\u4E0D\u4F1A\u964D\u7EA7\u521B\u5EFA\u4E0D\u53EF\u89C1\u7684 legacy \u4F1A\u8BDD\u3002",{cause:c}):c}const d=a.thread?.id;if(!d)throw new Error("codex app-server thread/start returned no thread id");this.managedPermissionConfig&&(R(a,this.managedPermissionConfig.profileId),r&&(this.managedRoomTools.bindThread(d),this.managedRoomToolThreadId=d)),this.agentSessionId=d}async buildManagedPermissionConfig(){const e=this.managedAgentPolicy;if(!e)throw new Error("managed_agent_policy_missing");const t=e.adapterPolicy.externalCapabilities.mcp?[]:J(await this.sendRpc("config/read",{cwd:e.adapterPolicy.workDir}));return V(U(e),e.adapterPolicy.workDir,{configuredMcpServerNames:t,shellSubprocessPolicy:e.environmentPolicy.shellSubprocess})}async resolveCodexProjectId(){const e=this.workDir?.trim();if(e)try{const t=[];let r;do{const c=await this.sendRpc("project/list",{limit:100,...r?{cursor:r}:{}});t.push(...c.data??[]),r=c.nextCursor}while(r);const s=de(t,e);if(s)return s;const i=g.resolve(e),n=`shennian-project-${w("sha256").update(i).digest("hex")}`,d=(await this.sendRpc("project/create",{idempotencyKey:n,name:g.basename(i)||i,roots:[{path:i}],metadata:{createdBy:"Shennian"}})).project?.id;if(!d)throw new Error("codex app-server project/create returned no project id");return d}catch(t){throw ae(t)?new Error("\u5F53\u524D Codex CLI \u4E0D\u652F\u6301\u521B\u5EFA\u53EF\u5F52\u5C5E\u5230 Codex Desktop \u9879\u76EE\u7684\u65B0\u4F1A\u8BDD\uFF0C\u8BF7\u624B\u52A8\u5347\u7EA7 Codex CLI \u540E\u91CD\u8BD5\u3002\u795E\u5FF5\u4E0D\u4F1A\u81EA\u52A8\u5347\u7EA7\uFF0C\u4E5F\u4E0D\u4F1A\u76F4\u63A5\u4FEE\u6539 Codex Desktop \u7684\u79C1\u6709\u4FA7\u680F\u7D22\u5F15\u3002",{cause:t}):t}}async startTurnWithRecovery(e,t,r,s,i){try{return await this.startTurn(e,t,r,s,i)}catch(n){if(!y(n))throw n;if(this.managedPermissionConfig){this.logManagedThreadLifecycle("rollout.missing",{threadId:e,recovery:"recreate"}),await this.killProcess(!0),this.agentSessionId=null,this.managedRoomToolThreadId=null,await this.ensureAppServer(r);const a=this.agentSessionId;if(!a)throw new Error("codex app-server did not recreate the managed thread",{cause:n});return this.logManagedThreadLifecycle("thread.recreated",{previousThreadId:e,threadId:a}),this.emitEvent({state:"start",agentSessionId:a}),await this.startFreshManagedTurnWithReadinessRetry(a,t,r,s,i)}return await this.killProcess(),await this.ensureAppServer(r),await this.startTurn(e,t,r,s,i)}}async startFreshManagedTurnWithReadinessRetry(e,t,r,s,i){const n=[0,100,300,1e3];let a;for(let d=0;d<n.length;d+=1){const c=n[d];c>0&&await new Promise(l=>setTimeout(l,c));try{const l=await this.startTurn(e,t,r,s,i);return d>0&&this.logManagedThreadLifecycle("rollout.ready",{threadId:e,attempt:d+1}),l}catch(l){if(a=l,!y(l)||d===n.length-1)throw l;this.logManagedThreadLifecycle("rollout.retry",{threadId:e,attempt:d+2,delayMs:n[d+1]})}}throw a}logManagedThreadLifecycle(e,t){console.error(`[codex.managed] ${JSON.stringify({event:e,...t})}`)}async startTurn(e,t,r,s,i){try{return await this.sendRpc("turn/start",{threadId:e,input:[le(t,i)],approvalPolicy:"never",...this.managedPermissionConfig?{}:{sandboxPolicy:{type:"dangerFullAccess"}},...r?{model:r}:{},...s?{effort:s}:{}})}catch(n){throw s&&j(n)?new Error(`Codex app-server does not accept reasoning effort "${s}" for this turn. Refresh models or upgrade Codex CLI, then retry.`,{cause:n}):n}}async interruptActiveTurn(){const e=this.agentSessionId,t=this.activeTurnId;this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),!(!e||!t||!this.process)&&await this.sendRpc("turn/interrupt",{threadId:e,turnId:t},5e3)}sendRpc(e,t,r=6e4){if(this.appServerStartupError)return Promise.reject(this.appServerStartupError);const s=this.process;if(!s?.stdin)return Promise.reject(new Error("codex app-server is not running"));const i=this.rpcSeq++,n=JSON.stringify({id:i,method:e,params:t});return new Promise((a,d)=>{const c=setTimeout(()=>{this.pendingRequests.delete(i),d(new Error(`codex app-server request timed out: ${e}`))},r);this.pendingRequests.set(i,{resolve:a,reject:d,timer:c}),s.stdin.write(`${n}
|
|
5
|
+
`,l=>{l&&(clearTimeout(c),this.pendingRequests.delete(i),d(l))})})}handleAppServerMessage(e){if(e.method==="item/tool/call"&&e.id!=null){this.handleManagedRoomToolCall(e.id,e.params??{});return}if(e.id!=null){const r=typeof e.id=="number"?this.pendingRequests.get(e.id):void 0;r&&(clearTimeout(r.timer),this.pendingRequests.delete(e.id),e.error?r.reject(new Error(e.error.message||`codex app-server error ${e.error.code??""}`.trim())):r.resolve(e.result))}if(!e.method)return;const t=e.params??{};switch(e.method){case"turn/started":{const r=typeof t.turn=="object"&&t.turn!==null?t.turn:null,s=typeof r?.id=="string"?r.id:null,i=typeof t.threadId=="string"?t.threadId:"";s&&(this.activeTurnId=s,this.managedRoomToolThreadId===i&&!this.managedRoomTools.bindTurn(i,s)&&this.emitErrorIfOpen("codex_managed_room_turn_binding_failed"));break}case"thread/status/changed":{const r=typeof t.threadId=="string"?t.threadId:"";if(r&&this.agentSessionId&&r!==this.agentSessionId)break;const s=typeof t.status=="object"&&t.status!==null?t.status:null;s?.type==="active"&&this.emitEvent({state:"heartbeat",runPhase:this.mapThreadStatusToRunPhase(s)});break}case"item/agentMessage/delta":{const r=typeof t.itemId=="string"?t.itemId:"";r&&this.deltaItemIds.add(r),typeof t.delta=="string"&&this.emitText(t.delta);break}case"item/completed":this.handleAppServerCompletedItem(t);break;case"thread/tokenUsage/updated":this.handleAppServerUsage(t);break;case"turn/completed":{const r=typeof t.turn=="object"&&t.turn!==null?t.turn:null,s=typeof r?.id=="string"?r.id:null;(!this.activeTurnId||s===this.activeTurnId)&&(this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),this.emitFinalIfOpen());break}case"turn/failed":{const r=E(t);if(this.scheduleManagedAsyncRolloutRecovery(r))return;this.emitErrorIfOpen(r||"turn failed");break}case"error":{const r=E(t);if(Z(r)){this.emitTransientStatus(r);return}if(this.scheduleManagedAsyncRolloutRecovery(r))return;this.emitErrorIfOpen(r);break}}}scheduleManagedAsyncRolloutRecovery(e){if(!this.managedPermissionConfig||!this.managedTurnReplay||!y(new Error(e)))return!1;if(this.managedAsyncRecovery)return!0;if(this.managedTurnReplay.recoveryCount>=2)return!1;this.managedTurnReplay.recoveryCount+=1;const t=this.managedTurnReplay.recoveryCount;return this.logManagedThreadLifecycle("rollout.failed-async",{threadId:this.agentSessionId,recoveryCount:t}),this.managedAsyncRecovery=this.recoverManagedTurnAfterAsyncRolloutFailure().catch(r=>{const s=y(r)?"no rollout found after managed thread recovery":r instanceof Error?r.message:"managed thread recovery failed";this.emitErrorIfOpen(s)}).finally(()=>{this.managedAsyncRecovery=null}),!0}async recoverManagedTurnAfterAsyncRolloutFailure(){const e=this.managedTurnReplay,t=this.agentSessionId;if(!e||!t||this.terminalState!=="open")return;this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),await this.killProcess(!0),this.agentSessionId=null,this.managedRoomToolThreadId=null,await this.ensureAppServer(e.modelId);const r=this.agentSessionId;if(!r)throw new Error("codex app-server did not recreate the managed thread");this.logManagedThreadLifecycle("thread.recreated-async",{previousThreadId:t,threadId:r,recoveryCount:e.recoveryCount}),this.emitEvent({state:"start",agentSessionId:r});const s=await this.startFreshManagedTurnWithReadinessRetry(r,e.text,e.modelId,e.reasoningEffort,e.attachments);if(this.activeTurnId=s.turn?.id??null,this.managedRoomToolThreadId===r&&this.activeTurnId&&!this.managedRoomTools.bindTurn(r,this.activeTurnId))throw new Error("codex_managed_room_turn_binding_failed")}async handleManagedRoomToolCall(e,t){const r=await this.managedRoomTools.execute(t);if(this.writeAppServerResponse(e,r),r.success&&t.tool==="shennian_room_send"){const s=typeof t.arguments=="object"&&t.arguments!==null?t.arguments:null,i=typeof s?.text=="string"?s.text.trim():"";i&&this.emitText(i,"block")}}writeAppServerResponse(e,t){const r=this.process;r?.stdin&&r.stdin.write(`${JSON.stringify({id:e,result:t})}
|
|
6
6
|
`)}mapThreadStatusToRunPhase(e){const t=e?.type==="active"&&Array.isArray(e.activeFlags)?e.activeFlags:[];return t.includes("waitingOnApproval")?"waiting_approval":t.includes("waitingOnUserInput")?"waiting_user_input":"thinking"}handleAppServerNonJsonStdout(e){const t=se(e);ee(t)&&this.failAppServerStartup(new Error("Codex \u672A\u767B\u5F55\u3002\u8BF7\u5148\u5728\u8FD9\u53F0\u7535\u8111\u7684\u7EC8\u7AEF\u8FD0\u884C `codex`\uFF0C\u6309\u63D0\u793A\u5B8C\u6210 ChatGPT \u767B\u5F55\u6216\u914D\u7F6E API key\uFF0C\u7136\u540E\u518D\u56DE\u5230\u795E\u5FF5\u53D1\u9001\u6D88\u606F\u3002"))}failAppServerStartup(e){this.appServerStartupError||(this.appServerStartupError=e,this.rejectAllPending(e),this.process?.kill("SIGTERM"))}handleAppServerCompletedItem(e){const t=typeof e.item=="object"&&e.item!==null?e.item:null;if(!(!t||typeof t.type!="string")&&!Q(t))switch(t.type){case"agentMessage":{const r=typeof t.id=="string"?t.id:"";if(r&&this.deltaItemIds.has(r))return;typeof t.text=="string"&&this.emitText(t.text,"block");break}case"commandExecution":{const r=typeof t.command=="string"?t.command:"",s=typeof t.aggregatedOutput=="string"?t.aggregatedOutput:"";r&&this.emitEvent({state:"tool-call",name:"command_execution",args:{command:r}}),this.emitEvent({state:"tool-result",name:"command_execution",result:[s,t.exitCode!=null?`
|
|
7
|
-
(exit ${t.exitCode})`:""].join("").trim()});break}case"mcpToolCall":case"dynamicToolCall":{const r=typeof t.tool=="string"?t.tool:t.type;if(
|
|
8
|
-
(exit ${t.exit_code})`:""];this.emitEvent({state:"tool-result",name:"command_execution",result:r.join("").trim()||String(t.status??"")})}break}case"mcp_tool_call":{if(t.tool&&
|
|
9
|
-
`);this.emitText(r,"block")}break}case"approval_request":case"approvalRequest":case"permission_request":case"permissionRequest":case"user_input_required":case"userInputRequired":{(e.type==="item.started"||e.type==="item.completed")&&this.emitApprovalPending(t);break}case"error":{e.type==="item.completed"&&t.message&&this.emitEvent({state:"error",message:t.message});break}default:break}}handleStreamEvent(e){const t=e.type??e.event;switch(t?.startsWith("item.")&&this.handleCodexItemLine(e),t){case"turn.completed":{const s=e.usage;if(s){const i=s.input_tokens??s.prompt_tokens,n=s.output_tokens??s.completion_tokens;(i!=null||n!=null)&&(this.pendingUsage={inputTokens:i??0,outputTokens:n??0})}this.emitFinalIfOpen();return}case"turn.failed":{const s=typeof e.error=="object"&&e.error?.message?e.error.message:typeof e.error=="string"?e.error:"turn failed";this.emitErrorIfOpen(s);return}case"error":{const s=typeof e.message=="string"?e.message:"";if(s.includes("Reconnecting"))return;this.emitErrorIfOpen(s||(typeof e.error=="string"?e.error:"stream error"));return}case"thread.started":{const s=e.thread_id??e.session_id??e.id;typeof s=="string"&&s&&(this.agentSessionId=s,this.emitEvent({state:"start",agentSessionId:this.agentSessionId}));return}case"turn.started":return}if(t?.startsWith("item."))return;switch(e.type??e.event){case"message":case"text":case"content":{const s=e.content??e.text;s&&this.emitText(s);break}case"function_call":case"tool_call":{if(this.isApprovalLikeName(e.name)){this.emitApprovalPending(e);break}if(e.name&&
|
|
7
|
+
(exit ${t.exitCode})`:""].join("").trim()});break}case"mcpToolCall":case"dynamicToolCall":{const r=typeof t.tool=="string"?t.tool:t.type;if(v(r))return;const s=typeof t.arguments=="object"&&t.arguments!==null?t.arguments:{};this.emitEvent({state:"tool-call",name:r,args:s}),this.emitEvent({state:"tool-result",name:r,result:k(t.error??t.result??t.contentItems??"")});break}case"fileChange":break;case"approvalRequest":case"approval_request":case"permissionRequest":case"permission_request":case"userInputRequired":case"user_input_required":{this.emitApprovalPending(t);break}}}handleAppServerUsage(e){const t=typeof e.tokenUsage=="object"&&e.tokenUsage!==null?e.tokenUsage:null,r=typeof t?.last=="object"&&t.last!==null?t.last:null,s=typeof t?.total=="object"&&t.total!==null?t.total:null,i=r??s;if(!i)return;const n=typeof i.inputTokens=="number"?i.inputTokens:void 0,a=typeof i.outputTokens=="number"?i.outputTokens:void 0;(n!=null||a!=null)&&(this.pendingUsage={inputTokens:n??0,outputTokens:a??0})}handleCodexItemLine(e){const t=e.item;if(t?.type&&!Y(t))switch(t.type){case"agent_message":case"reasoning":{e.type==="item.completed"&&t.text&&this.emitText(t.text,"block");break}case"command_execution":{if(e.type==="item.started"&&t.command&&this.emitEvent({state:"tool-call",name:"command_execution",args:{command:t.command}}),e.type==="item.completed"){const r=[t.aggregated_output??"",t.exit_code!=null?`
|
|
8
|
+
(exit ${t.exit_code})`:""];this.emitEvent({state:"tool-result",name:"command_execution",result:r.join("").trim()||String(t.status??"")})}break}case"mcp_tool_call":{if(t.tool&&v(t.tool))return;if(e.type==="item.started"&&this.emitEvent({state:"tool-call",name:t.tool??"mcp_tool",args:{server:t.server,...t.arguments&&typeof t.arguments=="object"?t.arguments:{}}}),e.type==="item.completed"){const r=t.error&&typeof t.error=="object"?t.error.message:void 0;this.emitEvent({state:"tool-result",name:t.tool??"mcp_tool",result:r??k(t.result)})}break}case"file_change":break;case"web_search":{e.type==="item.completed"&&t.query&&this.emitText(`[web_search] ${t.query}`,"block");break}case"todo_list":{if(e.type==="item.completed"&&t.items?.length){const r=t.items.map(s=>`- [${s.completed?"x":" "}] ${s.text}`).join(`
|
|
9
|
+
`);this.emitText(r,"block")}break}case"approval_request":case"approvalRequest":case"permission_request":case"permissionRequest":case"user_input_required":case"userInputRequired":{(e.type==="item.started"||e.type==="item.completed")&&this.emitApprovalPending(t);break}case"error":{e.type==="item.completed"&&t.message&&this.emitEvent({state:"error",message:t.message});break}default:break}}handleStreamEvent(e){const t=e.type??e.event;switch(t?.startsWith("item.")&&this.handleCodexItemLine(e),t){case"turn.completed":{const s=e.usage;if(s){const i=s.input_tokens??s.prompt_tokens,n=s.output_tokens??s.completion_tokens;(i!=null||n!=null)&&(this.pendingUsage={inputTokens:i??0,outputTokens:n??0})}this.emitFinalIfOpen();return}case"turn.failed":{const s=typeof e.error=="object"&&e.error?.message?e.error.message:typeof e.error=="string"?e.error:"turn failed";this.emitErrorIfOpen(s);return}case"error":{const s=typeof e.message=="string"?e.message:"";if(s.includes("Reconnecting"))return;this.emitErrorIfOpen(s||(typeof e.error=="string"?e.error:"stream error"));return}case"thread.started":{const s=e.thread_id??e.session_id??e.id;typeof s=="string"&&s&&(this.agentSessionId=s,this.emitEvent({state:"start",agentSessionId:this.agentSessionId}));return}case"turn.started":return}if(t?.startsWith("item."))return;switch(e.type??e.event){case"message":case"text":case"content":{const s=e.content??e.text;s&&this.emitText(s);break}case"function_call":case"tool_call":{if(this.isApprovalLikeName(e.name)){this.emitApprovalPending(e);break}if(e.name&&v(e.name))return;this.emitEvent({state:"tool-call",name:e.name,args:e.arguments??e.input});break}case"function_call_output":case"tool_result":{if(e.name&&v(e.name))return;this.emitEvent({state:"tool-result",name:e.name,result:e.output??e.result});break}case"approval_request":case"approvalRequest":case"permission_request":case"permissionRequest":case"user_input_required":case"userInputRequired":{this.emitApprovalPending(e);break}case"error":break;case"done":case"completed":{e.usage&&(this.pendingUsage={inputTokens:e.usage.prompt_tokens??e.usage.input_tokens??0,outputTokens:e.usage.completion_tokens??e.usage.output_tokens??0}),this.emitFinalIfOpen();break}}}emitEvent(e){if(this.terminalState!=="open"&&e.state!=="final"&&e.state!=="error"&&e.state!=="aborted")return;const t={...e,runId:this.runId,seq:this.seq++};this.emit("agentEvent",t)}isApprovalLikeName(e){return typeof e!="string"?!1:/approval|permission|confirm|user[_-]?input|request[_-]?user/i.test(e)}emitApprovalPending(e){const t=typeof e.command=="string"?e.command:"",r=typeof e.title=="string"&&e.title.trim()?e.title:"\u9700\u8981\u7528\u6237\u786E\u8BA4",s=typeof e.prompt=="string"?e.prompt:typeof e.reason=="string"?e.reason:typeof e.message=="string"?e.message:t?`Codex \u8BF7\u6C42\u6267\u884C\u547D\u4EE4\uFF1A${t}`:"Codex \u6B63\u5728\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u540E\u7EE7\u7EED\u3002";this.emitEvent({state:"approval-pending",approval:{title:r,content:s,source:"codex",actionHint:"\u5F53\u524D\u795E\u5FF5\u6682\u4E0D\u652F\u6301\u8FDC\u7A0B\u786E\u8BA4\uFF0C\u8BF7\u56DE\u5230\u8FD0\u884C Codex \u7684\u7535\u8111/\u7EC8\u7AEF\u5B8C\u6210\u786E\u8BA4\u3002"}})}async killProcess(e=!1){const t=this.process;if(this.appServerReady=null,this.activeTurnId=null,this.managedRoomTools.reset(),e||this.clearConfiguredManagedRoomContext(),!t){this.rejectAllPending(new Error("codex app-server stopped"));return}this.process=null,this.clearForceCloseTimer(),t.kill("SIGTERM"),await new Promise(r=>{t.on("close",r),setTimeout(()=>{t.kill("SIGKILL"),r()},3e3)}),this.rejectAllPending(new Error("codex app-server stopped"))}rejectAllPending(e){for(const[t,r]of this.pendingRequests.entries())clearTimeout(r.timer),r.reject(e),this.pendingRequests.delete(t)}handleProcessClose(e,t){const r=_(t);if(e!==0&&e!==null){const s=r||`codex exited with code ${e}`;this.emitErrorIfOpen(s)}else te(r)?this.emitErrorIfOpen(r):this.emitFinalIfOpen();this.pendingUsage=void 0}emitFinalIfOpen(){this.terminalState==="open"&&(this.terminalState="final",this.managedTurnReplay=null,this.managedRoomTools.deactivateTurn(),this.clearConfiguredManagedRoomContext(),this.emitEvent({state:"final",usage:this.pendingUsage,agentSessionId:this.agentSessionId??void 0}),this.killProcess().catch(()=>{}))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="error",this.managedTurnReplay=null,this.managedRoomTools.deactivateTurn(),this.clearConfiguredManagedRoomContext(),this.emitEvent({state:"error",message:X(e)}),this.scheduleProcessClose())}resetRunState(){this.pendingUsage=void 0,this.terminalState="open",this.deltaItemIds.clear(),this.lastTransientStatus="",this.clearForceCloseTimer(),this.resetTextState()}clearConfiguredManagedRoomContext(){this.configuredManagedRoomContextToken=null,this.configuredManagedRoomExecutor=null}emitTransientStatus(e){const t=K(e);!t||t===this.lastTransientStatus||this.terminalState!=="open"||(this.lastTransientStatus=t,this.emitEvent({state:"delta",text:`${t}
|
|
10
10
|
`,thinking:!0}))}resetTextState(){this.hasEmittedText=!1,this.textTail=""}emitText(e,t="delta"){if(!e)return;let r=ne(e);r&&(t==="block"&&this.hasEmittedText&&!r.startsWith(`
|
|
11
11
|
`)&&(this.textTail.endsWith(`
|
|
12
12
|
`)?r=`
|
|
@@ -14,4 +14,4 @@ ${r}`:this.textTail.endsWith(`
|
|
|
14
14
|
|
|
15
15
|
`)||(r=`
|
|
16
16
|
|
|
17
|
-
${r}`)),this.hasEmittedText=!0,this.textTail=(this.textTail+r).slice(-4),this.emitEvent({state:"delta",text:r}))}scheduleProcessClose(){const e=this.process;!e||this.forceCloseTimer||(this.forceCloseTimer=setTimeout(()=>{this.forceCloseTimer=null,this.process===e&&(e.kill("SIGTERM"),setTimeout(()=>{this.process===e&&e.kill("SIGKILL")},1e3))},250))}clearForceCloseTimer(){this.forceCloseTimer&&(clearTimeout(this.forceCloseTimer),this.forceCloseTimer=null)}}N("codex",()=>new ge);export{ge as CodexAdapter,me as buildCodexQueueArgs,pe as buildCodexQueuedText,P as isCodexActiveWriterError,oe as isCodexExcludeTurnsUnsupportedError,ie as isCodexPaginatedHistoryUnsupportedError,ae as isCodexProjectApiUnsupportedError,
|
|
17
|
+
${r}`)),this.hasEmittedText=!0,this.textTail=(this.textTail+r).slice(-4),this.emitEvent({state:"delta",text:r}))}scheduleProcessClose(){const e=this.process;!e||this.forceCloseTimer||(this.forceCloseTimer=setTimeout(()=>{this.forceCloseTimer=null,this.process===e&&(e.kill("SIGTERM"),setTimeout(()=>{this.process===e&&e.kill("SIGKILL")},1e3))},250))}clearForceCloseTimer(){this.forceCloseTimer&&(clearTimeout(this.forceCloseTimer),this.forceCloseTimer=null)}}N("codex",()=>new ge);export{ge as CodexAdapter,me as buildCodexQueueArgs,pe as buildCodexQueuedText,P as isCodexActiveWriterError,oe as isCodexExcludeTurnsUnsupportedError,ie as isCodexPaginatedHistoryUnsupportedError,ae as isCodexProjectApiUnsupportedError,Oe as isCodexUnsupportedEffortError,qe as isMissingCodexRolloutError,Ne as normalizeCodexModelId,De as normalizeCodexReasoningEffort,de as selectCodexProjectIdForWorkDir};
|
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{Command as se}from"commander";import y from"chalk";import ae from"node:os";import $ from"node:fs";import{loadConfig as
|
|
2
|
-
`),process.exitCode=1}function go(o){const r=[`trigger=${o.trigger??"unknown"}`,`phase=${o.phase}`,`code=${o.code??"unknown"}`,`attempt=${o.reconnectAttempt}`];return o.reason&&r.push(`reason=${o.reason}`),o.error&&r.push(`error=${o.error}`),r.join(" ")}async function ne(o,r=5e3){const a=Date.now();for(;Date.now()-a<r;){try{process.kill(o,0)}catch{return!0}await new Promise(m=>setTimeout(m,100))}try{return process.kill(o,0),!1}catch{return!0}}const s=new se;s.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(ee),s.enablePositionalOptions(),be(s),Ae(s),s.option("--api <url>","Server URL override").option("--name <name>","Machine name",ae.hostname()).action(async o=>{const r=
|
|
3
|
-
Disconnecting...`)),R({level:"info",wsEvent:"daemon.stop"}),clearInterval(a);try{await
|
|
1
|
+
import{Command as se}from"commander";import y from"chalk";import ae from"node:os";import $ from"node:fs";import{loadConfig as p,saveConfig as ce,getConfigPath as me,getShennianDir as de,resolveShennianPath as J,normalizeAutoUpgradePolicy as le}from"./config/index.js";import{CliRelayClient as pe}from"./relay/client.js";import{collectKnownLogSecrets as K,installCliLogRedaction as ge,sanitizeCliLogValue as ue}from"./log-redaction.js";import{registerPairCommand as he,runSmartStart as fe}from"./commands/pair.js";import{registerLogoutCommand as Se}from"./commands/logout.js";import{clearDaemonPidIfOwner as j,createDaemonInstanceId as ve,clearDaemonLauncher as W,findRunningDaemonProcessIds as Ie,isRemoteAccessDisabled as Y,prepareRemoteAccessPause as we,stopPreparedRemoteAccessService as ye,isIsolatedDaemonE2eInstance as Re,registerDaemonCommand as Ce,writeDaemonPid as q,writeDaemonLauncher as Q}from"./commands/daemon.js";import{registerRuntimeCommand as ke}from"./commands/runtime.js";import{registerManagerCommand as De}from"./commands/manager.js";import{registerUpgradeCommand as Ee}from"./commands/upgrade.js";import{registerToolsCommand as Te}from"./commands/tools.js";import{registerIntegrationCommand as be}from"./commands/integration.js";import{registerRoomCommand as Ae}from"./commands/room.js";import{SessionManager as Pe}from"./session/manager.js";import{SERVERS as _,regionToUrl as Me,urlToRegion as Oe}from"./region.js";import{getCurrentVersion as X,handleStartupCrashCheck as Ue}from"./upgrade/engine.js";import{AutoUpgradeScheduler as Ne}from"./upgrade/scheduler.js";import{detectAgents as $e}from"./agents/detect.js";import{augmentProcessPath as Z}from"./env-path.js";Z();const ee=X();import{getCachedAgentInfos as _e,resolveAgentInfos as xe}from"./agents/model-registry.js";import{initCliLogReporter as Le,reportLog as R}from"./log-reporter.js";import{isNativeFusionEnabled as Be}from"./native-fusion/config.js";import{NativeSessionFusionService as Ve}from"./native-fusion/service.js";import{startDaemonLogRetention as Ge}from"./daemon-log.js";import{startDaemonIpcServer as He}from"./daemon-ipc/server.js";import{resolveManagedRoomIpcPaths as Fe}from"./daemon-ipc/paths.js";import{RoomSessionRegistry as ze}from"./room/session-registry.js";import{HttpRoomBindingClient as Je}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as Ke}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as je}from"./room/host-session-observations.js";import{RoomMirrorSyncService as We}from"./room/mirror-sync-service.js";import{RoomActivationService as Ye}from"./room/activation-service.js";import{RoomActivationAudit as qe}from"./room/activation-audit.js";import{RoomAttachmentCache as Qe}from"./room/attachment-cache.js";import{RoomAttachmentUploader as Xe}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Ze}from"./room/managed-context.js";import{createManagedRoomExecutor as eo}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as oo,SESSION_SYNC_ADVERTISED_CAPABILITIES as no}from"@shennian/wire";import{getDaemonInstallationId as to}from"./session/store.js";import{synchronizeMachinePersonalAuthority as ro}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as io,ensurePersonalDataEpoch as so}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as ao,DeletionSyncTerminalError as co}from"./session/deletion-sync-state.js";const oe=de(),mo=J("daemon.pid");function lo(){try{const o=$.readFileSync(mo,"utf-8").trim();if(o.startsWith("{")){const a=JSON.parse(o),m=Number(a.pid);return Number.isInteger(m)&&m>0?m:null}const r=parseInt(o,10);return Number.isInteger(r)&&r>0?r:null}catch{return null}}function po(o){return o.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function x(){process.stdout.write(`${JSON.stringify({ok:!1,error:"integration_command_retired",message:"Room MCP and Hooks were retired. Use the Shennian Skill and CLI.",nextAction:{type:"open_install_guide",url:"https://shennian.net/install.md"}})}
|
|
2
|
+
`),process.exitCode=1}function go(o){const r=[`trigger=${o.trigger??"unknown"}`,`phase=${o.phase}`,`code=${o.code??"unknown"}`,`attempt=${o.reconnectAttempt}`];return o.reason&&r.push(`reason=${o.reason}`),o.error&&r.push(`error=${o.error}`),r.join(" ")}async function ne(o,r=5e3){const a=Date.now();for(;Date.now()-a<r;){try{process.kill(o,0)}catch{return!0}await new Promise(m=>setTimeout(m,100))}try{return process.kill(o,0),!1}catch{return!0}}const s=new se;s.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(ee),s.enablePositionalOptions(),be(s),Ae(s),s.option("--api <url>","Server URL override").option("--name <name>","Machine name",ae.hostname()).action(async o=>{const r=p(),a=o.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??r.serverUrl??void 0;await fe(a,o.name)}),s.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async o=>{const r=J("env.json");try{const e=JSON.parse($.readFileSync(r,"utf-8"));for(const[i,n]of Object.entries(e))process.env[i]||(process.env[i]=n);Z()}catch{}ge(K(process.env,p()));const a=Ge();Y()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(a),process.exit(0));const m=!!(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM||process.env.SHENNIAN_DESKTOP_SERVER_URL),L=async(e,i)=>{console.log(`[${new Date().toISOString()}] ${i} (PID ${e})`),process.kill(e,"SIGTERM"),await ne(e)||(process.kill(e,"SIGKILL"),await ne(e,2e3))};try{const e=lo();if(e&&e!==process.pid)try{process.kill(e,0),m?await L(e,"managed start taking over from existing daemon"):(console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),process.exit(0))}catch{}}catch{}const M=Re(process.env,oe)?[]:Ie(process.pid);if(M.length>0)if(m)for(const e of M)try{await L(e,"managed start taking over from orphan daemon")}catch{}else console.log(`[${new Date().toISOString()}] daemon already running (PID ${M[0]}), skipping duplicate start`),process.exit(0);const g=ve();q(process.pid,g,{version:ee}),Q(process.pid,void 0,g),process.on("exit",()=>{j(process.pid,g),W(g)}),await Ue()&&(console.log(`[${new Date().toISOString()}] Rolled back to previous version, restarting...`),process.exit(0));try{so()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const c=p(),h=o.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??c.serverUrl??_.cn.url,u=X(),O=$e(),v=O.map(e=>e.type),U=_e(O),f=new ze,C=new je,I=new Ze(f),S=new Je(h),re=new Qe({actionClient:S}),ie=new Xe({actionClient:S});let k=null,d;const B=eo({authority:I,actionClient:S,attachmentCache:re,attachmentUploader:ie,observations:C,getMachineCredentials:()=>({machineToken:p().machineToken}),onPaused:({registryKey:e,sessionId:i})=>{const t=f.getByRegistryKey(e)?.binding;t&&f.setBinding({registryKey:e,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:"paused",activationMode:"paused"}),I.revokeSession(e),d?.discardPendingManagedRoomMessages(i)}}),D=await He({paths:Fe(),handlers:{"managed-room.execute":e=>B(e)}});let V=!1;const E=()=>{V||(V=!0,D.close().finally(()=>process.exit(0)))};if(process.once("SIGINT",E),process.once("SIGTERM",E),!c.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await D.close(),process.exitCode=1;return}process.removeListener("SIGINT",E),process.removeListener("SIGTERM",E);const G=`${po(h)}/relay/machine`,N=c.machineToken;if(!N)return;try{await ro({serverUrl:h,machineToken:N,machineId:c.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await D.close(),process.exitCode=1;return}c.machineId&&Le(h,c.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${G}... (v${u}) agents: ${v.join(",")}`),R({level:"info",wsEvent:"daemon.start",metadata:{version:u,agents:v}});let T=null,b=null,A=0;const H=le(c.autoUpgrade),l=new pe({serverUrl:G,machineToken:N,cliVersion:u,agentList:v,autoUpgradePolicy:H,sessionSyncProtocolVersion:oo,sessionSyncCapabilities:no,daemonInstallationId:to(),dataEpoch:io,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),R({level:"info",wsEvent:"daemon.connected"}),U.some(n=>n.models.length>0)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:v,agents:U}}),xe(O,{serverUrl:c.serverUrl??h,authToken:c.machineToken??c.accessToken}).then(n=>{JSON.stringify(n)!==JSON.stringify(U)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:v,agents:n}})}).catch(()=>{}),import("./upgrade/engine.js").then(({readUpgradeAttempt:n,clearUpgradeAttempt:t})=>{const w=n();t(),w&&(console.log(`[${new Date().toISOString()}] [upgrade] Reporting success: ${w.from} \u2192 ${w.to}`),l.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:w.from,to:w.to}}))}).catch(()=>{}),P.start(),b?.start();const e=++A,i=async()=>{try{await d.synchronizeSessionDeletions(),e===A&&l.getState()==="connected"&&T?.handleConnected()}catch(n){if(console.error("[session.deletions] startup catch-up failed",n),e===A&&l.getState()==="connected"){if(n instanceof co)return;const t=n instanceof ao?Math.max(1e3,n.retryAfterMs):5e3;setTimeout(()=>{i()},t).unref?.()}}};i()},onDisconnected:e=>{A+=1,b?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${go(e)}`),R({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),R({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),d.handleReq(e)}});T=Be()?new Ve(l):null,b=new We({registry:f,observations:C,actionClient:S,getMachineCredentials:()=>({machineToken:p().machineToken}),onMessages:async(e,i,n,t)=>{await k?.processMessages(e,i,n,t)},onBindingPaused:e=>{I.revokeSession(e.registryKey),d?.discardPendingManagedRoomMessages(e.nianSessionId)}});const P=new Ne({currentVersion:u,machineId:c.machineId??"unknown-machine",serverUrl:c.serverUrl??h,getIdleState:()=>d.getUpgradeIdleState(),startUpgrade:async e=>{const{handleUpgradeStart:i}=await import("./commands/upgrade.js");await i(l,"auto-upgrade",e,{currentVersion:u})}});P.setPolicy(H),d=new Pe(l,T,u,P,new Ke({registry:f,observations:C,bindingClient:S,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${g}`}),{prepare:()=>we(),isDisabled:()=>Y(),shutdownAfterResponse:()=>{setTimeout(()=>{ye(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>I.revokeToken(e),B),k=new Ye({registry:f,observations:C,enqueueConversationBatch:e=>d.enqueueManagedRoomBatch(e),loadContext:async e=>{const i=p().machineToken?.trim(),n=e.binding;if(!i||!n)throw new Error("managed_room_context_unavailable");const t=await S.status({machineToken:i,roomId:n.roomId,bindingId:n.bindingId});return{roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent}:{},agentSystemPromptsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,i)=>{const n=p().machineToken;if(!n)throw new Error("machine_not_authorized");return I.issue({registryKey:e,machineCredential:n,activationCauseMessageId:i})},audit:new qe}),d.setManagedRoomTurnPreparer(e=>k.prepareConversationTurn(e)),d.setManagedAgentDirectTurnPreparer(e=>k.prepareDirectConversationTurn(e)),$.mkdirSync(oe,{recursive:!0}),q(process.pid,g,{version:u}),Q(process.pid,void 0,g),l.connect(),process.stdin.resume();let F=!1;const z=async()=>{if(!F){F=!0,console.log(y.gray(`
|
|
3
|
+
Disconnecting...`)),R({level:"info",wsEvent:"daemon.stop"}),clearInterval(a);try{await d.cleanup(),P.stop(),T?.stop(),b?.stop(),l.disconnect(),await D.close(),j(process.pid,g),W(g)}finally{process.exit(0)}}};process.on("SIGINT",()=>{z()}),process.on("SIGTERM",()=>{z()})}),s.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>x()),s.command("room-session-hook",{hidden:!0}).description("(internal) Authorize the official Room tool for one trusted Agent Session").argument("<runtime>","Trusted runtime adapter").allowUnknownOption(!0).action(()=>x()),s.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>x());const te=s.command("config").description("View or modify configuration");te.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${me()}`);const o=p(),r=ue(o,K(process.env,o));if(o.serverUrl){const a=Oe(o.serverUrl);console.log(`Region: ${a} (${_[a].label})`)}console.log(JSON.stringify(r,null,2))}),te.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((o,r)=>{const a=p();if(o==="server"){r!=="cn"&&r!=="global"&&(console.error(y.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const m=r;a.serverUrl=Me(m),ce(a),console.log(y.green(`\u2713 Server set to ${_[m].label}`)),console.log(y.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(y.red(`\u2717 Unknown config key: ${o}. Supported: server`)),process.exit(1)}),he(s),Se(s),Ce(s),ke(s),De(s),Ee(s),Te(s),s.parse();
|
|
@@ -49,6 +49,8 @@ export declare class RoomActivationService {
|
|
|
49
49
|
});
|
|
50
50
|
processMessages(roomId: string, messages: RoomMessageView[], registrations: RoomSessionRegistration[], context?: RoomActivationContext): Promise<void>;
|
|
51
51
|
prepareConversationTurn(contexts: ManagedRoomQueueContext[]): Promise<PreparedManagedRoomTurn>;
|
|
52
|
+
prepareDirectConversationTurn(sessionId: string): Promise<PreparedManagedRoomTurn | null>;
|
|
53
|
+
private prepareManagedTurn;
|
|
52
54
|
private audit;
|
|
53
55
|
}
|
|
54
56
|
export declare function resolveManagedAgentPolicy(input: {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{isAvailableBuiltinAgentType as A}from"@shennian/wire";import{getBuiltinAgentVersion as k,resolveBuiltinCommand as
|
|
2
|
-
${
|
|
3
|
-
`)}`:"";return{speaker:
|
|
1
|
+
import{isAvailableBuiltinAgentType as A}from"@shennian/wire";import{getBuiltinAgentVersion as k,resolveBuiltinCommand as S}from"../agents/command-spec.js";import{registerCustomRuntimeSecurity as P,resolveCustomRuntimeExecutableFingerprint as K}from"../agents/custom-managed-runtime.js";import{loadConfig as B}from"../config/index.js";import{resolveManagedAgentExecutableIdentity as $}from"../security/managed-agent-executable.js";import{compileManagedAgentPolicy as T}from"../security/managed-agent-policy.js";import{resolveManagedAgentRuntimeProtection as x}from"../security/managed-agent-runtime.js";import{selectRoomActivationTargets as q}from"./activation-policy.js";import{composeManagedRoomPrompt as D}from"./managed-prompt-composer.js";const W=4,E=6e4,R=6,N=5*6e4;class oe{deps;latestContexts=new Map;constructor(e){this.deps=e}async processMessages(e,i,o,r={roomName:"\u5F53\u524D\u7FA4\u804A"}){const d=[],c=[],u=new Map,m=new Set,g=[...i].sort((n,a)=>G(n.sequence,a.sequence));for(const n of o){const a=this.deps.registry.getByRegistryKey(n.registryKey);!a?.binding||a.binding.roomId!==e||a.binding.status!=="active"&&a.binding.status!=="paused"&&a.binding.activationQueue.length}for(const n of o){const a=this.deps.registry.getByRegistryKey(n.registryKey);if(!a?.binding||a.binding.roomId!==e||a.binding.activationQueue.length===0&&!a.binding.activeActivation||(m.add(a.registryKey),a.binding.status!=="active"||a.binding.activationMode==="paused")||a.binding.activationQueue.length===0)continue;const p=this.deps.observations.getBySource(a);if(!p?.workspacePath||p.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");this.latestContexts.set(a.registryKey,r);for(const s of a.binding.activationQueue)d.push({registration:a,input:{params:{sessionId:a.nianSessionId,text:s.deliveryText??"\u7FA4\u804A\u4E2D\u6709\u4E00\u6761\u5F85\u5904\u7406\u6D88\u606F\uFF0C\u8BF7\u7ED3\u5408\u5F53\u524D\u7FA4\u804A\u4E0A\u4E0B\u6587\u5904\u7406\u3002",agentType:y(a.runtimeAdapter),sessionMode:r.agentsByIdentityId?.[a.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:r.agentsByIdentityId?.[a.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:r.agentsByIdentityId?.[a.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:r.agentsByIdentityId?.[a.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:p.workspacePath,agentSessionId:a.sourceSessionKey,queueMessageId:`room-activation-${a.binding.bindingId}-${s.causeMessageId}`,clientMessageId:`room-activation-${a.binding.bindingId}-${s.causeMessageId}`},context:{registryKey:a.registryKey,causeMessageId:s.causeMessageId,rootCauseMessageId:s.rootCauseMessageId,sequence:s.sequence,hopCount:s.hopCount}}})}for(const n of g){const a=new Set(q(n,o).map(p=>p.registration.registryKey));for(const p of o){const s=this.deps.registry.getByRegistryKey(p.registryKey);if(!s?.binding||s.binding.roomId!==e||s.binding.status!=="active"&&s.binding.status!=="paused"||BigInt(n.sequence)<=BigInt(s.binding.lastActivatedSequence))continue;const f=u.get(s.registryKey)??[...s.binding.mentionContext??[]];if(u.set(s.registryKey,f),a.has(s.registryKey)){const l=(n.activationCause?.hopCount??0)+(n.author.principalType==="agent"?1:0),I=n.activationCause?.rootCauseMessageId??n.id;if(l>W)this.audit(s,n.id,I,l,"suppressed","max_hops");else{const h=this.deps.registry.claimActivation({registryKey:s.registryKey,windowMs:E,maxActivations:R,pauseMs:N,now:this.deps.now?.()});if(!h.allowed){h.newlyPaused&&this.audit(s,n.id,I,l,"suppressed","rate_limited"),c.push({registryKey:s.registryKey,sequence:n.sequence});continue}const b=this.deps.observations.getBySource(s);if(!b?.workspacePath||b.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const v=M([n]),C=s.binding.activationMode==="mention_only"?L([...f.map(_=>_.deliveryText),v]):v;this.latestContexts.set(s.registryKey,r),d.push({registration:s,input:{params:{sessionId:s.nianSessionId,text:C,agentType:y(s.runtimeAdapter),sessionMode:r.agentsByIdentityId?.[s.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:r.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:r.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:r.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:b.workspacePath,agentSessionId:s.sourceSessionKey,queueMessageId:`room-activation-${s.binding.bindingId}-${n.id}`,clientMessageId:`room-activation-${s.binding.bindingId}-${n.id}`},context:{registryKey:s.registryKey,causeMessageId:n.id,rootCauseMessageId:I,sequence:n.sequence,hopCount:l}}}),s.binding.activationMode==="mention_only"&&u.set(s.registryKey,[])}}else s.binding.activationMode==="mention_only"&&n.kind!=="system"&&n.author.membershipId!==s.binding.agentMembershipId&&(f.some(l=>l.sequence===n.sequence)||f.push({sequence:n.sequence,deliveryText:M([n])}));c.push({registryKey:s.registryKey,sequence:n.sequence})}}await this.deps.enqueueConversationBatch(d.map(n=>n.input));for(const n of m)this.deps.registry.retireLegacyActivationState(n,this.deps.now?.()??new Date);for(const[n,a]of u)this.deps.registry.replaceMentionContext(n,a,this.deps.now?.()??new Date);for(const n of d)this.audit(n.registration,n.input.context.causeMessageId,n.input.context.rootCauseMessageId,n.input.context.hopCount,"queued","conversation_queue");for(const n of c)this.deps.registry.advanceCursor({registryKey:n.registryKey,cursor:"activated",sequence:n.sequence})}async prepareConversationTurn(e){const i=e.at(-1);if(!i)throw new Error("managed_room_queue_context_missing");const o=this.deps.registry.getByRegistryKey(i.registryKey);if(!o)throw new Error("managed_room_binding_unavailable");const r=await this.prepareManagedTurn(o,i.causeMessageId);return this.audit(o,i.causeMessageId,i.rootCauseMessageId,i.hopCount,"activated","conversation_queue_dispatch"),r}async prepareDirectConversationTurn(e){const i=this.deps.registry.getByNianSessionId(e);return i?this.prepareManagedTurn(i):null}async prepareManagedTurn(e,i){if(!e.binding||e.binding.status!=="active")throw new Error("managed_room_binding_unavailable");const o=this.deps.loadContext?await this.deps.loadContext(e):this.latestContexts.get(e.registryKey);if(!o)throw new Error("managed_room_prompt_context_unavailable");const r=o.agentsByIdentityId?.[e.binding.agentIdentityId];if(!r?.permissionPolicy)throw new Error("managed_agent_policy_missing");const d=r.sessionMode==="manager"?"manager":"standard",c=d==="manager"?{...r.managerConfig,defaultWorkerAgentType:r.managerConfig?.defaultWorkerAgentType??y(e.runtimeAdapter),defaultWorkerModelId:r.managerConfig?.defaultWorkerModelId??null}:null,u=this.deps.observations.getBySource(e);if(!u?.workspacePath||u.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const m=this.deps.resolveManagedAgentPolicy?await this.deps.resolveManagedAgentPolicy({registration:e,permissionPolicy:r.permissionPolicy,permissionConfirmation:r.permissionConfirmation,workDir:u.workspacePath}):await j({registration:e,permissionPolicy:r.permissionPolicy,permissionConfirmation:r.permissionConfirmation,workDir:u.workspacePath}),g=Q(o,e,m),n=this.deps.issueManagedRoomContext(e.registryKey,i);if(!/^mrc_[A-Za-z0-9_-]{43}$/.test(n))throw new Error("managed_room_context_unavailable");return{agentType:y(e.runtimeAdapter),workDir:m.adapterPolicy.workDir,agentSessionId:e.sourceSessionKey,systemPrompt:g.systemPrompt,sessionMode:d,managerConfig:c,managerDefaultWorkerAgentType:c?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:c?.defaultWorkerModelId??null,roomContextRegistryKey:e.registryKey,managedRoomContextToken:n,managedAgentPolicy:m,managedRoomPromptSnapshot:g.snapshot}}audit(e,i,o,r,d,c){e.binding&&this.deps.audit?.record({roomId:e.binding.roomId,bindingId:e.binding.bindingId,causeMessageId:i,rootCauseMessageId:o,hopCount:r,outcome:d,reason:c,at:this.deps.now?.()})}}function M(t){const e=t.map(i=>{const o=i.text?.trim()||"\u53D1\u9001\u4E86\u4E00\u6761\u6D88\u606F",r=i.attachments.length>0?`
|
|
2
|
+
${i.attachments.map(F).join(`
|
|
3
|
+
`)}`:"";return{speaker:i.author.displayName,body:`${o}${r}`}});return w(e)}function F(t){return`\u9644\u4EF6\uFF1A${`${O(t.name)} \xB7 ${V(t.byteSize)}`}\uFF5C${t.mimeType}\uFF5CattachmentId=${t.attachmentId}`}function O(t){return String(t||"\u9644\u4EF6").replace(/[[\]\\]/g,"\\$&").replace(/[\r\n]+/g," ")}function V(t){const e=Number(t);if(!Number.isFinite(e)||e<0)return`${t} B`;if(e<1024)return`${e} B`;const i=["KB","MB","GB","TB"];let o=e,r=-1;do o/=1024,r+=1;while(o>=1024&&r<i.length-1);const d=o>=100?0:o>=10?1:2;return`${o.toFixed(d).replace(/\.0+$|(?<=\.[0-9])0+$/,"")} ${i[r]}`}function L(t){return w(t.map(z))}function z(t){const e=t.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);if(e)return{speaker:e[1],body:e[2]};const i=t.match(/^\[群消息\]\n<([^>\n]+)>\n([\s\S]*)$/);if(i)return{speaker:i[1],body:i[2]};const o=t.match(/^群消息\n([^\n]+)\n([\s\S]*)$/);if(o)return{speaker:o[1],body:o[2]};const r=t.match(/^([^\n]+)\n([\s\S]*)$/);return r?{speaker:r[1],body:r[2]}:{speaker:"\u7FA4\u6210\u5458",body:t.trim()}}function w(t){if(t.length===1){const[e]=t;return`[\u7FA4\u6D88\u606F] <${e.speaker}>
|
|
4
4
|
${e.body}`}return`[\u7FA4\u6D88\u606F]
|
|
5
|
-
${
|
|
5
|
+
${t.map(e=>`<${e.speaker}>
|
|
6
6
|
${e.body}`).join(`
|
|
7
7
|
|
|
8
|
-
`)}`}function Q(
|
|
8
|
+
`)}`}function Q(t,e,i){const o=e.binding?t.agentsByIdentityId?.[e.binding.agentIdentityId]:void 0;if(!e.binding||!o)throw new Error("managed_room_prompt_incomplete");return D({runtimeAdapter:y(e.runtimeAdapter),agent:{displayName:o.displayName,description:o.description,systemPrompt:t.agentSystemPromptsByIdentityId?.[e.binding.agentIdentityId]},room:{name:t.roomName,activationMode:e.binding.activationMode,members:t.members},runtimePermissions:{requestedPolicy:i.requestedPolicy,enforcement:i.enforcement,resolvedWorkDir:i.adapterPolicy.workDir}})}async function j(t){const e=y(t.registration.runtimeAdapter),i=A(e)?S(e):null,o=e.startsWith("custom:")?B().customAgents?.[e.slice(7)]:void 0;o&&P(e,o);const r=i?k(e,i):o?.caps.version,d=i?$(i):{platform:process.platform,isWsl:process.platform==="linux"&&!!(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP),executableFingerprint:o?K(o):void 0},c={agentType:e,runtimeVersion:r,platform:d.platform,isWsl:d.isWsl,executableFingerprint:d.executableFingerprint,requireExecutableFingerprint:!0},u=x(c),m=X({confirmation:t.permissionConfirmation,policy:t.permissionPolicy,runtimeAdapter:e,protection:u}),g=await T({...c,policy:t.permissionPolicy,workDir:t.workDir,hostFullAccessConfirmed:m&&t.permissionConfirmation?.hostFullAccess,limitedProtectionConfirmed:m&&t.permissionConfirmation?.limitedProtection});if(!g.ok){const n=g.unsupportedCapabilities?.length?`:${g.unsupportedCapabilities.join(",")}`:"";throw new Error(`${g.errorCode}${n}`)}return g.value}function X(t){const{confirmation:e,policy:i,runtimeAdapter:o,protection:r}=t;return!!(e&&e.policyVersion===i.version&&e.runtimeAdapter===o&&e.runtimeVersion===r.runtimeVersion&&e.platform===r.platform&&e.executableFingerprint===r.executableFingerprint&&e.hostFullAccess===(i.fileAccess==="host-full")&&e.limitedProtection===(r.level!=="hard"))}function y(t){return t==="claude_code"?"claude":t}function G(t,e){const i=BigInt(t),o=BigInt(e);return i<o?-1:i>o?1:0}export{oe as RoomActivationService,X as managedAgentPermissionConfirmationMatches,j as resolveManagedAgentPolicy};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
`),a=t>0?Math.min(t,80):Math.min(e.length,80);return e.slice(0,a)}function ue(e,t,a){return`${e}:${K("sha256").update(`${t}\0${a??""}`).digest("hex")}`}function He(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 ze=3e4;function Be(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 ge(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function fe(e,t){return e!=="manager"?e:t==="claude"?"claude":"codex"}function Le(e,t){H({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 me(e){return be(e)}function pe(e,t,a){return{}}function X(e,t,a,s,r,l,c,R){const S=a==="codex";e.configure?.({sessionId:t,externalChannel:s??null,env:{...me(a),...pe(t,s,r),...l&&!S?{SHENNIAN_MANAGED_ROOM_CONTEXT:l,[Oe]:We()}:{}},...c?{managedAgentPolicy:c}:{},...S?{managedRoomContextToken:l??null,managedRoomExecutor:l?R??null:null}:{}})}function he(e,t,a){const s=t.managedRoomContextToken;s&&s!==a&&e.revokeManagedRoomContextToken?.(s),t.managedRoomContextToken=a}function v(e,t){const a=t?.managedRoomContextToken;!t||!a||(t.managedRoomContextToken=null,e.revokeManagedRoomContextToken?.(a))}function Ue(e,t,a){if(e!=="claude"||!a)return t;const s=t.trim();return!!s&&s.startsWith("-")&&!s.includes("/")?Te(a)??t:t}function Je(e,t,a,s){let r=null;function l(n,o={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:He(n,t,o),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function c(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function R(n){if(!n.currentRunId||!n.currentRunPhase)return;const o=n.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:t,runId:n.currentRunId,seq:o,runPhase:n.currentRunPhase},seq:o,id:`agent-heartbeat-${n.currentRunId}-${o}-${Date.now()}`})}function S(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{R(n)},ze),n.heartbeatTimer.unref?.())}function h(n){const o=n?.pendingTextEvent;!n||!o||!o.text||(b(t,{id:`agent-${o.runId}-${o.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:o.text}),x(e.client),l({state:"delta",runId:o.runId,seq:o.seq,text:o.text,thinking:o.thinking||void 0}),n.pendingTextEvent=null)}s.on("agentEvent",n=>{const o=e.sessions.get(t),q=Be(n),T=n.state==="final"||n.state==="error"||n.state==="aborted";if(o&&(o.nextEventSeq=n.seq+1,T||(o.currentRunId=n.runId),!T&&q&&(o.currentRunPhase=q,S(o)),n.agentSessionId&&(o.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}`;ve({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:Me(n.approval)}),k=!0,o&&H({sessionId:t,agentType:a,workDir:o.workDir,agentSessionId:o.agentSessionId,attentionId:ue("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 A=`${t}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&e.runTextAcc.set(A,(e.runTextAcc.get(A)??"")+n.text),n.agentSessionId&&n.agentSessionId!==r&&(r=n.agentSessionId,e.nativeFusion?.noteManagedSourceSession(t,fe(a,n.source),n.agentSessionId),o&&H({sessionId:t,agentType:a,workDir:o.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;o?.pendingTextEvent&&o.pendingTextEvent.runId!==n.runId&&h(o),o&&!o.pendingTextEvent&&(o.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:!1}),o?.pendingTextEvent&&(o.pendingTextEvent.text+=m,o.pendingTextEvent.seq=n.seq);return}let C={};if(n.state==="final"){h(o);const m=e.runTextAcc.get(A)??"";m&&(C={messageSummary:Fe(m)}),e.runTextAcc.delete(A)}else n.state==="error"||n.state==="aborted"?(h(o),e.runTextAcc.delete(A)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&h(o);T&&o?.currentRunId===n.runId&&(v(e,o),o.currentRunId=null,o.currentRunPhase=null,o.nextEventSeq=0,c(o),e.chatQueue?.noteTerminal(t),H({sessionId:t,agentType:a,workDir:o.workDir,agentSessionId:o.agentSessionId,status:n.state==="final"?"completed":"failed",attentionId:ue("terminal",n.runId),lastActivityAt:new Date().toISOString()}),x(e.client)),l(n,C)}),s.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${t} agentType=${a}: ${n.message}`),v(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 Qe(e,t){if(e.processedReqIds.add(t),e.processedReqIds.size>1e3){const a=e.processedReqIds.values().next().value;e.processedReqIds.delete(a)}}async function B(e,t){v(e,t),t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})}function ye(e,t,a,s,r,l,c){return c?e.send(t,a,s,r,l,c):l?.trim()?e.send(t,a,s,r,l):r?.length?e.send(t,a,s,r):s?e.send(t,a,s):e.send(t,a)}function Ve(e,t,a,s,r,l,c){return e.prepareSend?e.prepareSend(t,a,s,r,l,c):Promise.resolve()}async function je(e,t,a,s,r,l,c,R,S){e.evictIdleSessions();const h=Ee(a);if(!h)throw new Error(`Unsupported agent: ${a}`);const n=s==="manager"&&a!=="manager"?new xe(h,a):h;X(n,t,a,c,R,null,S,e.managedRoomExecutor),await n.start(t,r,l);const o={adapter:n,workDir:r,agentType:a,sessionMode:s,agentSessionId:l??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:c??null,externalReplyTarget:R??null,externalChannelEnv:{...me(a),...pe(t,c,R)},managedRoomContextToken:null};return e.sessions.set(t,o),Je(e,t,a,n),o}function Ge(e,t){const a=e.sessions.get(t),s=a?.currentRunId;if(!a||!s)return;const r=a.nextEventSeq;e.runTextAcc.delete(`${t}:${s}`),v(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:r},seq:r,id:`agent-evt-${s}-${r}`}),e.chatQueue?.noteTerminal(t)}async function pt(e,t,a){const s=a?t.params.managedRoomContextToken??null:null;let r=!1;try{await Ke(e,t,a,()=>{r=!0})}finally{s&&!r&&e.revokeManagedRoomContextToken?.(s)}}async function Ke(e,t,a,s){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}const{sessionId:r,text:l,agentType:c,sessionMode:R,managerConfig:S,workDir:h,agentSessionId:n,modelId:o,systemPrompt:q,managerDefaultWorkerAgentType:T,managerDefaultWorkerModelId:k,reasoningEffort:A,clientMessageId:C,sessionListProjection:m,waitForDispatch:Y,responseId:Ie,managedRoomContextToken:P,suppressExternalOwnerEcho:Re,queuedBatchItems:L,managedWorkerParentSessionId:W}=t.params,y=Ie||t.id;De(m);const O=null,U="";if(a?.source==="room_activation"&&(Ne(a,q,P),c==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(Qe(e,t.id),!r||!l){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:"sessionId and text are required"});return}const g=c,$=g==="manager"||R==="manager"?"manager":"standard";$==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(r,T??null,k??null);const N=(g==="claude"||g==="codex"||g==="workbuddy"||g==="pi-coding-agent")&&typeof A=="string"&&A.trim()?A.trim():void 0,w=e.resolvePath(Ue(g,h||de.homedir(),n)||de.homedir()),_=a?.managedAgentPolicy??(W?e.managerRuntime?.getManagedAgentPolicyForWorker(W,w)??null:null);if(W&&!_)throw new Error("managed_worker_permission_context_unavailable");const J=W&&_?Ce({requestedPolicy:_.requestedPolicy,enforcement:_.enforcement,resolvedWorkDir:_.adapterPolicy.workDir}):q,Se=w,F=qe(t.params.attachments);F?.length&&Pe(w,F);const p={text:l,attachments:F,localized:!1};let i=e.sessions.get(r);if(i){if(i.lastActiveAt=Date.now(),i.agentType!==g||(i.sessionMode??(i.agentType==="manager"?"manager":"standard"))!==$||i.workDir!==w||JSON.stringify(i.externalChannel??null)!==JSON.stringify(O??null)){e.sessions.delete(r);try{await B(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(r);try{await B(e,i)}catch{e.processedReqIds.delete(t.id)}i=void 0}}if(!i)try{i=await je(e,r,g,$,w,n,O,U,_)}catch(d){const f=d instanceof Error&&d.message.startsWith("Unsupported agent:")?d.message:`Failed to start ${c}: ${d instanceof Error?d.message:String(d)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${r} agentType=${c} workDir=${w} agentSessionId=${n??""}: ${f}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:f,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:f});return}const I={id:C??`user-${t.id}`,sessionId:r,role:"user",ts:Date.now(),payload:ie(p.text,p.attachments)},Z=Array.isArray(L)&&L.length>0?L.map(d=>({id:d.clientMessageId??d.id,sessionId:r,role:"user",ts:Date.parse(d.createdAt),payload:ie(d.text,d.attachments)})):null,Q=Z??[I],E=_e(r,I.id),ee=E.state==="queued";if(E.state!=="not_found"&&!ee){e.client.sendRes({type:"res",id:y,ok:!0,payload:E});return}if(Y){a?.source==="room_activation"&&X(i.adapter,r,g,O,U,P,a.managedAgentPolicy,e.managedRoomExecutor);try{await Ve(i.adapter,p.text,o,N,p.attachments,J,a?{source:a.source}:void 0)}catch(d){e.processedReqIds.delete(t.id),e.sessions.delete(r),await B(e,i),e.client.sendRes({type:"res",id:y,ok:!1,error:d instanceof Error?d.message:String(d)});return}}D({level:"info",sessionId:r,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:g,sessionMode:$,managerConfig:S??null,modelId:o,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:k??null,reasoningEffort:N,...a?.source==="room_activation"?{managedRoomPromptSnapshot:a.managedRoomPromptSnapshot}:{}}});const V=(d,f)=>({...ce(le()),sessionId:r,clientMessageId:I.id,messageId:I.id,deliveryState:d,bodyRevision:f,committedAt:new Date(I.ts).toISOString()}),te=d=>{e.nativeFusion?.registerManagedSend({sessionId:r,agentType:g,sourceAgentType:fe(g,o),canonicalMessageId:I.id,sourceSessionKey:i.agentSessionId??n??null,text:p.text,managedEchoPolicy:d?.deliveryMode==="external_owner_queue"&&!Re?"import_following":"suppress_following",externalRunId:d?.deliveryMode==="external_owner_queue"?`codex-external-${d.queuedSubmissionId??I.id}`:null})},ne=()=>{i.currentRunId||(i.currentRunId=`dispatch:${t.id}`,i.currentRunPhase="thinking",i.nextEventSeq=0)},ae=()=>{if(te(),ee)return{...ce(le()),sessionId:r,clientMessageId:E.clientMessageId,messageId:E.messageId,deliveryState:E.deliveryState,bodyRevision:E.bodyRevision,committedAt:E.committedAt};try{Le(e,{sessionId:r,agentType:g,sessionMode:$,managerConfig:S??null,workDir:Se,agentSessionId:i.agentSessionId??n??null,modelId:o,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:k??null});let d=0;for(const[f,u]of Q.entries()){const M=Z?z(r,u,"committed"):b(r,u,"committed");f===0&&(d=M)}return $e(w,F??[]),x(e.client),V("committed",d)}catch(d){throw e.nativeFusion?.cancelManagedSend?.(I.id),d}},re=d=>{i.currentRunId||(i.nextEventSeq=0),te(d);const f=d?.deliveryMode==="external_owner_queue"?"queued":"executing";let u=0;for(const[M,j]of Q.entries()){const G=z(r,j,f);M===0&&(u=G)}return x(e.client),V(f,u)},oe=async(d,f)=>{e.nativeFusion?.cancelManagedSend?.(I.id);const u=ge(g,d);console.error(`[chat.send] send failed reqId=${t.id} sessionId=${r} agentType=${c} workDir=${w} agentSessionId=${i.agentSessionId??n??""}: ${u}`),e.sessions.delete(r);try{await B(e,i)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:u,runId:"",seq:0}}),!f){const M={id:`agent-error-${t.id}-${Date.now()}`,sessionId:r,role:"agent",ts:Date.now(),payload:u};b(r,M),x(e.client)}f&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:u}))};if(Y){try{ae()}catch(u){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:`Durable message commit failed: ${u instanceof Error?u.message:String(u)}`});return}ne();let d;try{a?.source==="room_activation"&&(he(e,i,P),s());const u=p.attachments;d=await ye(i.adapter,p.text,o,N,u,J,a?{source:a.source}:void 0),D({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(u){let M=0;for(const[G,ke]of Q.entries()){const we=z(r,ke,"failed");G===0&&(M=we)}x(e.client);const j=ge(g,u);await oe(u,!1),e.client.sendRes({type:"res",id:y,ok:!0,payload:{...V("failed",M),failureMessage:j}});return}const f=re(d??void 0);e.client.sendRes({type:"res",id:y,ok:!0,payload:{...f,...p.localized?{localizedAttachments:!0}:{},...d?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:d.deliveryMode,queuedSubmissionId:d.queuedSubmissionId}:{}}}),D({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}let se;try{se=ae()}catch(d){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:`Durable message commit failed: ${d instanceof Error?d.message:String(d)}`});return}ne(),a?.source==="room_activation"&&(X(i.adapter,r,g,O,U,P,a.managedAgentPolicy,e.managedRoomExecutor),he(e,i,P),s()),re(),e.client.sendRes({type:"res",id:y,ok:!0,payload:{...se,...p.localized?{localizedAttachments:!0}:{}}}),D({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const Ae=p.attachments;ye(i.adapter,p.text,o,N,Ae,J,a?{source:a.source}:void 0).then(()=>{D({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(d=>{z(r,I,"failed"),x(e.client),oe(d,!1)})}async function ht(e,t){const{sessionId:a}=t.params,s=e.sessions.get(a);if(s){v(e,s);try{await s.adapter.stop()}catch{e.client.sendRes({type:"res",id:t.id,ok:!1,error:"agent_stop_failed"});return}Ge(e,a),e.activityPublisher?.publish(a,null)}e.client.sendRes({type:"res",id:t.id,ok:!0})}export{ht as handleChatAbort,pt as handleChatSend,Le as sendSessionUpdateEvent};
|
|
1
|
+
import ie from"node:os";import{createHash as K}from"node:crypto";import{createAgent as Me}from"../../agents/adapter.js";import{buildApprovalPendingPayload as xe,buildUserMessagePayload as le}from"@shennian/wire";import{ManagerModeAdapter as Te}from"../../agents/manager.js";import{reportLog as b}from"../../log-reporter.js";import{lookupClaudeTranscriptCwd as _e}from"../../native-fusion/parsers.js";import{appendMessage as q,getDaemonInstallationId as ce,getMessageDeliveryStatus as De,recordSession as H,upsertMessage as z}from"../store.js";import{mergeProjectedSessions as be}from"../projection.js";import{buildManagedAgentEnv as qe}from"../../agents/config-status.js";import{parsePersonalAttachmentReferences as Pe}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as $e,markAttachmentReferences as ve}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as T}from"../index-publisher.js";import{writeToolDetail as Ce}from"../tool-detail-store.js";import{getPersonalSyncIdentity as ue}from"../../personal-sync/epoch.js";import{buildManagedAgentPermissionPrompt as We}from"../../room/managed-prompt-composer.js";import{getManagedRoomIpcRoot as Oe,MANAGED_ROOM_IPC_ROOT_ENV as Ne}from"../../daemon-ipc/paths.js";function Fe(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=K("sha256").update(JSON.stringify({requestedPolicy:e.managedAgentPolicy.requestedPolicy,enforcement:e.managedAgentPolicy.enforcement})).digest("hex"),l=K("sha256").update(e.managedAgentPolicy.adapterPolicy.workDir).digest("hex");if(s.runtimePermissionPolicyHash!==o||s.resolvedWorkDirHash!==l||s.enforcementLevel!==e.managedAgentPolicy.enforcement.level)throw new Error("managed_room_prompt_snapshot_mismatch")}function He(e){const t=e.indexOf(`
|
|
2
|
+
`),a=t>0?Math.min(t,80):Math.min(e.length,80);return e.slice(0,a)}function ge(e,t,a){return`${e}:${K("sha256").update(`${t}\0${a??""}`).digest("hex")}`}function ze(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 Be=3e4;function Le(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 fe(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function me(e,t){return e!=="manager"?e:t==="claude"?"claude":"codex"}function Ue(e,t){H({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 pe(e){return qe(e)}function he(e,t,a){return{}}function X(e,t,a,s,o,l,c,R){const S=a==="codex";e.configure?.({sessionId:t,externalChannel:s??null,env:{...pe(a),...he(t,s,o),...l&&!S?{SHENNIAN_MANAGED_ROOM_CONTEXT:l,[Ne]:Oe()}:{}},...c?{managedAgentPolicy:c}:{},...S?{managedRoomContextToken:l??null,managedRoomExecutor:l?R??null:null}:{}})}function ye(e,t,a){const s=t.managedRoomContextToken;s&&s!==a&&e.revokeManagedRoomContextToken?.(s),t.managedRoomContextToken=a}function C(e,t){const a=t?.managedRoomContextToken;!t||!a||(t.managedRoomContextToken=null,e.revokeManagedRoomContextToken?.(a))}function Je(e,t,a){if(e!=="claude"||!a)return t;const s=t.trim();return!!s&&s.startsWith("-")&&!s.includes("/")?_e(a)??t:t}function Qe(e,t,a,s){let o=null;function l(n,r={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:ze(n,t,r),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function c(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function R(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 S(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{R(n)},Be),n.heartbeatTimer.unref?.())}function h(n){const r=n?.pendingTextEvent;!n||!r||!r.text||(q(t,{id:`agent-${r.runId}-${r.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:r.text}),T(e.client),l({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),w=Le(n),_=n.state==="final"||n.state==="error"||n.state==="aborted";if(r&&(r.nextEventSeq=n.seq+1,_||(r.currentRunId=n.runId),!_&&w&&(r.currentRunPhase=w,S(r)),n.agentSessionId&&(r.agentSessionId=n.agentSessionId)),e.managerRuntime?.noteAgentEvent(t,n),n.state==="delta"&&n.thinking)return;n.state!=="delta"&&b({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}`;Ce({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}}),q(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"?(q(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:xe(n.approval)}),k=!0,r&&H({sessionId:t,agentType:a,workDir:r.workDir,agentSessionId:r.agentSessionId,attentionId:ge("approval",n.runId,n.seq)})):(n.state==="error"||n.state==="aborted")&&n.message&&(q(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.message}),k=!0);k&&T(e.client);const A=`${t}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&e.runTextAcc.set(A,(e.runTextAcc.get(A)??"")+n.text),n.agentSessionId&&n.agentSessionId!==o&&(o=n.agentSessionId,e.nativeFusion?.noteManagedSourceSession(t,me(a,n.source),n.agentSessionId),r&&H({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 W={};if(n.state==="final"){h(r);const m=e.runTextAcc.get(A)??"";m&&(W={messageSummary:He(m)}),e.runTextAcc.delete(A)}else n.state==="error"||n.state==="aborted"?(h(r),e.runTextAcc.delete(A)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&h(r);_&&r?.currentRunId===n.runId&&(C(e,r),r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,c(r),e.chatQueue?.noteTerminal(t),H({sessionId:t,agentType:a,workDir:r.workDir,agentSessionId:r.agentSessionId,status:n.state==="final"?"completed":"failed",attentionId:ge("terminal",n.runId),lastActivityAt:new Date().toISOString()}),T(e.client)),l(n,W)}),s.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${t} agentType=${a}: ${n.message}`),C(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 Ve(e,t){if(e.processedReqIds.add(t),e.processedReqIds.size>1e3){const a=e.processedReqIds.values().next().value;e.processedReqIds.delete(a)}}async function B(e,t){C(e,t),t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})}function Ie(e,t,a,s,o,l,c){return c?e.send(t,a,s,o,l,c):l?.trim()?e.send(t,a,s,o,l):o?.length?e.send(t,a,s,o):s?e.send(t,a,s):e.send(t,a)}function je(e,t,a,s,o,l,c){return e.prepareSend?e.prepareSend(t,a,s,o,l,c):Promise.resolve()}async function Ge(e,t,a,s,o,l,c,R,S,h="user"){e.evictIdleSessions();const n=Me(a);if(!n)throw new Error(`Unsupported agent: ${a}`);const r=s==="manager"&&a!=="manager"?new Te(n,a):n;X(r,t,a,c,R,null,S,e.managedRoomExecutor),h==="internal"?await r.start(t,o,l,{nativeSessionVisibility:h}):await r.start(t,o,l);const w={adapter:r,workDir:o,agentType:a,sessionMode:s,nativeSessionVisibility:h,agentSessionId:l??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:c??null,externalReplyTarget:R??null,externalChannelEnv:{...pe(a),...he(t,c,R)},managedRoomContextToken:null};return e.sessions.set(t,w),Qe(e,t,a,r),w}function Ke(e,t){const a=e.sessions.get(t),s=a?.currentRunId;if(!a||!s)return;const o=a.nextEventSeq;e.runTextAcc.delete(`${t}:${s}`),C(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 ht(e,t,a){const s=a?t.params.managedRoomContextToken??null:null;let o=!1;try{await Xe(e,t,a,()=>{o=!0})}finally{s&&!o&&e.revokeManagedRoomContextToken?.(s)}}async function Xe(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:l,agentType:c,sessionMode:R,managerConfig:S,workDir:h,agentSessionId:n,modelId:r,systemPrompt:w,managerDefaultWorkerAgentType:_,managerDefaultWorkerModelId:k,reasoningEffort:A,clientMessageId:W,sessionListProjection:m,waitForDispatch:Y,responseId:Re,managedRoomContextToken:P,suppressExternalOwnerEcho:Se,queuedBatchItems:L,managedWorkerParentSessionId:$}=t.params,y=Re||t.id;be(m);const O=null,U="";if(a?.source==="room_activation"&&(Fe(a,w,P),c==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(Ve(e,t.id),!o||!l){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:"sessionId and text are required"});return}const g=c,v=g==="manager"||R==="manager"?"manager":"standard";v==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(o,_??null,k??null);const N=(g==="claude"||g==="codex"||g==="workbuddy"||g==="pi-coding-agent")&&typeof A=="string"&&A.trim()?A.trim():void 0,E=e.resolvePath(Je(g,h||ie.homedir(),n)||ie.homedir()),D=a?.managedAgentPolicy??($?e.managerRuntime?.getManagedAgentPolicyForWorker($,E)??null:null);if($&&!D)throw new Error("managed_worker_permission_context_unavailable");const Z=g==="codex"&&(a?.source==="room_activation"||$)?"internal":"user",J=$&&D?We({requestedPolicy:D.requestedPolicy,enforcement:D.enforcement,resolvedWorkDir:D.adapterPolicy.workDir}):w,Ae=E,F=Pe(t.params.attachments);F?.length&&$e(E,F);const p={text:l,attachments:F,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"))!==v||i.workDir!==E||(i.nativeSessionVisibility??"user")!==Z||JSON.stringify(i.externalChannel??null)!==JSON.stringify(O??null)){e.sessions.delete(o);try{await B(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 B(e,i)}catch{e.processedReqIds.delete(t.id)}i=void 0}}if(!i)try{i=await Ge(e,o,g,v,E,n,O,U,D,Z)}catch(d){const f=d instanceof Error&&d.message.startsWith("Unsupported agent:")?d.message:`Failed to start ${c}: ${d instanceof Error?d.message:String(d)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${o} agentType=${c} workDir=${E} 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:y,ok:!1,error:f});return}const I={id:W??`user-${t.id}`,sessionId:o,role:"user",ts:Date.now(),payload:le(p.text,p.attachments)},ee=Array.isArray(L)&&L.length>0?L.map(d=>({id:d.clientMessageId??d.id,sessionId:o,role:"user",ts:Date.parse(d.createdAt),payload:le(d.text,d.attachments)})):null,Q=ee??[I],M=De(o,I.id),te=M.state==="queued";if(M.state!=="not_found"&&!te){e.client.sendRes({type:"res",id:y,ok:!0,payload:M});return}if(Y){a?.source==="room_activation"&&X(i.adapter,o,g,O,U,P,a.managedAgentPolicy,e.managedRoomExecutor);try{await je(i.adapter,p.text,r,N,p.attachments,J,a?{source:a.source}:void 0)}catch(d){e.processedReqIds.delete(t.id),e.sessions.delete(o),await B(e,i),e.client.sendRes({type:"res",id:y,ok:!1,error:d instanceof Error?d.message:String(d)});return}}b({level:"info",sessionId:o,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:g,sessionMode:v,managerConfig:S??null,modelId:r,managerDefaultWorkerAgentType:_??null,managerDefaultWorkerModelId:k??null,reasoningEffort:N,...a?.source==="room_activation"?{managedRoomPromptSnapshot:a.managedRoomPromptSnapshot}:{}}});const V=(d,f)=>({...ue(ce()),sessionId:o,clientMessageId:I.id,messageId:I.id,deliveryState:d,bodyRevision:f,committedAt:new Date(I.ts).toISOString()}),ne=d=>{e.nativeFusion?.registerManagedSend({sessionId:o,agentType:g,sourceAgentType:me(g,r),canonicalMessageId:I.id,sourceSessionKey:i.agentSessionId??n??null,text:p.text,managedEchoPolicy:d?.deliveryMode==="external_owner_queue"&&!Se?"import_following":"suppress_following",externalRunId:d?.deliveryMode==="external_owner_queue"?`codex-external-${d.queuedSubmissionId??I.id}`:null})},ae=()=>{i.currentRunId||(i.currentRunId=`dispatch:${t.id}`,i.currentRunPhase="thinking",i.nextEventSeq=0)},re=()=>{if(ne(),te)return{...ue(ce()),sessionId:o,clientMessageId:M.clientMessageId,messageId:M.messageId,deliveryState:M.deliveryState,bodyRevision:M.bodyRevision,committedAt:M.committedAt};try{Ue(e,{sessionId:o,agentType:g,sessionMode:v,managerConfig:S??null,workDir:Ae,agentSessionId:i.agentSessionId??n??null,modelId:r,managerDefaultWorkerAgentType:_??null,managerDefaultWorkerModelId:k??null});let d=0;for(const[f,u]of Q.entries()){const x=ee?z(o,u,"committed"):q(o,u,"committed");f===0&&(d=x)}return ve(E,F??[]),T(e.client),V("committed",d)}catch(d){throw e.nativeFusion?.cancelManagedSend?.(I.id),d}},oe=d=>{i.currentRunId||(i.nextEventSeq=0),ne(d);const f=d?.deliveryMode==="external_owner_queue"?"queued":"executing";let u=0;for(const[x,j]of Q.entries()){const G=z(o,j,f);x===0&&(u=G)}return T(e.client),V(f,u)},se=async(d,f)=>{e.nativeFusion?.cancelManagedSend?.(I.id);const u=fe(g,d);console.error(`[chat.send] send failed reqId=${t.id} sessionId=${o} agentType=${c} workDir=${E} agentSessionId=${i.agentSessionId??n??""}: ${u}`),e.sessions.delete(o);try{await B(e,i)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:u,runId:"",seq:0}}),!f){const x={id:`agent-error-${t.id}-${Date.now()}`,sessionId:o,role:"agent",ts:Date.now(),payload:u};q(o,x),T(e.client)}f&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:u}))};if(Y){try{re()}catch(u){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:`Durable message commit failed: ${u instanceof Error?u.message:String(u)}`});return}ae();let d;try{a?.source==="room_activation"&&(ye(e,i,P),s());const u=p.attachments;d=await Ie(i.adapter,p.text,r,N,u,J,a?{source:a.source}:void 0),b({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(u){let x=0;for(const[G,ke]of Q.entries()){const Ee=z(o,ke,"failed");G===0&&(x=Ee)}T(e.client);const j=fe(g,u);await se(u,!1),e.client.sendRes({type:"res",id:y,ok:!0,payload:{...V("failed",x),failureMessage:j}});return}const f=oe(d??void 0);e.client.sendRes({type:"res",id:y,ok:!0,payload:{...f,...p.localized?{localizedAttachments:!0}:{},...d?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:d.deliveryMode,queuedSubmissionId:d.queuedSubmissionId}:{}}}),b({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}let de;try{de=re()}catch(d){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:y,ok:!1,error:`Durable message commit failed: ${d instanceof Error?d.message:String(d)}`});return}ae(),a?.source==="room_activation"&&(X(i.adapter,o,g,O,U,P,a.managedAgentPolicy,e.managedRoomExecutor),ye(e,i,P),s()),oe(),e.client.sendRes({type:"res",id:y,ok:!0,payload:{...de,...p.localized?{localizedAttachments:!0}:{}}}),b({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const we=p.attachments;Ie(i.adapter,p.text,r,N,we,J,a?{source:a.source}:void 0).then(()=>{b({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(d=>{z(o,I,"failed"),T(e.client),se(d,!1)})}async function yt(e,t){const{sessionId:a}=t.params,s=e.sessions.get(a);if(s){C(e,s);try{await s.adapter.stop()}catch{e.client.sendRes({type:"res",id:t.id,ok:!1,error:"agent_stop_failed"});return}Ke(e,a),e.activityPublisher?.publish(a,null)}e.client.sendRes({type:"res",id:t.id,ok:!0})}export{yt as handleChatAbort,ht as handleChatSend,Ue as sendSessionUpdateEvent};
|
|
@@ -42,11 +42,14 @@ export declare class SessionManager {
|
|
|
42
42
|
private getRuntime;
|
|
43
43
|
getUpgradeIdleState(): UpgradeIdleState;
|
|
44
44
|
setManagedRoomTurnPreparer(prepare: (contexts: ManagedRoomQueueContext[]) => Promise<PreparedManagedRoomTurn>): void;
|
|
45
|
+
setManagedAgentDirectTurnPreparer(prepare: (sessionId: string) => Promise<PreparedManagedRoomTurn | null>): void;
|
|
45
46
|
enqueueManagedRoomBatch(inputs: Array<{
|
|
46
47
|
params: ManagedRoomEnqueueParams;
|
|
47
48
|
context: ManagedRoomQueueContext;
|
|
48
49
|
}>): Promise<void>;
|
|
49
50
|
discardPendingManagedRoomMessages(sessionId: string): number;
|
|
51
|
+
private managedChatContext;
|
|
52
|
+
private dispatchManagedChat;
|
|
50
53
|
private dispatchQueuedChat;
|
|
51
54
|
synchronizeSessionDeletions(): Promise<void>;
|
|
52
55
|
private synchronizeSessionDeletionsOnce;
|
|
@@ -1 +1 @@
|
|
|
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 M,handleAgentConfigGet as x,handleAgentConfigTest as O,handleAgentConfigUpsert as _}from"./handlers/agent-config.js";import{handleChatAbort as F,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 z}from"./handlers/title.js";import{handleSessionMessageStatus as U}from"./handlers/message-status.js";import{handleAgentWorkspaceValidation as W}from"./handlers/agent-workspace-validation.js";import{handleVoiceSessionBodyWindow as V}from"./handlers/voice-window.js";import{handleRemoteAccessPause as B}from"./handlers/remote-access.js";import{cleanupPendingTransfers as j,handleFsLs as $,handleFsRead as L,handleFsRename as H,handleFsWrite as Y,handleFsTransferAbort as G,handleFsTransferChunk as K,handleFsTransferFinish as X,handleFsTransferStart as Z,handleFsExportMarkdownPdf as J,handleFsExportMarkdownPdfSetup as q,handleFsArchiveZip as ee}from"./handlers/fs.js";import{handleRegionProbe as se,handleRegionSwitch as te,handleUpgradeSetPolicy as ae}from"./handlers/control.js";import{ManagerRuntimeService as ie,setManagerRuntimeService as b}from"../manager/runtime.js";import{ChatQueueManager as ne,TerminalQueuedChatDispatchError as oe}from"./queue.js";import{createAuthorizedFsRoot as re,createAuthorizedFsRootAsync as ce,resolveAuthorizedPath as le,resolveAuthorizedPathAsync as he,resolveSessionWorkDir as de}from"../fs/boundary.js";import"../agents/claude.js";import"../agents/codex.js";import"../agents/workbuddy.js";import"../agents/gemini.js";import"../agents/cursor.js";import"../agents/opencode.js";import"../agents/pi-coding-agent.js";import"../agents/manager.js";import{registerCustomAgent as ue}from"../agents/custom.js";import{handleManagedRoomBind as me,handleManagedRoomRestart as pe}from"./handlers/room-managed.js";import{getDaemonInstallationId as y,listSessionRecords as R,purgeCanonicalSession as fe,purgeSessionRecord as ge}from"./store.js";import{PersonalHistorySubscriptions as ve}from"./history-subscriptions.js";import{sweepAttachmentTransfers as be}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as ye}from"../native-fusion/native-source-deletions.js";import{purgeToolDetails as Re}from"./tool-detail-store.js";import{readDeletionSyncRevision as we,writeDeletionSyncRevision as w}from"./deletion-sync-state.js";import{assertDeletionSyncRunnable as ke,clearDeletionSyncFailure as Se,DeletionSyncRetryWaitError as Ae,DeletionSyncTerminalError as Te,recordDeletionSyncFailure as m}from"./deletion-sync-state.js";import{NativeSessionCleanupOutbox as Ie}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as l}from"../personal-sync/epoch.js";const Ce=50;function Pe(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 t=r.baselineRevision;for(const s of r.deletions){if(!s?.sessionId||!Number.isSafeInteger(s.deletionRevision)||s.deletionRevision!==t+1||!Array.isArray(s.sources)||Number.isNaN(Date.parse(s.deletedAt))||s.nativeCleanupPolicy!==void 0&&s.nativeCleanupPolicy!=="none"&&s.nativeCleanupPolicy!=="auto")throw new Error("invalid session deletion command");for(const a of s.sources)if(!a?.sourceSessionKey||!k(a.agentType))throw new Error("invalid native source deletion command");t=s.deletionRevision}if(t!==r.latestRevision)throw new Error("invalid deletion sync revision range")}import{resolveSessionWorkDir as bs}from"../fs/boundary.js";class fs{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,t=null,s,a=null,i=null,c=null,n=null,h=null){this.client=e,this.nativeFusion=t,this.cliVersion=s,this.upgradePolicyController=a,this.managedRoomBinder=i,this.remoteAccessPauseController=c,this.managedRoomContextRevoker=n,this.managedRoomExecutor=h,this.managerRuntime=new ie({getRuntime:()=>this.getRuntime(),dispatchReq:o=>this.handleReq(o,{propagateErrors:!0})}),this.chatQueue=new ne({getRuntime:()=>this.getRuntime(),dispatchReq:o=>this.handleReq(o),dispatchQueuedReq:o=>this.dispatchQueuedChat(o)}),this.historySubscriptions=new ve(this.client),this.nativeCleanupOutbox=new Ie({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([...R().map(d=>d.workDir),...Array.from(this.pendingTransfers.values()).map(d=>d.rootPath)]);for(const d of o)try{be(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,t)=>this.resolveAuthorizedPath(e,t),resolveAuthorizedPathAsync:(e,t)=>this.resolveAuthorizedPathAsync(e,t),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,t)=>this.publishSessionActivity(e,t)},upgradePolicyController:this.upgradePolicyController,revokeManagedRoomContextToken:this.managedRoomContextRevoker,managedRoomExecutor:this.managedRoomExecutor}}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(t=>t.currentRunId!==null)&&e.push("active-agent-turn"),this.pendingTransfers.size>0&&e.push("file-transfer"),this.activeRequests>0&&e.push("active-daemon-request"),{idle:e.length===0,reasons:e}}setManagedRoomTurnPreparer(e){this.chatQueue.setManagedRoomTurnPreparer(e)}async enqueueManagedRoomBatch(e){const t=e.map(s=>{const a=R().find(i=>i.sessionId===s.params.sessionId);return{context:s.context,params:{...s.params,modelId:a?.modelId??s.params.modelId,sessionMode:a?.sessionMode??s.params.sessionMode,managerConfig:a?.managerConfig??s.params.managerConfig,managerDefaultWorkerAgentType:a?.managerDefaultWorkerAgentType??s.params.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:a?.managerDefaultWorkerModelId??s.params.managerDefaultWorkerModelId}}});await this.chatQueue.enqueueManagedRoomBatch(t)}discardPendingManagedRoomMessages(e){return this.chatQueue.discardPendingManagedRoomMessages(e)}async dispatchQueuedChat(e){const t={value:null},s=new Proxy(this.client,{get:(c,n)=>{if(n==="sendRes")return o=>{t.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:s},e,i),t.value?.payload?.deliveryState==="failed")throw new oe(t.value.payload.failureMessage??t.value.error??"queued_chat_dispatch_failed");if(!t.value?.ok)throw new Error(t.value?.error??"queued_chat_dispatch_failed")}async synchronizeSessionDeletions(){ke();try{await this.synchronizeSessionDeletionsOnce(),Se()}catch(e){if(e instanceof Ae||e instanceof Te)throw e;const t=e instanceof Error?e.message:String(e),s=/offline|not connected|disconnect|timed out|timeout|temporar|unavailable|ECONN|ENOTFOUND|EIO|EBUSY/i.test(t);throw m({code:s?"transport_unavailable":"invalid_deletion_sync_response",retryable:s}),e}}async synchronizeSessionDeletionsOnce(){const e=y(),t=we(),s=await this.client.sendReq({type:"req",id:`session-deletions-sync-${g.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:p,dataEpoch:f,daemonInstallationId:e,afterRevision:t}});if(!s.ok){const n=s.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(s.error??"unstructured_rejection"))}const a=s.payload;l(a),Pe(a,t);let i=a.baselineRevision;i>t&&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 t=new ye({daemonInstallationId:y()});t.recordSession(e);for(const a of e.sources)t.record({...e,...a});const s=this.sessions.get(e.sessionId);if(s){this.revokeManagedRoomToken(s);try{await s.adapter.stop()}catch{}this.sessions.delete(e.sessionId)}this.historySubscriptions.closeSession(e.sessionId),this.nativeFusion?.handleSessionDeleted(e.sessionId),this.chatQueue.purgeSession(e.sessionId),Re(e.sessionId),fe(e.sessionId),ge(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null),this.nativeCleanupOutbox.enqueue(e),this.nativeCleanupOutbox.kick()}publishSessionActivity(e,t){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:t}})}async handleExternalTerminal(e){const t=this.sessions.get(e);t&&(this.sessions.delete(e),this.revokeManagedRoomToken(t),t.heartbeatTimer&&clearInterval(t.heartbeatTimer),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})),this.chatQueue.noteTerminal(e)}async publishManagedActivitySnapshots(){for(const[e,t]of this.sessions.entries()){if(t.heartbeatTimer||!t.currentRunId||!t.adapter.getStatus)continue;const s=await t.adapter.getStatus().catch(()=>null);if(!s?.active||!s.runPhase)continue;const a=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:s.runId||t.currentRunId,runPhase:s.runPhase,startedAt:new Date(t.lastActiveAt).toISOString(),updatedAt:a,canStop:s.canStop??!0});const i=t.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:s.runId||t.currentRunId,seq:i,runPhase:s.runPhase,canStop:s.canStop??!0},seq:i,id:`agent-status-${s.runId||t.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const t of S())t.startsWith("custom:")&&A(t);const e=T();for(const[t,s]of Object.entries(e.customAgents??{}))ue(t,s)}async handleReq(e,t={}){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 F(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":U(s,e);break;case"session.tool.detail":await Q(s,e);break;case"voice.session.body.window":V(s,e);break;case"session.title.set":await z(s,e);break;case"room.agent.bind-managed":await me(s,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await pe(s,this.managedRoomBinder,e);break;case"fs.ls":await $(s,e);break;case"fs.read":await L(s,e);break;case"fs.write":await Y(s,e);break;case"fs.export.markdown-pdf":await J(s,e);break;case"fs.export.markdown-pdf.setup":await q(s,e);break;case"fs.archive.zip":await ee(s,e);break;case"fs.rename":await H(s,e);break;case"fs.transfer.start":await Z(s,e);break;case"fs.transfer.chunk":await K(s,e);break;case"fs.transfer.finish":await X(s,e);break;case"fs.transfer.abort":await G(s,e);break;case"region.probe":await se(s,e);break;case"region.switch":await te(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 ae(s,e);break;case"machine.remote-access.pause":await B(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 x(s,e);break;case"agent.config.upsert":await _(s,e);break;case"agent.config.clear":await M(s,e);break;case"agent.config.test":await O(s,e);break;case"agent.workspace.validate":await W(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(a){if(t.propagateErrors)throw a;this.client.sendRes({type:"res",id:e.id,ok:!1,error:a instanceof Error?a.message:String(a)})}finally{this.activeRequests--}}evictIdleSessions(){if(this.sessions.size<Ce)return;let e=null;for(const[t,s]of this.sessions)(!e||s.lastActiveAt<e.session.lastActiveAt)&&(e={key:t,session:s});e&&(this.revokeManagedRoomToken(e.session),e.session.heartbeatTimer&&(clearInterval(e.session.heartbeatTimer),e.session.heartbeatTimer=null),e.session.adapter.removeAllListeners(),e.session.adapter.stop().catch(()=>{}),this.sessions.delete(e.key))}resolvePath(e){return de(e)}resolveAuthorizedPath(e,t){return le(e,re(t))}async resolveAuthorizedPathAsync(e,t){return he(e,await ce(t))}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(),j(this.getRuntime()),await this.managerRuntime.stop(),b(null)}revokeManagedRoomToken(e){const t=e.managedRoomContextToken;e.managedRoomContextToken=null,t&&this.managedRoomContextRevoker?.(t)}}export{fs as SessionManager,bs as resolveSessionWorkDir};
|
|
1
|
+
import{SESSION_SYNC_PROTOCOL_VERSION as g,SESSION_SYNC_DATA_EPOCH as f,isAvailableAgentType as k}from"@shennian/wire";import v 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 C,handleUpgradeStatus as I}from"../commands/upgrade.js";import{handleAgentsRefresh as P,handleModelsRefresh as M}from"./handlers/agents.js";import{handleAgentCapabilitiesList as E}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as D,handleAgentConfigGet as x,handleAgentConfigTest as O,handleAgentConfigUpsert as _}from"./handlers/agent-config.js";import{handleChatAbort as F,handleChatSend as m}from"./handlers/chat.js";import{handleSessionRefresh as Q}from"./handlers/session-refresh.js";import{handleSessionToolDetail as N}from"./handlers/tool-detail.js";import{handleSessionTitleSet as z}from"./handlers/title.js";import{handleSessionMessageStatus as U}from"./handlers/message-status.js";import{handleAgentWorkspaceValidation as W}from"./handlers/agent-workspace-validation.js";import{handleVoiceSessionBodyWindow as V}from"./handlers/voice-window.js";import{handleRemoteAccessPause as B}from"./handlers/remote-access.js";import{cleanupPendingTransfers as j,handleFsLs as $,handleFsRead as L,handleFsRename as H,handleFsWrite as Y,handleFsTransferAbort as G,handleFsTransferChunk as K,handleFsTransferFinish as X,handleFsTransferStart as Z,handleFsExportMarkdownPdf as J,handleFsExportMarkdownPdfSetup as q,handleFsArchiveZip as ee}from"./handlers/fs.js";import{handleRegionProbe as te,handleRegionSwitch as se,handleUpgradeSetPolicy as ae}from"./handlers/control.js";import{ManagerRuntimeService as ie,setManagerRuntimeService as b}from"../manager/runtime.js";import{ChatQueueManager as ne,TerminalQueuedChatDispatchError as oe}from"./queue.js";import{createAuthorizedFsRoot as re,createAuthorizedFsRootAsync as ce,resolveAuthorizedPath as le,resolveAuthorizedPathAsync as he,resolveSessionWorkDir as de}from"../fs/boundary.js";import"../agents/claude.js";import"../agents/codex.js";import"../agents/workbuddy.js";import"../agents/gemini.js";import"../agents/cursor.js";import"../agents/opencode.js";import"../agents/pi-coding-agent.js";import"../agents/manager.js";import{registerCustomAgent as ue}from"../agents/custom.js";import{handleManagedRoomBind as me,handleManagedRoomRestart as pe}from"./handlers/room-managed.js";import{getDaemonInstallationId as y,listSessionRecords as R,purgeCanonicalSession as ge,purgeSessionRecord as fe}from"./store.js";import{PersonalHistorySubscriptions as ve}from"./history-subscriptions.js";import{sweepAttachmentTransfers as be}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as ye}from"../native-fusion/native-source-deletions.js";import{purgeToolDetails as Re}from"./tool-detail-store.js";import{readDeletionSyncRevision as we,writeDeletionSyncRevision as w}from"./deletion-sync-state.js";import{assertDeletionSyncRunnable as ke,clearDeletionSyncFailure as Se,DeletionSyncRetryWaitError as Ae,DeletionSyncTerminalError as Te,recordDeletionSyncFailure as p}from"./deletion-sync-state.js";import{NativeSessionCleanupOutbox as Ce}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as l}from"../personal-sync/epoch.js";const Ie=50;function Pe(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 bt}from"../fs/boundary.js";class gt{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,h=null,n=null,d=null){this.client=e,this.nativeFusion=s,this.cliVersion=t,this.upgradePolicyController=a,this.managedRoomBinder=i,this.remoteAccessPauseController=h,this.managedRoomContextRevoker=n,this.managedRoomExecutor=d,this.managerRuntime=new ie({getRuntime:()=>this.getRuntime(),dispatchReq:o=>this.handleReq(o,{propagateErrors:!0})}),this.chatQueue=new ne({getRuntime:()=>this.getRuntime(),dispatchReq:o=>this.handleReq(o),dispatchManagedReq:(o,c)=>this.dispatchManagedChat(o,c),dispatchQueuedReq:(o,c)=>this.dispatchQueuedChat(o,c)}),this.historySubscriptions=new ve(this.client),this.nativeCleanupOutbox=new Ce({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([...R().map(c=>c.workDir),...Array.from(this.pendingTransfers.values()).map(c=>c.rootPath)]);for(const c of o)try{be(this.resolvePath(c))}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),resolveAuthorizedPathAsync:(e,s)=>this.resolveAuthorizedPathAsync(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)}setManagedAgentDirectTurnPreparer(e){this.chatQueue.setManagedAgentDirectTurnPreparer(e)}async enqueueManagedRoomBatch(e){const s=e.map(t=>{const a=R().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)}discardPendingManagedRoomMessages(e){return this.chatQueue.discardPendingManagedRoomMessages(e)}managedChatContext(e){return{source:"room_activation",managedAgentPolicy:e.managedAgentPolicy,managedRoomPromptSnapshot:e.managedRoomPromptSnapshot}}async dispatchManagedChat(e,s){await m(this.getRuntime(),e,this.managedChatContext(s))}async dispatchQueuedChat(e,s){const t={value:null},a=new Proxy(this.client,{get:(i,h)=>{if(h==="sendRes")return d=>{t.value=d};const n=Reflect.get(i,h,i);return typeof n=="function"?n.bind(i):n}});if(await m({...this.getRuntime(),client:a},e,s?this.managedChatContext(s):void 0),t.value?.payload?.deliveryState==="failed")throw new oe(t.value.payload.failureMessage??t.value.error??"queued_chat_dispatch_failed");if(!t.value?.ok)throw new Error(t.value?.error??"queued_chat_dispatch_failed")}async synchronizeSessionDeletions(){ke();try{await this.synchronizeSessionDeletionsOnce(),Se()}catch(e){if(e instanceof Ae||e instanceof Te)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 p({code:t?"transport_unavailable":"invalid_deletion_sync_response",retryable:t}),e}}async synchronizeSessionDeletionsOnce(){const e=y(),s=we(),t=await this.client.sendReq({type:"req",id:`session-deletions-sync-${v.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:g,dataEpoch:f,daemonInstallationId:e,afterRevision:s}});if(!t.ok){const n=t.rejection;throw n?(p({code:n.code,retryable:n.retryable&&n.requiredAction==="retry"}),new Error(n.code)):(p({code:"unstructured_rejection",retryable:!1}),new Error(t.error??"unstructured_rejection"))}const a=t.payload;l(a),Pe(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 h=await this.client.sendReq({type:"req",id:`session-deletions-ack-${v.randomUUID()}`,method:"session.deletions.ack",params:{protocolVersion:g,dataEpoch:f,daemonInstallationId:e,deletionRevision:i}});if(!h.ok)throw new Error(h.error??"session deletion acknowledgement failed")}async applySessionDeletion(e){const s=new ye({daemonInstallationId:y()});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),Re(e.sessionId),ge(e.sessionId),fe(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??{}))ue(s,t)}async handleReq(e,s={}){const t=this.getRuntime();this.activeRequests++;try{switch(e.method){case"chat.send":l(e.params),await m(t,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 F(t,e);break;case"session.refresh":l(e.params),await Q(t,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":U(t,e);break;case"session.tool.detail":await N(t,e);break;case"voice.session.body.window":V(t,e);break;case"session.title.set":await z(t,e);break;case"room.agent.bind-managed":await me(t,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await pe(t,this.managedRoomBinder,e);break;case"fs.ls":await $(t,e);break;case"fs.read":await L(t,e);break;case"fs.write":await Y(t,e);break;case"fs.export.markdown-pdf":await J(t,e);break;case"fs.export.markdown-pdf.setup":await q(t,e);break;case"fs.archive.zip":await ee(t,e);break;case"fs.rename":await H(t,e);break;case"fs.transfer.start":await Z(t,e);break;case"fs.transfer.chunk":await K(t,e);break;case"fs.transfer.finish":await X(t,e);break;case"fs.transfer.abort":await G(t,e);break;case"region.probe":await te(t,e);break;case"region.switch":await se(t,e);break;case"upgrade.start":await C(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0});break;case"upgrade.status":await I(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await ae(t,e);break;case"machine.remote-access.pause":await B(this.client,this.remoteAccessPauseController,e);break;case"agents.refresh":await P(t,e);break;case"models.refresh":await M(t,e);break;case"agent.config.get":await x(t,e);break;case"agent.config.upsert":await _(t,e);break;case"agent.config.clear":await D(t,e);break;case"agent.config.test":await O(t,e);break;case"agent.workspace.validate":await W(t,e);break;case"agent.capabilities.list":await E(t,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(a){if(s.propagateErrors)throw a;this.client.sendRes({type:"res",id:e.id,ok:!1,error:a instanceof Error?a.message:String(a)})}finally{this.activeRequests--}}evictIdleSessions(){if(this.sessions.size<Ie)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 de(e)}resolveAuthorizedPath(e,s){return le(e,re(s))}async resolveAuthorizedPathAsync(e,s){return he(e,await ce(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(),j(this.getRuntime()),await this.managerRuntime.stop(),b(null)}revokeManagedRoomToken(e){const s=e.managedRoomContextToken;e.managedRoomContextToken=null,s&&this.managedRoomContextRevoker?.(s)}}export{gt as SessionManager,bt as resolveSessionWorkDir};
|
|
@@ -11,6 +11,9 @@ export type ManagedRoomQueueContext = {
|
|
|
11
11
|
hopCount: number;
|
|
12
12
|
};
|
|
13
13
|
export type PreparedManagedRoomTurn = {
|
|
14
|
+
agentType: AgentType;
|
|
15
|
+
workDir: string;
|
|
16
|
+
agentSessionId: string;
|
|
14
17
|
systemPrompt: string;
|
|
15
18
|
sessionMode: SessionMode;
|
|
16
19
|
managerConfig: ManagerConfig | null;
|
|
@@ -30,13 +33,16 @@ export declare class ChatQueueManager {
|
|
|
30
33
|
private draining;
|
|
31
34
|
private inFlightMessageIds;
|
|
32
35
|
private prepareManagedRoomTurn;
|
|
36
|
+
private prepareManagedAgentDirectTurn;
|
|
33
37
|
constructor(opts: {
|
|
34
38
|
getRuntime: () => SessionManagerRuntime;
|
|
35
39
|
dispatchReq: LocalReqDispatcher;
|
|
36
|
-
|
|
40
|
+
dispatchManagedReq?: (req: ReqFrame, prepared: PreparedManagedRoomTurn) => Promise<void>;
|
|
41
|
+
dispatchQueuedReq: (req: ReqFrame, prepared?: PreparedManagedRoomTurn) => Promise<void>;
|
|
37
42
|
});
|
|
38
43
|
getSnapshot(sessionId: string): ChatQueueSnapshot;
|
|
39
44
|
setManagedRoomTurnPreparer(prepare: (contexts: ManagedRoomQueueContext[]) => Promise<PreparedManagedRoomTurn>): void;
|
|
45
|
+
setManagedAgentDirectTurnPreparer(prepare: (sessionId: string) => Promise<PreparedManagedRoomTurn | null>): void;
|
|
40
46
|
enqueueManagedRoomBatch(inputs: Array<{
|
|
41
47
|
params: ManagedRoomEnqueueParams;
|
|
42
48
|
context: ManagedRoomQueueContext;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import h from"node:fs";import{randomUUID as q}from"node:crypto";import{CODEX_DESKTOP_SEND_UNAVAILABLE as
|
|
2
|
-
`,{encoding:"utf8",mode:384,flag:"wx"});const i=h.openSync(t,"r");if(typeof i=="number")try{h.fsyncSync(i)}finally{h.closeSync(i)}h.renameSync(t,n)}finally{try{h.unlinkSync(t)}catch{}}}function A(){return T("outbound-queue.json")}function Q(s){const e=$();return{...S(R()),id:s.queueMessageId||s.clientMessageId||`queue-${q()}`,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:D(s.attachments),externalChannel:s.externalChannel??null,replyTarget:s.replyTarget??null,origin:s.origin,createdAt:e,updatedAt:e}}function x(s){return{id:s.clientMessageId??s.id,sessionId:s.sessionId,role:"user",ts:Date.parse(s.createdAt),payload:j(s.text,s.attachments)}}class he{opts;draining=new Set;inFlightMessageIds=new Map;prepareManagedRoomTurn=null;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}getSnapshot(e){return{...S(R()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:(f().sessions[e]??[]).map(B)}}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const n=this.opts.getRuntime(),t=f(),i=new Set,o=new Map;let a=!1;for(const r of e){const d=r.params;if(!d.sessionId||!d.text||!d.agentType||!d.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=Q({...S(R()),...d});u.origin="room",u.managedRoom=r.context;const c=t.sessions[d.sessionId]??[],l=N(c,u),I=u.clientMessageId??u.id,y=k(d.sessionId,I);if(!l&&y.state==="not_found")c.push(u),t.sessions[d.sessionId]=c,o.set(`${u.sessionId}\0${I}`,u),i.add(d.sessionId),a=!0;else if(l&&y.state==="not_found")o.set(`${l.sessionId}\0${I}`,l),i.add(d.sessionId);else if(l&&y.state==="queued")i.add(d.sessionId);else if(l){const m=c.filter(g=>g.id!==l.id);m.length?t.sessions[d.sessionId]=m:delete t.sessions[d.sessionId],a=!0}}a&&p(t);for(const r of o.values())v(r.sessionId,x(r),"queued"),b({sessionId:r.sessionId,agentType:r.agentType,sessionMode:r.sessionMode,managerConfig:r.managerConfig,workDir:r.workDir,agentSessionId:W(n,r),modelId:r.modelId});if(i.size!==0){M(n.client);for(const r of i)this.logLifecycle("enqueue.persisted",r,{origin:"room",pendingCount:f().sessions[r]?.length??0}),this.broadcast(r),this.drainNext(r)}}async handleEnqueue(e){const n=this.opts.getRuntime(),t=e.params;if(J(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),o=D(t.attachments);o?.length&&w(i,o);const a={text:t.text,attachments:o,localized:!1},r=n.sessions.get(t.sessionId);if(t.agentType==="codex"&&t.agentSessionId&&r?.currentRunId?.startsWith("dispatch:")&&K(t.agentSessionId)){n.client.sendRes({type:"res",id:e.id,ok:!1,error:z});return}const d=await this.isSessionBusy(t.sessionId),u=f(),c=u.sessions[t.sessionId]??[];if(!d&&!c.length){await this.opts.dispatchReq({...e,id:H(e.id),method:"chat.send",params:{...t,text:a.text,responseId:e.id,clientMessageId:t.clientMessageId??t.queueMessageId,waitForDispatch:!0,attachments:a.attachments}});return}const l=Q({...t,text:a.text,attachments:a.attachments}),I=l.clientMessageId??l.id,y=k(t.sessionId,I);if(y.state!=="not_found"){n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:y.state==="queued",queueMessageId:l.id,...y,queue:this.getSnapshot(t.sessionId)}});return}const m=N(c,l);m||(u.sessions[t.sessionId]=[...c,l],p(u));const g=m??l;b({sessionId:g.sessionId,agentType:g.agentType,sessionMode:g.sessionMode,managerConfig:g.managerConfig,workDir:g.workDir,agentSessionId:g.agentSessionId,modelId:g.modelId}),v(g.sessionId,x(g),"queued"),C(n.resolvePath(g.workDir),g.attachments??[]),M(n.client),this.logLifecycle("enqueue.persisted",t.sessionId,{origin:t.origin??"app",queueMessageId:g.id,pendingCount:(f().sessions[t.sessionId]??[]).length}),this.broadcast(t.sessionId),n.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:m?.id??l.id,...k(t.sessionId,m?.clientMessageId??m?.id??l.clientMessageId??l.id),queue:this.getSnapshot(t.sessionId),...a.localized?{localizedAttachments:!0}:{}}}),d||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=f(),o=i.sessions[t.sessionId]??[],a=o.findIndex(u=>u.id===t.queueMessageId);if(a<0){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(o[a].managedRoom){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const r=D(t.attachments),d=n.resolvePath(o[a].workDir);r?.length&&w(d,r),o[a]={...o[a],text:t.text,attachments:r,updatedAt:$()},i.sessions[t.sessionId]=o,p(i),P(t.sessionId,x(o[a]),"queued"),o[a].attachments?.length&&C(d,o[a].attachments??[]),M(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=f(),o=i.sessions[t.sessionId]??[],a=o.find(d=>d.id===t.queueMessageId);if(a?.managedRoom){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const r=o.filter(d=>d.id!==t.queueMessageId);if(r.length===o.length){n.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}r.length?i.sessions[t.sessionId]=r:delete i.sessions[t.sessionId],p(i),E(t.sessionId,a?.clientMessageId??a?.id??t.queueMessageId),M(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){X(e),this.draining.delete(e)}discardPendingManagedRoomMessages(e){const n=f(),t=n.sessions[e]??[],i=this.inFlightMessageIds.get(e)??new Set,o=t.filter(r=>r.managedRoom&&!i.has(r.id));if(o.length===0)return 0;const a=t.filter(r=>!o.includes(r));a.length?n.sessions[e]=a:delete n.sessions[e],p(n);for(const r of o)E(e,r.clientMessageId??r.id);return M(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:o.length,pendingCount:a.length}),this.broadcast(e),o.length}drainIdleQueues(){const e=f();for(const n of Object.keys(e.sessions))this.drainNext(n)}async drainNext(e){if(this.draining.has(e))return;this.draining.add(e);let n=!1,t=[];try{if(await this.isSessionBusy(e)){this.logLifecycle("drain.deferred",e,{pendingCount:f().sessions[e]?.length??0});return}const i=f(),o=i.sessions[e]??[],a=o.filter(u=>{const c=u.clientMessageId??u.id,l=k(e,c).state;return l==="queued"||l==="not_found"});a.length!==o.length&&(a.length?i.sessions[e]=a:delete i.sessions[e],p(i),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:o.length-a.length,pendingCount:a.length}));const r=Y(a);if(r.length===0){this.broadcast(e);return}t=r,this.inFlightMessageIds.set(e,new Set(r.map(u=>u.id))),this.logLifecycle("dispatch.start",e,{batchCount:r.length,queueMessageId:r[0].id,origin:r[0].origin??"app"});for(const u of r)P(e,x(u),"queued");await this.dispatchQueuedMessages(r);const d=this.removeBatch(e,r);this.logLifecycle("dispatch.accepted",e,{batchCount:r.length,queueMessageId:r[0].id,pendingCount:d}),n=d>0,this.broadcast(e)}catch(i){if(i instanceof V){const o=this.removeBatch(e,t);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:t.length,queueMessageId:t[0]?.id,pendingCount:o,errorCode:O(i.message,"terminal_dispatch_failed")},"error"),n=o>0}else this.logLifecycle("dispatch.transient-failure",e,{errorCode:O(i instanceof Error?i.message:String(i),"transient_dispatch_failed")},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e),n&&queueMicrotask(()=>{this.drainNext(e)})}}removeBatch(e,n){if(n.length===0)return f().sessions[e]?.length??0;const t=f(),i=new Set(n.map(a=>a.id)),o=(t.sessions[e]??[]).filter(a=>!i.has(a.id));return o.length?t.sessions[e]=o:delete t.sessions[e],p(t),o.length}logLifecycle(e,n,t,i="info"){const o=`[chat.queue] ${JSON.stringify({event:e,sessionId:n,...t})}`;i==="error"?console.error(o):console.info(o)}async dispatchQueuedMessages(e){const n=e[0],t=e.flatMap(c=>c.managedRoom?[c.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 o=t.length>0?i:null,a=o?.sessionMode==="manager"?o.managerConfig:null,r=e.length===1?n.text:Z(e),d=te(e.flatMap(c=>c.attachments??[])),u=t.length>0?W(this.opts.getRuntime(),n):n.agentSessionId??null;await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${n.id}-${Date.now()}`,method:"chat.send",params:{...S(R()),sessionId:n.sessionId,text:r,agentType:n.agentType,sessionMode:o?.sessionMode??n.sessionMode,managerConfig:o?a:n.managerConfig,workDir:n.workDir,agentSessionId:u,modelId:n.modelId??void 0,systemPrompt:i?.systemPrompt??n.systemPrompt??null,managerDefaultWorkerAgentType:o?o.managerDefaultWorkerAgentType:n.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:o?o.managerDefaultWorkerModelId: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(B),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 W(s,e){if(s.sessions.has(e.sessionId))return null;const t=U().find(i=>i.sessionId===e.sessionId)?.agentSessionId?.trim();return!t||t.startsWith("managed:")?null:t}function X(s){const e=f();e.sessions[s]&&(delete e.sessions[s],p(e))}function N(s,e){const n=_(e);if(n)return s.find(i=>_(i)===n)??null;if(e.origin!=="external")return null;const t=F(e);return s.find(i=>F(i)===t)??null}function B(s){const{origin:e,managedRoom:n,...t}=s;return t}function Y(s){const e=s[0];if(!e)return[];const n=L(e),t=[];for(const i of s){if(L(i)!==n)break;t.push(i)}return t}function L(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 Z(s){return s.every(e=>e.managedRoom)?`[\u7FA4\u6D88\u606F]
|
|
3
|
-
${s.map(e=>
|
|
1
|
+
import h from"node:fs";import{randomUUID as q}from"node:crypto";import{CODEX_DESKTOP_SEND_UNAVAILABLE as V,buildUserMessagePayload as G}from"@shennian/wire";import{isCodexThreadTailInterrupted as H}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as X}from"./projection.js";import{parsePersonalAttachmentReferences as C}from"./attachment-reference.js";import{assertFinishedAttachmentReferences as E,markAttachmentReferences as b}from"./attachment-transfer-store.js";import{resolvePersonalSyncV1Path as P}from"../personal-sync/paths.js";import{getPersonalSyncIdentity as S}from"../personal-sync/epoch.js";import{getDaemonInstallationId as R}from"../personal-sync/identity.js";import{appendMessage as $,getMessageDeliveryStatus as w,listSessionRecords as v,recordSession as W,tombstoneMessage as Q,upsertMessage as N}from"./store.js";import{requestSessionIndexPublish as D}from"./index-publisher.js";class Y extends Error{constructor(e){super(e),this.name="TerminalQueuedChatDispatchError"}}function Z(){return{sessions:{}}}function B(){return new Date().toISOString()}function ee(s){return`enqueue-send-${s}-${q()}`}function g(){try{const s=JSON.parse(h.readFileSync(L(),"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 Z();throw s}}function y(s){const e=P();h.mkdirSync(e,{recursive:!0,mode:448});const t=L(),n=`${t}.tmp-${process.pid}-${q()}`;try{h.writeFileSync(n,`${JSON.stringify(s,null,2)}
|
|
2
|
+
`,{encoding:"utf8",mode:384,flag:"wx"});const i=h.openSync(n,"r");if(typeof i=="number")try{h.fsyncSync(i)}finally{h.closeSync(i)}h.renameSync(n,t)}finally{try{h.unlinkSync(n)}catch{}}}function L(){return P("outbound-queue.json")}function F(s){const e=B();return{...S(R()),id:s.queueMessageId||s.clientMessageId||`queue-${q()}`,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:C(s.attachments),externalChannel:s.externalChannel??null,replyTarget:s.replyTarget??null,origin:s.origin,createdAt:e,updatedAt:e}}function x(s){return{id:s.clientMessageId??s.id,sessionId:s.sessionId,role:"user",ts:Date.parse(s.createdAt),payload:G(s.text,s.attachments)}}class Me{opts;draining=new Set;inFlightMessageIds=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}getSnapshot(e){return{...S(R()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:(g().sessions[e]??[]).map(j)}}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const t=this.opts.getRuntime(),n=g(),i=new Set,r=new Map;let o=!1;for(const a of e){const d=a.params;if(!d.sessionId||!d.text||!d.agentType||!d.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=F({...S(R()),...d});u.origin="room",u.managedRoom=a.context;const p=n.sessions[d.sessionId]??[],c=z(p,u),l=u.clientMessageId??u.id,I=w(d.sessionId,l);if(!c&&I.state==="not_found")p.push(u),n.sessions[d.sessionId]=p,r.set(`${u.sessionId}\0${l}`,u),i.add(d.sessionId),o=!0;else if(c&&I.state==="not_found")r.set(`${c.sessionId}\0${l}`,c),i.add(d.sessionId);else if(c&&I.state==="queued")i.add(d.sessionId);else if(c){const m=p.filter(_=>_.id!==c.id);m.length?n.sessions[d.sessionId]=m:delete n.sessions[d.sessionId],o=!0}}o&&y(n);for(const a of r.values())$(a.sessionId,x(a),"queued"),W({sessionId:a.sessionId,agentType:a.agentType,sessionMode:a.sessionMode,managerConfig:a.managerConfig,workDir:a.workDir,agentSessionId:O(t,a),modelId:a.modelId});if(i.size!==0){D(t.client);for(const a of i)this.logLifecycle("enqueue.persisted",a,{origin:"room",pendingCount:g().sessions[a]?.length??0}),this.broadcast(a),this.drainNext(a)}}async handleEnqueue(e){const t=this.opts.getRuntime(),n=e.params;if(X(n.sessionListProjection),!n.sessionId||!n.text||!n.agentType||!n.workDir){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"});return}const i=n.managedAgentDirect?await this.prepareManagedAgentDirectTurn?.(n.sessionId):null;if(n.managedAgentDirect&&!i)throw new Error("managed_agent_context_unavailable");const r=i?v().find(k=>k.sessionId===n.sessionId):null,o=i?{...n,...i,agentType:i.agentType,sessionMode:i.sessionMode,managerConfig:i.managerConfig,workDir:i.workDir,agentSessionId:i.agentSessionId,modelId:r?.modelId??void 0,systemPrompt:i.systemPrompt,managerDefaultWorkerAgentType:i.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:i.managerDefaultWorkerModelId}:n,a=t.resolvePath(o.workDir),d=C(o.attachments);d?.length&&E(a,d);const u={text:o.text,attachments:d,localized:!1},p=t.sessions.get(o.sessionId);if(o.agentType==="codex"&&o.agentSessionId&&p?.currentRunId?.startsWith("dispatch:")&&H(o.agentSessionId)){t.client.sendRes({type:"res",id:e.id,ok:!1,error:V});return}const c=await this.isSessionBusy(o.sessionId),l=g(),I=l.sessions[o.sessionId]??[];if(!c&&!I.length){const k={...e,id:ee(e.id),method:"chat.send",params:{...o,text:u.text,responseId:e.id,clientMessageId:o.clientMessageId??o.queueMessageId,waitForDispatch:!0,attachments:u.attachments}};if(i){if(!this.opts.dispatchManagedReq)throw new Error("managed_agent_dispatch_unavailable");await this.opts.dispatchManagedReq(k,i)}else await this.opts.dispatchReq(k);return}const m=F({...o,text:u.text,attachments:u.attachments});i&&(m.managedAgentDirect=!0);const _=m.clientMessageId??m.id,T=w(o.sessionId,_);if(T.state!=="not_found"){t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:T.state==="queued",queueMessageId:m.id,...T,queue:this.getSnapshot(o.sessionId)}});return}const M=z(I,m);M||(l.sessions[o.sessionId]=[...I,m],y(l));const f=M??m;W({sessionId:f.sessionId,agentType:f.agentType,sessionMode:f.sessionMode,managerConfig:f.managerConfig,workDir:f.workDir,agentSessionId:f.agentSessionId,modelId:f.modelId}),$(f.sessionId,x(f),"queued"),b(t.resolvePath(f.workDir),f.attachments??[]),D(t.client),this.logLifecycle("enqueue.persisted",o.sessionId,{origin:o.origin??"app",queueMessageId:f.id,pendingCount:(g().sessions[o.sessionId]??[]).length}),this.broadcast(o.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:M?.id??m.id,...w(o.sessionId,M?.clientMessageId??M?.id??m.clientMessageId??m.id),queue:this.getSnapshot(o.sessionId),...u.localized?{localizedAttachments:!0}:{}}}),c||this.drainNext(o.sessionId)}async handleGet(e){const t=this.opts.getRuntime(),n=e.params;if(!n.sessionId){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(n.sessionId)}}),this.getSnapshot(n.sessionId).busy||this.drainNext(n.sessionId)}async handleEdit(e){const t=this.opts.getRuntime(),n=e.params;if(!n.sessionId||!n.queueMessageId||!n.text){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const i=g(),r=i.sessions[n.sessionId]??[],o=r.findIndex(u=>u.id===n.queueMessageId);if(o<0){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(r[o].managedRoom){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const a=C(n.attachments),d=t.resolvePath(r[o].workDir);a?.length&&E(d,a),r[o]={...r[o],text:n.text,attachments:a,updatedAt:B()},i.sessions[n.sessionId]=r,y(i),N(n.sessionId,x(r[o]),"queued"),r[o].attachments?.length&&b(d,r[o].attachments??[]),D(t.client),this.broadcast(n.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(n.sessionId)}})}async handleDelete(e){const t=this.opts.getRuntime(),n=e.params;if(!n.sessionId||!n.queueMessageId){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const i=g(),r=i.sessions[n.sessionId]??[],o=r.find(d=>d.id===n.queueMessageId);if(o?.managedRoom){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const a=r.filter(d=>d.id!==n.queueMessageId);if(a.length===r.length){t.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}a.length?i.sessions[n.sessionId]=a:delete i.sessions[n.sessionId],y(i),Q(n.sessionId,o?.clientMessageId??o?.id??n.queueMessageId),D(t.client),this.broadcast(n.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(n.sessionId)}})}noteTerminal(e){this.drainNext(e)}purgeSession(e){te(e),this.draining.delete(e)}discardPendingManagedRoomMessages(e){const t=g(),n=t.sessions[e]??[],i=this.inFlightMessageIds.get(e)??new Set,r=n.filter(a=>a.managedRoom&&!i.has(a.id));if(r.length===0)return 0;const o=n.filter(a=>!r.includes(a));o.length?t.sessions[e]=o:delete t.sessions[e],y(t);for(const a of r)Q(e,a.clientMessageId??a.id);return D(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:r.length,pendingCount:o.length}),this.broadcast(e),r.length}drainIdleQueues(){const e=g();for(const t of Object.keys(e.sessions))this.drainNext(t)}async drainNext(e){if(this.draining.has(e))return;this.draining.add(e);let t=!1,n=[];try{if(await this.isSessionBusy(e)){this.logLifecycle("drain.deferred",e,{pendingCount:g().sessions[e]?.length??0});return}const i=g(),r=i.sessions[e]??[],o=r.filter(u=>{const p=u.clientMessageId??u.id,c=w(e,p).state;return c==="queued"||c==="not_found"});o.length!==r.length&&(o.length?i.sessions[e]=o:delete i.sessions[e],y(i),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:r.length-o.length,pendingCount:o.length}));const a=ne(o);if(a.length===0){this.broadcast(e);return}n=a,this.inFlightMessageIds.set(e,new Set(a.map(u=>u.id))),this.logLifecycle("dispatch.start",e,{batchCount:a.length,queueMessageId:a[0].id,origin:a[0].origin??"app"});for(const u of a)N(e,x(u),"queued");await this.dispatchQueuedMessages(a);const d=this.removeBatch(e,a);this.logLifecycle("dispatch.accepted",e,{batchCount:a.length,queueMessageId:a[0].id,pendingCount:d}),t=d>0,this.broadcast(e)}catch(i){if(i instanceof Y){const r=this.removeBatch(e,n);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:n.length,queueMessageId:n[0]?.id,pendingCount:r,errorCode:U(i.message,"terminal_dispatch_failed")},"error"),t=r>0}else this.logLifecycle("dispatch.transient-failure",e,{errorCode:U(i instanceof Error?i.message:String(i),"transient_dispatch_failed")},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e),t&&queueMicrotask(()=>{this.drainNext(e)})}}removeBatch(e,t){if(t.length===0)return g().sessions[e]?.length??0;const n=g(),i=new Set(t.map(o=>o.id)),r=(n.sessions[e]??[]).filter(o=>!i.has(o.id));return r.length?n.sessions[e]=r:delete n.sessions[e],y(n),r.length}logLifecycle(e,t,n,i="info"){const r=`[chat.queue] ${JSON.stringify({event:e,sessionId:t,...n})}`;i==="error"?console.error(r):console.info(r)}async dispatchQueuedMessages(e){const t=e[0],n=e.flatMap(l=>l.managedRoom?[l.managedRoom]:[]),i=e.some(l=>l.managedAgentDirect),r=n.length>0?await this.prepareManagedRoomTurn?.(n):i?await this.prepareManagedAgentDirectTurn?.(t.sessionId):void 0;if(n.length>0&&!r)throw new Error("managed_room_turn_preparer_unavailable");if(i&&!r)throw new Error("managed_agent_context_unavailable");const o=r??null,a=o?.sessionMode==="manager"?o.managerConfig:null,d=e.length===1?t.text:se(e),u=oe(e.flatMap(l=>l.attachments??[])),p=i?r.agentSessionId:n.length>0?O(this.opts.getRuntime(),t):t.agentSessionId??null,c=r?v().find(l=>l.sessionId===t.sessionId):null;await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${t.id}-${Date.now()}`,method:"chat.send",params:{...S(R()),sessionId:t.sessionId,text:d,agentType:i?r.agentType:t.agentType,sessionMode:o?.sessionMode??t.sessionMode,managerConfig:o?a:t.managerConfig,workDir:i?r.workDir:t.workDir,agentSessionId:p,modelId:c?.modelId??t.modelId??void 0,systemPrompt:r?.systemPrompt??t.systemPrompt??null,managerDefaultWorkerAgentType:o?o.managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:o?o.managerDefaultWorkerModelId:t.managerDefaultWorkerModelId,reasoningEffort:t.reasoningEffort??void 0,clientMessageId:t.clientMessageId??t.id,attachments:u,externalChannel:t.externalChannel,replyTarget:t.replyTarget,roomContextRegistryKey:r?.roomContextRegistryKey,managedRoomContextToken:r?.managedRoomContextToken,suppressExternalOwnerEcho:n.length>0||void 0,queuedBatchItems:e.map(j),waitForDispatch:!0}},r??void 0)}async isSessionBusy(e){const t=this.opts.getRuntime().sessions.get(e);if(!t)return!1;if(t.currentRunId)return!0;if(!t.adapter.getStatus)return!1;const n=await t.adapter.getStatus().catch(()=>null);return n?.active?(n.runId&&(t.currentRunId=n.runId,t.currentRunPhase=n.runPhase??t.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}function O(s,e){if(s.sessions.has(e.sessionId))return null;const n=v().find(i=>i.sessionId===e.sessionId)?.agentSessionId?.trim();return!n||n.startsWith("managed:")?null:n}function te(s){const e=g();e.sessions[s]&&(delete e.sessions[s],y(e))}function z(s,e){const t=A(e);if(t)return s.find(i=>A(i)===t)??null;if(e.origin!=="external")return null;const n=J(e);return s.find(i=>J(i)===n)??null}function j(s){const{origin:e,managedRoom:t,managedAgentDirect:n,...i}=s;return i}function ne(s){const e=s[0];if(!e)return[];const t=K(e),n=[];for(const i of s){if(K(i)!==t)break;n.push(i)}return n}function K(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 se(s){return s.every(e=>e.managedRoom)?`[\u7FA4\u6D88\u606F]
|
|
3
|
+
${s.map(e=>ie(e.text)).filter(Boolean).join(`
|
|
4
4
|
|
|
5
5
|
`)}`:`[\u8FDE\u7EED\u7528\u6237\u6D88\u606F]
|
|
6
|
-
${s.map((e,
|
|
6
|
+
${s.map((e,t)=>`<\u6D88\u606F ${t+1}>
|
|
7
7
|
${e.text.trim()}`).join(`
|
|
8
8
|
|
|
9
|
-
`)}`}function
|
|
10
|
-
${
|
|
11
|
-
`)?e.slice(6):e}function
|
|
12
|
-
`)}function
|
|
9
|
+
`)}`}function ie(s){const e=s.trim(),t=e.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);return t?`<${t[1]}>
|
|
10
|
+
${t[2]}`:e.startsWith(`[\u7FA4\u6D88\u606F]
|
|
11
|
+
`)?e.slice(6):e}function J(s){const e=A(s);return e?`id:${e}`:["body",re(s.text),...(s.attachments??[]).map(t=>t.path||`${t.name}:${t.mimeType}`)].join(`
|
|
12
|
+
`)}function A(s){const e=typeof s.clientMessageId=="string"?s.clientMessageId.trim():"";if(e)return e;const t=typeof s.id=="string"?s.id.trim():"";return t&&!t.startsWith("queue-")?t:""}function oe(s){const e=new Set,t=[];for(const n of s){const i=n.path||`${n.name}:${n.mimeType}`;e.has(i)||(e.add(i),t.push(n))}return t.length?t:void 0}function re(s){return String(s||"").replace(/\s+/g," ").trim()}function U(s,e){const t=s.toLowerCase();return t.includes("no rollout found")?"missing_rollout":t.includes("managed_room_permission_snapshot_mismatch")?"managed_room_permission_snapshot_mismatch":t.includes("managed_room_prompt_snapshot_mismatch")?"managed_room_prompt_snapshot_mismatch":t.includes("managed_room_turn_preparer_unavailable")?"managed_room_turn_preparer_unavailable":t.includes("timeout")?"timeout":t.includes("connection")?"connection_error":e}export{Me as ChatQueueManager,Y as TerminalQueuedChatDispatchError,te as purgeQueuedSessionData};
|
|
@@ -300,6 +300,8 @@ export type ChatQueueSnapshot = PersonalSyncIdentity & {
|
|
|
300
300
|
export type ChatEnqueueParams = ChatSendParams & {
|
|
301
301
|
queueMessageId?: string;
|
|
302
302
|
origin?: 'app' | 'external';
|
|
303
|
+
/** Relay-attested. Clients must not set this field directly. */
|
|
304
|
+
managedAgentDirect?: true;
|
|
303
305
|
};
|
|
304
306
|
export type ChatQueueGetParams = PersonalSyncIdentity & {
|
|
305
307
|
sessionId: string;
|