shennian 0.4.64 → 0.4.66
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 +8 -8
- package/dist/src/agents/codex.d.ts +2 -0
- package/dist/src/agents/codex.js +10 -10
- package/dist/src/session/handlers/chat-recovery.js +1 -1
- package/dist/src/session/queue.js +1 -1
- package/dist/src/session/queued-chat-dispatch.js +1 -1
- package/package.json +1 -1
|
@@ -121,8 +121,8 @@
|
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
"file": "src/agents/codex.js",
|
|
124
|
-
"beforeBytes":
|
|
125
|
-
"afterBytes":
|
|
124
|
+
"beforeBytes": 68764,
|
|
125
|
+
"afterBytes": 35246
|
|
126
126
|
},
|
|
127
127
|
{
|
|
128
128
|
"file": "src/agents/command-spec.js",
|
|
@@ -801,8 +801,8 @@
|
|
|
801
801
|
},
|
|
802
802
|
{
|
|
803
803
|
"file": "src/session/handlers/chat-recovery.js",
|
|
804
|
-
"beforeBytes":
|
|
805
|
-
"afterBytes":
|
|
804
|
+
"beforeBytes": 6838,
|
|
805
|
+
"afterBytes": 3586
|
|
806
806
|
},
|
|
807
807
|
{
|
|
808
808
|
"file": "src/session/handlers/chat-session-lifecycle.js",
|
|
@@ -931,13 +931,13 @@
|
|
|
931
931
|
},
|
|
932
932
|
{
|
|
933
933
|
"file": "src/session/queue.js",
|
|
934
|
-
"beforeBytes":
|
|
935
|
-
"afterBytes":
|
|
934
|
+
"beforeBytes": 44697,
|
|
935
|
+
"afterBytes": 19471
|
|
936
936
|
},
|
|
937
937
|
{
|
|
938
938
|
"file": "src/session/queued-chat-dispatch.js",
|
|
939
|
-
"beforeBytes":
|
|
940
|
-
"afterBytes":
|
|
939
|
+
"beforeBytes": 2902,
|
|
940
|
+
"afterBytes": 1626
|
|
941
941
|
},
|
|
942
942
|
{
|
|
943
943
|
"file": "src/session/run-recovery/barrier.js",
|
|
@@ -54,6 +54,8 @@ export declare class CodexAdapter extends AgentAdapter {
|
|
|
54
54
|
private configuredManagedRoomContextToken;
|
|
55
55
|
private configuredManagedRoomExecutor;
|
|
56
56
|
private managedRoomToolThreadId;
|
|
57
|
+
private managedRoomCapabilityExpectedForTurn;
|
|
58
|
+
private managedRoomTurnBindingFailed;
|
|
57
59
|
private preparedExternalOwnerThreadId;
|
|
58
60
|
private managedTurnReplay;
|
|
59
61
|
private managedAsyncRecovery;
|
package/dist/src/agents/codex.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import{spawnSync as q}from"node:child_process";import{createInterface as
|
|
2
|
-
`)}function fe(o,e){const t=e?.filter(
|
|
3
|
-
`)}function
|
|
4
|
-
${l}`.trim();if(u!==0){m(new Error(f||`codex queue exited with code ${u??"unknown"}`));return}const b=f.match(/Queued message\s+([^\s]+)\s+for thread/i)?.[1];m(void 0,b)})})}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??W(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(A(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:Y,capabilities:{experimentalApi:!0}}),this.managedPermissionConfig=this.managedAgentPolicy&&!E(this.managedAgentPolicy)?await this.buildManagedPermissionConfig():null;const r=!!(this.configuredManagedRoomContextToken||this.configuredManagedRoomExecutor);if(this.managedAgentPolicy&&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.managedAgentPolicy&&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,...t?{model:t}:{}};let l;try{l=await this.sendRpc("thread/resume",{...c,excludeTurns:!0})}catch(p){if(!ce(p))throw p;l=await this.sendRpc("thread/resume",c)}this.agentSessionId=l.thread?.id??this.agentSessionId,this.managedPermissionConfig&&R(l,this.managedPermissionConfig.profileId),this.managedAgentPolicy&&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",...s?{}:{threadSource:"user"},experimentalRawEvents:!1,persistExtendedHistory:!0,...this.managedAgentPolicy&&r?{dynamicTools:this.extraEnv.SHENNIAN_MANAGER_SESSION_ID?H:J}:{},...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 le(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),this.managedAgentPolicy&&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?[]:Q(await this.sendRpc("config/read",{cwd:e.adapterPolicy.workDir}));return K(z(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=he(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 ue(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.managedAgentPolicy||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:[M(t,i)],approvalPolicy:"never",...this.managedPermissionConfig?{}:{sandboxPolicy:{type:"dangerFullAccess"}},...r?{model:r}:{},...s?{effort:s}:{}})}catch(n){throw s&&re(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
|
-
`,
|
|
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=ae(e);
|
|
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
|
|
1
|
+
import{spawnSync as q}from"node:child_process";import{createInterface as w}from"node:readline";import{createHash as S,randomUUID as N}from"node:crypto";import p from"node:path";import{AgentAdapter as F,registerAgent as D}from"./adapter.js";import{resolveBuiltinCommand as v,spawnAgentCommand as x}from"./command-spec.js";import{buildAgentProcessEnv as L,buildManagedAgentProcessEnv as $,buildManagedNativeFullProcessEnv as U}from"../agent-env.js";import{ensurePlatformInstructionsFile as W}from"./platform-instructions.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as E}from"@shennian/wire";import{isCodexThreadTailInterrupted as B}from"../native-fusion/codex-files.js";import{resolveManagedAgentAdapterEnforcement as G,isManagedAgentNativeFull as R}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as z}from"../security/managed-agent-runtime.js";import{CODEX_MANAGED_MANAGER_DYNAMIC_TOOLS as H,CODEX_MANAGED_ROOM_DYNAMIC_TOOLS as J,CodexManagedRoomToolBinding as V}from"./codex-managed-room-tools.js";import{assertCodexManagedPermissionProfile as C,CODEX_MANAGED_RUNTIME_SECURITY as X,compileCodexManagedPermissions as K,extractCodexMcpServerNames as Q}from"./codex-managed-permissions.js";import{CODEX_APP_SERVER_CLIENT_INFO as Y,extractAppServerErrorMessage as _,formatCodexErrorMessage as j,formatTransientCodexStatus as Z,isCodexCollabAgentItem as ee,isCodexCollabAgentToolName as y,isCodexLegacyCollabAgentItem as te,isCodexUnsupportedEffortError as re,isMissingCodexRolloutError as T,isTransientCodexErrorMessage as ne,looksLikeCodexInteractiveAuthPrompt as se,looksLikeFatalCodexStderr as ie,normalizeCodexModelId as I,normalizeCodexReasoningEffort as oe,normalizeCodexStderr as A,normalizeTerminalText as ae,safeStringify as k,stripGitDirectiveArtifacts as de}from"./codex-utils.js";import{isCodexUnsupportedEffortError as Fe,isMissingCodexRolloutError as De,normalizeCodexModelId as Le,normalizeCodexReasoningEffort as $e}from"./codex-utils.js";z("codex",X);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 le(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 ce(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 ue(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 he(o,e){const t=p.resolve(e);let r;for(const n of o)if(n.id)for(const i of n.roots??[]){if(!i.path)continue;const s=p.resolve(i.path),a=p.relative(s,t);!(a===""||!a.startsWith("..")&&!p.isAbsolute(a))||s.length<=(r?.rootLength??-1)||(r={id:n.id,rootLength:s.length})}return r?.id}function M(o,e){const t=e?.flatMap(n=>pe(n)).filter(n=>!/^https?:\/\//i.test(n.path)).filter(n=>n.path);return{type:"text",text:ge(o,e),text_elements:[],...t?.length?{local_images:t}:{}}}function pe(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=me(o.path);return e?[{path:e}]:[]}return[]}function me(o){if(!o||/^https?:\/\//i.test(o))return null;const e=p.extname(o),r=`${e?o.slice(0,-e.length):o}.preview.png`;return q("ffmpeg",["-y","-ss","0","-i",o,"-frames:v","1","-vf","scale=320:-1",r],{stdio:"ignore",timeout:15e3}).status===0?r:null}function ge(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 n=t.map((i,s)=>{const a=i.name||`attachment-${s+1}`,d=i.mimeType?` (${i.mimeType})`:"";return`[${b(a)}${d}](<${i.path}>)`});return[o,"",...n].join(`
|
|
2
|
+
`)}function fe(o,e){const t=e?.filter(n=>n.path&&!/^https?:\/\//i.test(n.path)&&!o.includes(n.path));if(!t?.length)return o;const r=t.map((n,i)=>{const s=b(n.name||`attachment-${i+1}`);return n.kind==="image"?``:`[${s}](<${n.path}>)`});return[o,"",...r].join(`
|
|
3
|
+
`)}function ye(o,e,t){return["queue","--thread",o,"--message",fe(e,t)]}function b(o){return o.replace(/[[\]\\]/g,"\\$&")}class Te extends F{options;get supportsActiveInput(){return!0}async injectActiveInput(e){const t=this.agentSessionId,r=this.activeTurnId;if(!t||!r||!this.process)throw new Error("codex_active_turn_unavailable");const n=await this.sendRpc("turn/steer",{threadId:t,expectedTurnId:r,input:[M(e.text,e.attachments)],...e.clientMessageId?{clientUserMessageId:e.clientMessageId}:{}});if(n.turnId&&n.turnId!==r)throw new Error("codex_active_turn_changed")}type="codex";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}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 V;configuredManagedRoomContextToken=null;configuredManagedRoomExecutor=null;managedRoomToolThreadId=null;managedRoomCapabilityExpectedForTurn=!1;managedRoomTurnBindingFailed=!1;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 S("sha256").update(JSON.stringify({sessionId:this.shennianSessionId,externalChannel:this.externalChannel,env:e,managedAgentPolicy:this.managedAgentPolicy})).digest("hex")}buildProcessEnv(e={}){if(!this.managedAgentPolicy)return L({...e,...this.extraEnv});if(R(this.managedAgentPolicy))return U({...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 $({agentType:"codex",providerEnv:t,sessionEnv:t,runtimeEnv:e})}async start(e,t,r,n){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,this.seq=0,this.persistNativeSession=n?.persistNativeSession!==!1,this.nativeSessionVisibility=n?.nativeSessionVisibility??"user",r&&(this.agentSessionId=r)}async prepareSend(e,t,r,n,i,s){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(s?.source==="room_activation")throw new Error("codex_managed_thread_owned_externally",{cause:d});if(this.isThreadTailInterrupted(this.agentSessionId))throw new Error(E,{cause:d});this.preparedExternalOwnerThreadId=this.agentSessionId}}}async send(e,t,r,n,i,s){if(s?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");if(s?.source==="room_activation"&&(!this.configuredManagedRoomContextToken||!this.configuredManagedRoomExecutor))throw new Error("codex_managed_room_tool_context_unavailable");const a=s?.source==="room_activation";this.managedRoomCapabilityExpectedForTurn=!1,this.activeTurnId&&(await this.interruptActiveTurn().catch(()=>{}),await this.killProcess(!0)),this.configurationRestartRequired&&this.process&&await this.killProcess(!0),this.configurationRestartRequired=!1;const d=I(t),l=oe(r),c=i?.trim()??"";if(c!==this.agentSystemPrompt&&(this.agentSystemPrompt=c,this.process&&await this.killProcess(!0)),s?.source==="room_activation"&&this.managedRoomTools.configure(this.configuredManagedRoomContextToken,this.configuredManagedRoomExecutor),this.runId=N(),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,n)};this.managedRoomCapabilityExpectedForTurn=a;try{await this.ensureAppServer(d)}catch(u){if(!this.agentSessionId||!P(u))throw u;if(s?.source==="room_activation")throw new Error("codex_managed_thread_owned_externally",{cause:u});if(this.isThreadTailInterrupted(this.agentSessionId))throw new Error(E,{cause:u});return{deliveryMode:"external_owner_queue",queuedSubmissionId:await this.enqueueForExternalOwner(this.agentSessionId,e,n)}}const h=this.agentSessionId;if(!h)throw new Error("codex app-server did not create a thread");this.emitEvent({state:"start",agentSessionId:h}),this.managedTurnReplay=this.managedPermissionConfig?{text:e,modelId:d,reasoningEffort:l,attachments:n,recoveryCount:0}:null;const m=await this.startTurnWithRecovery(h,e,d,l,n),g=this.agentSessionId??h;if(this.activeTurnId=m.turn?.id??null,this.managedRoomTurnBindingFailed)throw await this.killProcess(),new Error("codex_managed_room_turn_binding_failed");if(this.managedRoomCapabilityExpectedForTurn&&this.managedRoomToolThreadId===g&&this.activeTurnId&&!this.managedRoomTools.bindTurn(g,this.activeTurnId))throw await this.killProcess(),new Error("codex_managed_room_turn_binding_failed");return{deliveryMode:"direct"}}isThreadTailInterrupted(e){return this.options.isThreadTailInterrupted?.(e)??B(e)}async enqueueForExternalOwner(e,t,r){const n=v("codex");if(!n)throw new Error('Command "codex" not found. Is OpenAI Codex CLI installed?');const i=ye(e,t,r),s=x(n,i,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});return await new Promise((a,d)=>{let l="",c="",h=!1;const m=(u,f)=>{h||(h=!0,clearTimeout(g),u?d(u):a(f))},g=setTimeout(()=>{s.kill("SIGTERM"),m(new Error("Codex Desktop message queue timed out"))},6e4);g.unref?.(),s.stdout?.on("data",u=>{l+=u.toString()}),s.stderr?.on("data",u=>{c+=u.toString()}),s.once("error",u=>m(u)),s.once("close",u=>{const f=`${l}
|
|
4
|
+
${c}`.trim();if(u!==0){m(new Error(f||`codex queue exited with code ${u??"unknown"}`));return}const O=f.match(/Queued message\s+([^\s]+)\s+for thread/i)?.[1];m(void 0,O)})})}async resume(e){await this.killProcess(),this.agentSessionId=e,this.resetTextState()}async setTitle(e,t,r){const n=e.trim(),i=t.replace(/\s+/g," ").trim();!n||!i||(this.agentSessionId=n,r?.trim()&&(this.workDir=r),await this.ensureAppServer(),await this.sendRpc("thread/name/set",{threadId:n,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 n=r?.type==="active"||!!t;return{active:n,runId:t,runPhase:n?this.mapThreadStatusToRunPhase(r):null,canStop:!!(n&&t&&this.process)}}spawnCodex(e){const t=v("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,w({input:r.stdout}).on("line",s=>{if(s.trim())try{const a=JSON.parse(s);this.handleStreamEvent(a)}catch{this.emitEvent({state:"delta",text:s})}});let i="";r.stderr?.on("data",s=>{i+=s.toString()}),r.on("close",s=>{this.process===r&&(this.process=null,this.clearForceCloseTimer(),this.handleProcessClose(s,i))}),r.on("error",s=>{this.process===r&&(this.process=null,s.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",s))})}spawnAppServer(){const e=v("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??W(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,this.agentSystemPrompt);r&&t.push("-c",`model_instructions_file=${JSON.stringify(r)}`);const n=x(e,t,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});this.process=n,this.stderrBuf="",this.appServerStartupError=null,w({input:n.stdout}).on("line",s=>{if(!s.trim())return;let a;try{a=JSON.parse(s)}catch{this.handleAppServerNonJsonStdout(s);return}this.handleAppServerMessage(a)}),n.stderr?.on("data",s=>{this.stderrBuf+=s.toString()}),n.on("close",s=>{if(this.process!==n)return;const a=this.activeTurnId!=null;this.process=null,this.appServerReady=null,this.activeTurnId=null,this.managedRoomTools.reset(),this.clearConfiguredManagedRoomContext(),this.rejectAllPending(new Error(A(this.stderrBuf)||`codex app-server exited with code ${s}`)),this.terminalState==="open"&&a&&this.handleProcessClose(s,this.stderrBuf)}),n.on("error",s=>{this.process===n&&(this.process=null,this.managedRoomTools.reset(),this.clearConfiguredManagedRoomContext(),this.rejectAllPending(s),s.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",s))})}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:Y,capabilities:{experimentalApi:!0}}),this.managedPermissionConfig=this.managedAgentPolicy&&!R(this.managedAgentPolicy)?await this.buildManagedPermissionConfig():null;const r=!!(this.configuredManagedRoomContextToken||this.configuredManagedRoomExecutor);if(this.managedAgentPolicy&&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.managedAgentPolicy&&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 l={threadId:this.agentSessionId,cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},persistExtendedHistory:!0,...t?{model:t}:{}};let c;try{c=await this.sendRpc("thread/resume",{...l,excludeTurns:!0})}catch(h){if(!ce(h))throw h;c=await this.sendRpc("thread/resume",l)}this.agentSessionId=c.thread?.id??this.agentSessionId,this.managedPermissionConfig&&C(c,this.managedPermissionConfig.profileId),this.managedAgentPolicy&&r&&(this.managedRoomTools.bindThread(this.agentSessionId),this.managedRoomToolThreadId=this.agentSessionId);return}const n=this.options.hidden||this.nativeSessionVisibility==="internal"||!this.persistNativeSession,i=n?void 0:await this.resolveCodexProjectId(),s={cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},serviceName:"Shennian",...n?{}:{threadSource:"user"},experimentalRawEvents:!1,persistExtendedHistory:!0,...this.managedAgentPolicy&&r?{dynamicTools:this.extraEnv.SHENNIAN_MANAGER_SESSION_ID?H:J}:{},...i?{projectId:i}:{},...t?{model:t}:{}};let a;if(n&&!this.managedPermissionConfig)a=await this.sendRpc("thread/start",s);else try{a=await this.sendRpc("thread/start",{...s,historyMode:"paginated"})}catch(l){throw le(l)?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:l}):l}const d=a.thread?.id;if(!d)throw new Error("codex app-server thread/start returned no thread id");this.managedPermissionConfig&&C(a,this.managedPermissionConfig.profileId),this.managedAgentPolicy&&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?[]:Q(await this.sendRpc("config/read",{cwd:e.adapterPolicy.workDir}));return K(G(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 l=await this.sendRpc("project/list",{limit:100,...r?{cursor:r}:{}});t.push(...l.data??[]),r=l.nextCursor}while(r);const n=he(t,e);if(n)return n;const i=p.resolve(e),s=`shennian-project-${S("sha256").update(i).digest("hex")}`,d=(await this.sendRpc("project/create",{idempotencyKey:s,name:p.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 ue(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,n,i){try{return await this.startTurn(e,t,r,n,i)}catch(s){if(!T(s))throw s;if(this.managedAgentPolicy||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:s});return this.logManagedThreadLifecycle("thread.recreated",{previousThreadId:e,threadId:a}),this.emitEvent({state:"start",agentSessionId:a}),await this.startFreshManagedTurnWithReadinessRetry(a,t,r,n,i)}return await this.killProcess(),await this.ensureAppServer(r),await this.startTurn(e,t,r,n,i)}}async startFreshManagedTurnWithReadinessRetry(e,t,r,n,i){const s=[0,100,300,1e3];let a;for(let d=0;d<s.length;d+=1){const l=s[d];l>0&&await new Promise(c=>setTimeout(c,l));try{const c=await this.startTurn(e,t,r,n,i);return d>0&&this.logManagedThreadLifecycle("rollout.ready",{threadId:e,attempt:d+1}),c}catch(c){if(a=c,!T(c)||d===s.length-1)throw c;this.logManagedThreadLifecycle("rollout.retry",{threadId:e,attempt:d+2,delayMs:s[d+1]})}}throw a}logManagedThreadLifecycle(e,t){console.error(`[codex.managed] ${JSON.stringify({event:e,...t})}`)}async startTurn(e,t,r,n,i){try{return await this.sendRpc("turn/start",{threadId:e,input:[M(t,i)],approvalPolicy:"never",...this.managedPermissionConfig?{}:{sandboxPolicy:{type:"dangerFullAccess"}},...r?{model:r}:{},...n?{effort:n}:{}})}catch(s){throw n&&re(s)?new Error(`Codex app-server does not accept reasoning effort "${n}" for this turn. Refresh models or upgrade Codex CLI, then retry.`,{cause:s}):s}}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 n=this.process;if(!n?.stdin)return Promise.reject(new Error("codex app-server is not running"));const i=this.rpcSeq++,s=JSON.stringify({id:i,method:e,params:t});return new Promise((a,d)=>{const l=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:l}),n.stdin.write(`${s}
|
|
5
|
+
`,c=>{c&&(clearTimeout(l),this.pendingRequests.delete(i),d(c))})})}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,n=typeof r?.id=="string"?r.id:null,i=typeof t.threadId=="string"?t.threadId:"";n&&(this.activeTurnId=n,this.managedRoomCapabilityExpectedForTurn&&this.managedRoomToolThreadId===i&&!this.managedRoomTools.bindTurn(i,n)&&(this.managedRoomTurnBindingFailed=!0,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 n=typeof t.status=="object"&&t.status!==null?t.status:null;n?.type==="active"&&this.emitEvent({state:"heartbeat",runPhase:this.mapThreadStatusToRunPhase(n)});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,n=typeof r?.id=="string"?r.id:null;(!this.activeTurnId||n===this.activeTurnId)&&(this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),this.emitFinalIfOpen());break}case"turn/failed":{const r=_(t);if(this.scheduleManagedAsyncRolloutRecovery(r))return;this.emitErrorIfOpen(r||"turn failed");break}case"error":{const r=_(t);if(ne(r)){this.emitTransientStatus(r);return}if(this.scheduleManagedAsyncRolloutRecovery(r))return;this.emitErrorIfOpen(r);break}}}scheduleManagedAsyncRolloutRecovery(e){if(!this.managedAgentPolicy&&!this.managedPermissionConfig||!this.managedTurnReplay||!T(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 n=T(r)?"no rollout found after managed thread recovery":r instanceof Error?r.message:"managed thread recovery failed";this.emitErrorIfOpen(n)}).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 n=await this.startFreshManagedTurnWithReadinessRetry(r,e.text,e.modelId,e.reasoningEffort,e.attachments);if(this.activeTurnId=n.turn?.id??null,this.managedRoomTurnBindingFailed)throw new Error("codex_managed_room_turn_binding_failed");if(this.managedRoomCapabilityExpectedForTurn&&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 n=typeof t.arguments=="object"&&t.arguments!==null?t.arguments:null,i=typeof n?.text=="string"?n.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
|
+
`)}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=ae(e);se(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")&&!ee(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:"",n=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:[n,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(y(r))return;const n=typeof t.arguments=="object"&&t.arguments!==null?t.arguments:{};this.emitEvent({state:"tool-call",name:r,args:n}),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,n=typeof t?.total=="object"&&t.total!==null?t.total:null,i=r??n;if(!i)return;const s=typeof i.inputTokens=="number"?i.inputTokens:void 0,a=typeof i.outputTokens=="number"?i.outputTokens:void 0;(s!=null||a!=null)&&(this.pendingUsage={inputTokens:s??0,outputTokens:a??0})}handleCodexItemLine(e){const t=e.item;if(t?.type&&!te(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&&y(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(n=>`- [${n.completed?"x":" "}] ${n.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 n=e.usage;if(n){const i=n.input_tokens??n.prompt_tokens,s=n.output_tokens??n.completion_tokens;(i!=null||s!=null)&&(this.pendingUsage={inputTokens:i??0,outputTokens:s??0})}this.emitFinalIfOpen();return}case"turn.failed":{const n=typeof e.error=="object"&&e.error?.message?e.error.message:typeof e.error=="string"?e.error:"turn failed";this.emitErrorIfOpen(n);return}case"error":{const n=typeof e.message=="string"?e.message:"";if(n.includes("Reconnecting"))return;this.emitErrorIfOpen(n||(typeof e.error=="string"?e.error:"stream error"));return}case"thread.started":{const n=e.thread_id??e.session_id??e.id;typeof n=="string"&&n&&(this.agentSessionId=n,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 n=e.content??e.text;n&&this.emitText(n);break}case"function_call":case"tool_call":{if(this.isApprovalLikeName(e.name)){this.emitApprovalPending(e);break}if(e.name&&y(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&&y(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",n=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:n,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=A(t);if(e!==0&&e!==null){const n=r||`codex exited with code ${e}`;this.emitErrorIfOpen(n)}else ie(r)?this.emitErrorIfOpen(r):this.emitFinalIfOpen();this.pendingUsage=void 0}emitFinalIfOpen(){this.terminalState==="open"&&(this.terminalState="final",this.managedTurnReplay=null,this.managedRoomCapabilityExpectedForTurn=!1,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.managedRoomCapabilityExpectedForTurn=!1,this.managedRoomTools.deactivateTurn(),this.clearConfiguredManagedRoomContext(),this.emitEvent({state:"error",message:j(e)}),this.scheduleProcessClose())}resetRunState(){this.pendingUsage=void 0,this.terminalState="open",this.managedRoomTurnBindingFailed=!1,this.deltaItemIds.clear(),this.lastTransientStatus="",this.clearForceCloseTimer(),this.resetTextState()}clearConfiguredManagedRoomContext(){this.configuredManagedRoomContextToken=null,this.configuredManagedRoomExecutor=null}emitTransientStatus(e){const t=Z(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=de(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)}}
|
|
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)}}D("codex",()=>new Te);export{Te as CodexAdapter,ye as buildCodexQueueArgs,fe as buildCodexQueuedText,P as isCodexActiveWriterError,ce as isCodexExcludeTurnsUnsupportedError,le as isCodexPaginatedHistoryUnsupportedError,ue as isCodexProjectApiUnsupportedError,Fe as isCodexUnsupportedEffortError,De as isMissingCodexRolloutError,Le as normalizeCodexModelId,$e as normalizeCodexReasoningEffort,he as selectCodexProjectIdForWorkDir};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash as g}from"node:crypto";import{buildManagedAgentPermissionPrompt as d}from"../../room/managed-prompt-composer.js";import{configureAdapterForSession as m,replaceManagedRoomContextToken as p}from"./chat-adapter-config.js";import{createActiveSession as y}from"./chat-session-lifecycle.js";import{validateManagedRoomExecutionContext as
|
|
1
|
+
import{createHash as g}from"node:crypto";import{buildManagedAgentPermissionPrompt as d}from"../../room/managed-prompt-composer.js";import{configureAdapterForSession as m,replaceManagedRoomContextToken as p}from"./chat-adapter-config.js";import{createActiveSession as y}from"./chat-session-lifecycle.js";import{validateManagedRoomExecutionContext as c}from"./chat.js";async function h(o,e,u=()=>{}){let a=null,s=null,r=null,i;if(e.source==="managed_room"||e.source==="managed_direct"||e.source==="manager_event"&&e.managedPolicyVersion){const n=await o.prepareManagedRecovery?.(e);if(!n)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"managed recovery requires a fresh permission context"};if(n.agentType!==e.agentType||n.sessionMode!==e.sessionMode||o.resolvePath(n.workDir)!==o.resolvePath(e.workDir)||n.managedRoomPromptSnapshot.managedRoomPolicyVersion!==e.managedPolicyVersion||n.managedRoomPromptSnapshot.runtimePermissionPolicyHash!==e.managedRuntimePolicyHash)return o.revokeManagedRoomContextToken?.(n.managedRoomContextToken),{result:"recovery_failed",reason:"permission_context_unavailable",message:"managed recovery authority no longer matches the interrupted run"};if(a=n.managedAgentPolicy,e.source==="manager_event"||e.source==="managed_direct")o.revokeManagedRoomContextToken?.(n.managedRoomContextToken),i={source:"upgrade_recovery",recovery:"upgrade"};else{const l={source:"room_activation",managedAgentPolicy:n.managedAgentPolicy,managedRoomPromptSnapshot:n.managedRoomPromptSnapshot};c(l,n.systemPrompt,n.managedRoomContextToken),s=n.managedRoomContextToken,r=n.systemPrompt,i={...l,recovery:"upgrade"}}}else if(e.source==="manager_worker"&&!e.managerWorkerNativeFull){const n=e.parentSessionId;if(a=n?o.managerRuntime?.getManagedAgentPolicyForWorker(n,e.workDir)??null:null,!a)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker recovery requires the current parent policy"};if(g("sha256").update(JSON.stringify({requestedPolicy:a.requestedPolicy,enforcement:a.enforcement})).digest("hex")!==e.managedRuntimePolicyHash)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker policy changed after interruption"};r=d({requestedPolicy:a.requestedPolicy,enforcement:a.enforcement,resolvedWorkDir:a.adapterPolicy.workDir})}i??={source:"upgrade_recovery",recovery:"upgrade"};let t=o.sessions.get(e.sessionId);return t||(t=await y(o,e.sessionId,e.agentType,e.sessionMode,e.workDir,e.agentSessionId,null,null,a,e.nativeSessionVisibility)),a&&(e.source==="manager_event"||e.source==="managed_direct")?m(t.adapter,e.sessionId,e.agentType,null,null,null,a,null):s&&(p(o,t,s),m(t.adapter,e.sessionId,e.agentType,null,null,s,a,o.managedRoomExecutor)),t.journalRunId=e.runId,t.currentRunId=e.providerRunId??`recovery:${e.runId}`,t.currentRunPhase="thinking",e.agentType==="codex"&&e.deliveryMode==="external_owner_queue"&&e.agentSessionId&&e.externalRunId&&o.nativeFusion?.restoreExternalObservation({sessionId:e.sessionId,sourceSessionKey:e.agentSessionId,runId:e.externalRunId,canonicalMessageId:e.clientMessageId,createdAt:Date.parse(e.createdAt)})?{result:"reattached"}:t.adapter.recoverInterruptedRun({sessionId:e.sessionId,shennianRunId:e.runId,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,reasoningEffort:e.reasoningEffort,providerRunId:e.providerRunId,lastObservedEventSeq:e.lastObservedEventSeq,continueDispatchState:e.continueDispatchState,systemPrompt:r,sendContext:i,beforeContinueDispatch:u})}export{h as recoverJournaledRun};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{NativeSourceDeletionStore as G}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as H}from"./queue-retry.js";import{findDuplicateQueuedMessage as E,stripLocalQueueFields as D,takeCompatibleBatch as U,formatQueuedBatch as P,dedupeQueuedAttachments as B,classifyQueueError as Q,isControllableQueueMessage as T,takeControllableMessage as V,normalizeQueuedModelId as F,resolveManagedRoomNativeSessionId as q}from"./queue-helpers.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as X}from"@shennian/wire";import{isCodexThreadTailInterrupted as Y}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as Z}from"./projection.js";import{assertFinishedAttachmentReferences as b,markAttachmentReferences as L}from"./attachment-transfer-store.js";import{appendMessage as W,getMessageDeliveryStatus as _,listSessionRecords as j,recordSession as N,tombstoneMessage as z,upsertMessage as C}from"./store.js";import{requestSessionIndexPublish as w}from"./index-publisher.js";import{purgeQueuedSessionData as ee,readQueue as c,writeQueue as S}from"./queue-storage.js";import{getNativeSourceAgentType as te}from"./handlers/chat-events.js";import{parsePersonalAttachmentReferences as $}from"./attachment-reference.js";import{getPersonalSyncIdentity as x}from"../personal-sync/epoch.js";import{getDaemonInstallationId as A}from"../personal-sync/identity.js";import{TerminalQueuedChatDispatchError as v,nowIso as O,queueEnvelope as R,queueMessageFromParams as K}from"./queue-message.js";import{purgeQueuedSessionData as Se}from"./queue-storage.js";import{TerminalQueuedChatDispatchError as Re}from"./queue-message.js";class ye{opts;validateScheduledTarget;async enqueueScheduled(e){return this.handleEnqueue(e,!0)}draining=new Set;promoting=new Set;disposed=!1;retries=new H(e=>{this.wakeSession(e)});deletions=new G;inFlightMessageIds=new Map;deferredLogState=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}stop(){this.disposed=!0,this.retries.stop()}getSnapshot(e){const s=c().sessions[e]??[],t=this.inFlightMessageIds.get(e);return{...x(A()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(D),controllablePending:s.filter(r=>T(r)&&!t?.has(r.id)).map(D)}}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const s=this.opts.getRuntime(),t=c(),r=new Set,i=new Map;let a=!1;for(const n of e){const d=n.params;if(this.deletions.hasSession(d.sessionId)){delete t.sessions[d.sessionId],a=!0;continue}if(!d.sessionId||!d.text||!d.agentType||!d.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=K({...x(A()),...d});u.origin="room",u.managedRoom=n.context,u.trigger={kind:"room_activation",...n.context};const l=t.sessions[d.sessionId]??[],m=E(l,u),I=u.clientMessageId??u.id,f=_(d.sessionId,I);if(!m&&f.state==="not_found")l.push(u),t.sessions[d.sessionId]=l,i.set(`${u.sessionId}\0${I}`,u),r.add(d.sessionId),a=!0;else if(m&&f.state==="not_found")i.set(`${m.sessionId}\0${I}`,m),r.add(d.sessionId);else if(m&&f.state==="queued")r.add(d.sessionId);else if(m){const y=l.filter(h=>h.id!==m.id);y.length?t.sessions[d.sessionId]=y:delete t.sessions[d.sessionId],a=!0}}a&&S(t);let o;for(const n of r)try{for(const d of i.values())d.sessionId===n&&(W(n,R(d),"queued"),N({sessionId:n,agentType:d.agentType,sessionMode:d.sessionMode,managerConfig:d.managerConfig,workDir:d.workDir,agentSessionId:q(s,d),modelId:d.modelId}));this.logLifecycle("enqueue.persisted",n,{origin:"room",pendingCount:c().sessions[n]?.length??0}),this.broadcast(n),this.wakeSession(n)}catch(d){o=d,this.logLifecycle("enqueue.commit-failed",n,{failureClass:Q(d instanceof Error?d.message:"","canonical_commit_failed")},"error"),this.retries.schedule(n,!0)}if(r.size&&w(s.client),o)throw o}async handleEnqueue(e,s=!1){const t=this.opts.getRuntime(),r=e.params;if(r.scheduledTask&&!s)throw new Error("schedule_source_untrusted");const i=!!(s&&r.scheduledTask?.bindingId);if(Z(r.sessionListProjection),!r.sessionId||!r.text||!r.agentType||!r.workDir)return t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const a=r.managedAgentDirect||i?await this.prepareManagedAgentDirectTurn?.(r.sessionId):null;if((r.managedAgentDirect||i)&&!a)throw new Error("managed_agent_context_unavailable");const o=a?j().find(J=>J.sessionId===r.sessionId):null,n=a?{...r,...a,agentType:a.agentType,sessionMode:a.sessionMode,managerConfig:a.managerConfig,workDir:a.workDir,agentSessionId:a.agentSessionId?.startsWith("managed:")?o?.agentSessionId&&!o.agentSessionId.startsWith("managed:")?o.agentSessionId:null:a.agentSessionId,modelId:o?.modelId??void 0,systemPrompt:a.systemPrompt,managerDefaultWorkerAgentType:a.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:a.managerDefaultWorkerModelId}:r,d=t.resolvePath(n.workDir),u=$(n.attachments);u?.length&&b(d,u);const l={text:n.text,attachments:u,localized:!1},m=t.sessions.get(n.sessionId);if(n.agentType==="codex"&&n.agentSessionId&&m?.currentRunId?.startsWith("dispatch:")&&Y(n.agentSessionId))return t.client.sendRes({type:"res",id:e.id,ok:!1,error:X}),!1;a?.managedRoomContextToken&&t.revokeManagedRoomContextToken?.(a.managedRoomContextToken);const I=await this.isSessionBusy(n.sessionId),f=c(),y=f.sessions[n.sessionId]??[],h=K({...n,text:l.text,attachments:l.attachments,systemPrompt:a?null:n.systemPrompt});a&&(h.managedAgentDirect=!0,h.trigger?.kind!=="manager_event"&&h.trigger?.kind!=="scheduled"&&(h.trigger={kind:"managed_direct"}));const g=h.clientMessageId??h.id,k=_(n.sessionId,g);if(k.state!=="not_found")return t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:k.state==="queued",queueMessageId:h.id,...k,queue:this.getSnapshot(n.sessionId)}}),!0;const M=E(y,h);M||(f.sessions[n.sessionId]=[...y,h],S(f));const p=M??h;return N({sessionId:p.sessionId,agentType:p.agentType,sessionMode:p.sessionMode,managerConfig:p.managerConfig,workDir:p.workDir,agentSessionId:p.agentSessionId,modelId:p.modelId}),W(p.sessionId,R(p),"queued"),L(t.resolvePath(p.workDir),p.attachments??[]),w(t.client),this.logLifecycle("enqueue.persisted",n.sessionId,{origin:n.origin??"app",queueMessageId:p.id,pendingCount:(c().sessions[n.sessionId]??[]).length}),this.broadcast(n.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:M?.id??h.id,..._(n.sessionId,M?.clientMessageId??M?.id??h.clientMessageId??h.id),queue:this.getSnapshot(n.sessionId),...l.localized?{localizedAttachments:!0}:{}}}),I?this.retries.schedule(n.sessionId):this.wakeSession(n.sessionId),!0}async handleGet(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleEdit(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const r=c(),i=r.sessions[t.sessionId]??[],a=i.findIndex(d=>d.id===t.queueMessageId);if(a<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!T(i[a])||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId)){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=$(t.attachments),n=s.resolvePath(i[a].workDir);o?.length&&b(n,o),i[a]={...i[a],text:t.text,attachments:o,updatedAt:O()},r.sessions[t.sessionId]=i,S(r),C(t.sessionId,R(i[a]),"queued"),i[a].attachments?.length&&L(n,i[a].attachments??[]),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const r=c(),i=r.sessions[t.sessionId]??[],a=i.find(n=>n.id===t.queueMessageId);if(a&&(!T(a)||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId))){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=i.filter(n=>n.id!==t.queueMessageId);if(o.length===i.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}o.length?r.sessions[t.sessionId]=o:delete r.sessions[t.sessionId],S(r),z(t.sessionId,a?.clientMessageId??a?.id??t.queueMessageId),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handlePromote(e){const s=this.opts.getRuntime(),t=e.params,r=a=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:a});if(!t.sessionId||!t.queueMessageId){r("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){r("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let i=[];try{for(;this.draining.has(t.sessionId);)await new Promise(n=>setTimeout(n,5));if(i=V(c().sessions[t.sessionId]??[],t.queueMessageId),!i.length){r("Queued message not found");return}this.inFlightMessageIds.set(t.sessionId,new Set(i.map(n=>n.id)));const a=s.sessions.get(t.sessionId);let o="idle";if(a?.currentRunId)if(this.canInjectActiveBatch(a,i)){const n=i.length===1?i[0].text:P(i),d=i[0].clientMessageId??i[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:i[0].agentType,sourceAgentType:te(i[0].agentType,i[0].modelId),canonicalMessageId:d,sourceSessionKey:a.agentSessionId,text:n,managedEchoPolicy:"suppress_following"});const u=Date.now();try{await a.adapter.injectActiveInput({text:n,attachments:B(i.flatMap(l=>l.attachments??[])),clientMessageId:d})}catch(l){throw s.nativeFusion?.cancelManagedSend(d),l}for(const l of i)C(t.sessionId,{...R(l),ts:u},"executing");this.removeBatch(t.sessionId,i),o="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId),await this.dispatchQueuedMessages(i),this.removeBatch(t.sessionId,i),o="restarted"}else await this.dispatchQueuedMessages(i),this.removeBatch(t.sessionId,i);w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId),promotedMessageIds:i.map(n=>n.id),mode:o}})}catch(a){r(a instanceof Error?a.message:"queue_promotion_failed"),this.broadcast(t.sessionId)}finally{this.inFlightMessageIds.delete(t.sessionId),this.promoting.delete(t.sessionId),c().sessions[t.sessionId]?.length&&this.wakeSession(t.sessionId)}}noteTerminal(e){this.wakeSession(e)}purgeSession(e){ee(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const o=c().sessions[e]?.length??0;return this.purgeSession(e),o}const s=c(),t=s.sessions[e]??[],r=this.inFlightMessageIds.get(e)??new Set,i=t.filter(o=>o.managedRoom&&!r.has(o.id));if(i.length===0)return 0;const a=t.filter(o=>!i.includes(o));a.length?s.sessions[e]=a:delete s.sessions[e],S(s);for(const o of i)z(e,o.clientMessageId??o.id);return w(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:i.length,pendingCount:a.length}),this.broadcast(e),i.length}wakeSession(e){this.drainNext(e).catch(()=>{this.logLifecycle("drain.retry-required",e,{},"error"),this.retries.schedule(e,!0)})}drainIdleQueues(){if(this.disposed)return;const e=c();for(const s of Object.keys(e.sessions))this.wakeSession(s)}async drainNext(e){if(this.disposed||this.draining.has(e)||this.promoting.has(e))return;this.draining.add(e);let s=!1,t=!1,r=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const u=c().sessions[e]?.length??0,l=this.deferredLogState.get(e),m=Date.now();(!l||l.pendingCount!==u||m-l.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:u}),this.deferredLogState.set(e,{at:m,pendingCount:u}));return}this.deferredLogState.delete(e);const i=c(),a=i.sessions[e]??[],o=a.filter(u=>{const l=u.clientMessageId??u.id,m=_(e,l).state;return m==="queued"||m==="not_found"});o.length!==a.length&&(o.length?i.sessions[e]=o:delete i.sessions[e],S(i),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:a.length-o.length,pendingCount:o.length}));const n=U(o);if(n.length===0){this.broadcast(e);return}r=n,this.inFlightMessageIds.set(e,new Set(n.map(u=>u.id))),this.logLifecycle("dispatch.start",e,{batchCount:n.length,queueMessageId:n[0].id,origin:n[0].origin??"app"});for(const u of n)C(e,R(u),"queued");await this.dispatchQueuedMessages(n);const d=this.removeBatch(e,n);this.logLifecycle("dispatch.accepted",e,{batchCount:n.length,queueMessageId:n[0].id,pendingCount:d}),this.deferredLogState.delete(e),s=d>0,this.broadcast(e)}catch(i){const a=Q(i instanceof Error?i.message:String(i),i instanceof v?"terminal_dispatch_failed":"transient_dispatch_failed"),o=r[0],n=!!(o&&!o.trigger&&!o.clientMessageId&&Date.now()-Date.parse(o.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(i instanceof v||a==="managed_claim_source_binding_conflict"||n){const u=this.removeBatch(e,r);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:r.length,queueMessageId:r[0]?.id,pendingCount:u,failureClass:n?"stale_legacy_managed_worker_dispatch_failure":a},"error"),s=u>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:a},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e),c().sessions[e]?.length?s?queueMicrotask(()=>this.wakeSession(e)):this.retries.schedule(e,t):this.retries.cancel(e)}}canInjectActiveBatch(e,s){if(!e.adapter.supportsActiveInput||!e.adapter.injectActiveInput||!e.journalRunId)return!1;const t=this.opts.getRuntime().runJournal?.get(s[0].sessionId,e.journalRunId),r=s[0].trigger?.kind==="managed_direct"||s[0].managedAgentDirect?null:"personal";return!!(r&&s[0].systemPrompt==null&&t&&t.source===r&&t.deliveryMode==="direct"&&t.agentType===s[0].agentType&&t.workDir===e.workDir&&t.agentSessionId===e.agentSessionId&&t.modelId===(s[0].modelId??null)&&t.reasoningEffort===(s[0].reasoningEffort??null))}removeBatch(e,s){if(s.length===0)return c().sessions[e]?.length??0;const t=c(),r=new Set(s.map(a=>a.id)),i=(t.sessions[e]??[]).filter(a=>!r.has(a.id));return i.length?t.sessions[e]=i:delete t.sessions[e],S(t),i.length}logLifecycle(e,s,t,r="info"){const i=`[chat.queue] ${JSON.stringify({at:O(),event:e,sessionId:s,...t})}`;r==="error"?console.error(i):console.info(i)}async dispatchQueuedMessages(e){const s=e[0],t=s.trigger?.kind==="scheduled"?s.trigger:null;if(t&&await this.validateScheduledTarget?.({sessionId:s.sessionId,...t})!=="ready")throw new v("schedule_target_unavailable");const r=e.flatMap(g=>{if(g.trigger?.kind==="room_activation"){const{kind:k,...M}=g.trigger;return[M]}return!g.trigger&&g.managedRoom?[g.managedRoom]:[]}),i=e.find(g=>g.trigger?.kind==="manager_event")?.trigger,a=e.some(g=>g.trigger?.kind==="managed_direct"||!!g.managedAgentDirect||g.trigger?.kind==="scheduled"&&!!g.trigger.bindingId),o=e.find(g=>g.trigger?.kind==="manager_worker")?.trigger,n=r.length>0?await this.prepareManagedRoomTurn?.(r):a||i?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(s.sessionId):void 0;if(r.length>0&&!n)throw new Error("managed_room_turn_preparer_unavailable");if(a&&!n)throw new Error("managed_agent_context_unavailable");if(i?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(s.sessionId)&&!n)throw new Error("managed_agent_context_unavailable");const d=new Set((c().sessions[s.sessionId]??[]).map(g=>g.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(g=>!d.has(g.id)))throw n?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(n.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new v("queued_dispatch_cancelled");i?.kind==="manager_event"&&n?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(n.managedRoomContextToken);const u=n??null,l=u?.sessionMode==="manager"?u.managerConfig:null,m=e.length===1?s.text:P(e),I=B(e.flatMap(g=>g.attachments??[])),f=a||i?.kind==="manager_event"&&!!n,y=f?n.agentSessionId?.startsWith("managed:")?q(this.opts.getRuntime(),s):n.agentSessionId:r.length>0?q(this.opts.getRuntime(),s):s.agentSessionId??null,h=n?j().find(g=>g.sessionId===s.sessionId):null;await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...x(A()),sessionId:s.sessionId,text:m,agentType:f?n.agentType:s.agentType,sessionMode:u?.sessionMode??s.sessionMode,managerConfig:u?l:s.managerConfig,workDir:f?n.workDir:s.workDir,agentSessionId:y,modelId:F(h?.modelId)??F(s.modelId),systemPrompt:i?.kind==="manager_event"?null:n?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:u?u.managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:u?u.managerDefaultWorkerModelId:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:I,externalChannel:i?.kind==="manager_event"?void 0:s.externalChannel,replyTarget:i?.kind==="manager_event"?void 0:s.replyTarget,roomContextRegistryKey:i?.kind==="manager_event"?void 0:n?.roomContextRegistryKey,activationCauseMessageId:i?.kind==="manager_event"?void 0:r.at(-1)?.causeMessageId,rootCauseMessageId:i?.kind==="manager_event"?void 0:r.at(-1)?.rootCauseMessageId,activationSequence:i?.kind==="manager_event"?void 0:r.at(-1)?.sequence,activationHopCount:i?.kind==="manager_event"?void 0:r.at(-1)?.hopCount,managedRecoverySource:i?.kind==="manager_event"?"manager_event":a?"managed_direct":r.length>0?"managed_room":void 0,managerEventId:i?.kind==="manager_event"?i.eventId:void 0,managedWorkerParentSessionId:o?.kind==="manager_worker"?o.parentSessionId:void 0,managerWorkerNativeFull:o?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isNativeSkillManager(o.parentSessionId,s.workDir)||void 0,managedRoomContextToken:i?.kind==="manager_event"?void 0:n?.managedRoomContextToken,scheduledDelivery:!!t,suppressExternalOwnerEcho:r.length>0||!!t||void 0,queuedBatchItems:e.map(g=>{const k=D(g);return i?.kind==="manager_event"?{...k,systemPrompt:null,externalChannel:null,replyTarget:null}:k}),waitForDispatch:!0}},n??void 0)}async isSessionBusy(e){const s=this.opts.getRuntime().recoveryBarrier;if(s?.isDispatchFrozen()||s?.isRecovering(e))return!0;const t=this.opts.getRuntime().sessions.get(e);if(!t)return!1;if(t.currentRunId){if(!(t.currentRunId.startsWith("dispatch:")&&!!this.opts.getRuntime().runJournal&&!t.journalRunId)||(t.adapter.getStatus?await t.adapter.getStatus().catch(()=>null):null)?.active)return!0;this.logLifecycle("drain.recovered-stale-dispatch",e,{staleRunId:t.currentRunId}),t.currentRunId=null,t.currentRunPhase=null}if(!t.adapter.getStatus)return!1;const r=await t.adapter.getStatus().catch(()=>null);return r?.active?(r.runId&&(t.currentRunId=r.runId,t.currentRunPhase=r.runPhase??t.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}export{ye as ChatQueueManager,Re as TerminalQueuedChatDispatchError,Se as purgeQueuedSessionData};
|
|
1
|
+
import{NativeSourceDeletionStore as G}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as H}from"./queue-retry.js";import{findDuplicateQueuedMessage as P,stripLocalQueueFields as T,takeCompatibleBatch as U,formatQueuedBatch as B,dedupeQueuedAttachments as Q,classifyQueueError as F,isControllableQueueMessage as C,takeControllableMessage as V,normalizeQueuedModelId as b,resolveManagedRoomNativeSessionId as q}from"./queue-helpers.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as X}from"@shennian/wire";import{isCodexThreadTailInterrupted as Y}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as Z}from"./projection.js";import{assertFinishedAttachmentReferences as L,markAttachmentReferences as W}from"./attachment-transfer-store.js";import{appendMessage as j,getMessageDeliveryStatus as R,listSessionRecords as N,recordSession as z,tombstoneMessage as $,upsertMessage as x}from"./store.js";import{requestSessionIndexPublish as k}from"./index-publisher.js";import{purgeQueuedSessionData as ee,readQueue as m,writeQueue as M}from"./queue-storage.js";import{getNativeSourceAgentType as te}from"./handlers/chat-events.js";import{parsePersonalAttachmentReferences as O}from"./attachment-reference.js";import{getPersonalSyncIdentity as A}from"../personal-sync/epoch.js";import{getDaemonInstallationId as E}from"../personal-sync/identity.js";import{TerminalQueuedChatDispatchError as _,nowIso as K,queueEnvelope as w,queueMessageFromParams as J}from"./queue-message.js";import{purgeQueuedSessionData as Se}from"./queue-storage.js";import{TerminalQueuedChatDispatchError as Re}from"./queue-message.js";class ye{opts;validateScheduledTarget;async enqueueScheduled(e){return this.handleEnqueue(e,!0)}draining=new Set;promoting=new Set;disposed=!1;retries=new H(e=>{this.wakeSession(e)});deletions=new G;inFlightMessageIds=new Map;deferredLogState=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}stop(){this.disposed=!0,this.retries.stop()}getSnapshot(e){const s=m().sessions[e]??[],t=this.inFlightMessageIds.get(e);return{...A(E()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(T),controllablePending:s.filter(i=>C(i)&&!t?.has(i.id)).map(T)}}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const s=this.opts.getRuntime(),t=m(),i=new Set,n=new Map;let o=!1;for(const a of e){const r=a.params;if(this.deletions.hasSession(r.sessionId)){delete t.sessions[r.sessionId],o=!0;continue}if(!r.sessionId||!r.text||!r.agentType||!r.workDir)throw new Error("sessionId, text, agentType and workDir are required");const d=J({...A(E()),...r});d.origin="room",d.managedRoom=a.context,d.trigger={kind:"room_activation",...a.context};const c=t.sessions[r.sessionId]??[],l=P(c,d),I=d.clientMessageId??d.id,f=R(r.sessionId,I);if(!l&&f.state==="not_found")c.push(d),t.sessions[r.sessionId]=c,n.set(`${d.sessionId}\0${I}`,d),i.add(r.sessionId),o=!0;else if(l&&f.state==="not_found")n.set(`${l.sessionId}\0${I}`,l),i.add(r.sessionId);else if(l&&f.state==="queued")i.add(r.sessionId);else if(l){const y=c.filter(h=>h.id!==l.id);y.length?t.sessions[r.sessionId]=y:delete t.sessions[r.sessionId],o=!0}}o&&M(t);let u;for(const a of i)try{for(const r of n.values())r.sessionId===a&&(j(a,w(r),"queued"),z({sessionId:a,agentType:r.agentType,sessionMode:r.sessionMode,managerConfig:r.managerConfig,workDir:r.workDir,agentSessionId:q(s,r),modelId:r.modelId}));this.logLifecycle("enqueue.persisted",a,{origin:"room",pendingCount:m().sessions[a]?.length??0}),this.broadcast(a),this.wakeSession(a)}catch(r){u=r,this.logLifecycle("enqueue.commit-failed",a,{failureClass:F(r instanceof Error?r.message:"","canonical_commit_failed")},"error"),this.retries.schedule(a,!0)}if(i.size&&k(s.client),u)throw u}async handleEnqueue(e,s=!1){const t=this.opts.getRuntime(),i=e.params;if(i.scheduledTask&&!s)throw new Error("schedule_source_untrusted");const n=!!(s&&i.scheduledTask?.bindingId);if(Z(i.sessionListProjection),!i.sessionId||!i.text||!i.agentType||!i.workDir)return t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const o=i.managedAgentDirect||n?await this.prepareManagedAgentDirectTurn?.(i.sessionId):null;if((i.managedAgentDirect||n)&&!o)throw new Error("managed_agent_context_unavailable");const u=o?N().find(D=>D.sessionId===i.sessionId):null,a=o?{...i,...o,agentType:o.agentType,sessionMode:o.sessionMode,managerConfig:o.managerConfig,workDir:o.workDir,agentSessionId:o.agentSessionId?.startsWith("managed:")?u?.agentSessionId&&!u.agentSessionId.startsWith("managed:")?u.agentSessionId:null:o.agentSessionId,modelId:u?.modelId??void 0,systemPrompt:i.managedAgentDirect?null:o.systemPrompt,externalChannel:i.managedAgentDirect?null:i.externalChannel,replyTarget:i.managedAgentDirect?null:i.replyTarget,managerDefaultWorkerAgentType:o.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:o.managerDefaultWorkerModelId}:i,r=t.resolvePath(a.workDir),d=O(a.attachments);d?.length&&L(r,d);const c={text:a.text,attachments:d,localized:!1},l=t.sessions.get(a.sessionId);if(a.agentType==="codex"&&a.agentSessionId&&l?.currentRunId?.startsWith("dispatch:")&&Y(a.agentSessionId))return t.client.sendRes({type:"res",id:e.id,ok:!1,error:X}),!1;o?.managedRoomContextToken&&t.revokeManagedRoomContextToken?.(o.managedRoomContextToken);const I=await this.isSessionBusy(a.sessionId),f=m(),y=f.sessions[a.sessionId]??[],h=J({...a,text:c.text,attachments:c.attachments,systemPrompt:o?null:a.systemPrompt});o&&(i.managedAgentDirect&&(h.managedAgentDirect=!0),h.trigger?.kind!=="manager_event"&&h.trigger?.kind!=="scheduled"&&(h.trigger={kind:"managed_direct"}));const v=h.clientMessageId??h.id,S=R(a.sessionId,v);if(S.state!=="not_found")return t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:S.state==="queued",queueMessageId:h.id,...S,queue:this.getSnapshot(a.sessionId)}}),!0;const g=P(y,h);g||(f.sessions[a.sessionId]=[...y,h],M(f));const p=g??h;return z({sessionId:p.sessionId,agentType:p.agentType,sessionMode:p.sessionMode,managerConfig:p.managerConfig,workDir:p.workDir,agentSessionId:p.agentSessionId,modelId:p.modelId}),j(p.sessionId,w(p),"queued"),W(t.resolvePath(p.workDir),p.attachments??[]),k(t.client),this.logLifecycle("enqueue.persisted",a.sessionId,{origin:a.origin??"app",queueMessageId:p.id,pendingCount:(m().sessions[a.sessionId]??[]).length}),this.broadcast(a.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:g?.id??h.id,...R(a.sessionId,g?.clientMessageId??g?.id??h.clientMessageId??h.id),queue:this.getSnapshot(a.sessionId),...c.localized?{localizedAttachments:!0}:{}}}),I?this.retries.schedule(a.sessionId):this.wakeSession(a.sessionId),!0}async handleGet(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleEdit(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],o=n.findIndex(r=>r.id===t.queueMessageId);if(o<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!C(n[o])||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId)){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const u=O(t.attachments),a=s.resolvePath(n[o].workDir);u?.length&&L(a,u),n[o]={...n[o],text:t.text,attachments:u,updatedAt:K()},i.sessions[t.sessionId]=n,M(i),x(t.sessionId,w(n[o]),"queued"),n[o].attachments?.length&&W(a,n[o].attachments??[]),k(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],o=n.find(a=>a.id===t.queueMessageId);if(o&&(!C(o)||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId))){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const u=n.filter(a=>a.id!==t.queueMessageId);if(u.length===n.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}u.length?i.sessions[t.sessionId]=u:delete i.sessions[t.sessionId],M(i),$(t.sessionId,o?.clientMessageId??o?.id??t.queueMessageId),k(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handlePromote(e){const s=this.opts.getRuntime(),t=e.params,i=o=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:o});if(!t.sessionId||!t.queueMessageId){i("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){i("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let n=[];try{for(;this.draining.has(t.sessionId);)await new Promise(a=>setTimeout(a,5));if(n=V(m().sessions[t.sessionId]??[],t.queueMessageId),!n.length){i("Queued message not found");return}this.inFlightMessageIds.set(t.sessionId,new Set(n.map(a=>a.id)));const o=s.sessions.get(t.sessionId);let u="idle";if(o?.currentRunId)if(this.canInjectActiveBatch(o,n)){const a=n.length===1?n[0].text:B(n),r=n[0].clientMessageId??n[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:n[0].agentType,sourceAgentType:te(n[0].agentType,n[0].modelId),canonicalMessageId:r,sourceSessionKey:o.agentSessionId,text:a,managedEchoPolicy:"suppress_following"});const d=Date.now();try{await o.adapter.injectActiveInput({text:a,attachments:Q(n.flatMap(c=>c.attachments??[])),clientMessageId:r})}catch(c){throw s.nativeFusion?.cancelManagedSend(r),c}for(const c of n)x(t.sessionId,{...w(c),ts:d},"executing");this.removeBatch(t.sessionId,n),u="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId),await this.dispatchQueuedMessages(n),this.removeBatch(t.sessionId,n),u="restarted"}else await this.dispatchQueuedMessages(n),this.removeBatch(t.sessionId,n);k(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId),promotedMessageIds:n.map(a=>a.id),mode:u}})}catch(o){i(o instanceof Error?o.message:"queue_promotion_failed"),this.broadcast(t.sessionId)}finally{this.inFlightMessageIds.delete(t.sessionId),this.promoting.delete(t.sessionId),m().sessions[t.sessionId]?.length&&this.wakeSession(t.sessionId)}}noteTerminal(e){this.wakeSession(e)}purgeSession(e){ee(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const u=m().sessions[e]?.length??0;return this.purgeSession(e),u}const s=m(),t=s.sessions[e]??[],i=this.inFlightMessageIds.get(e)??new Set,n=t.filter(u=>u.managedRoom&&!i.has(u.id));if(n.length===0)return 0;const o=t.filter(u=>!n.includes(u));o.length?s.sessions[e]=o:delete s.sessions[e],M(s);for(const u of n)$(e,u.clientMessageId??u.id);return k(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:n.length,pendingCount:o.length}),this.broadcast(e),n.length}wakeSession(e){this.drainNext(e).catch(()=>{this.logLifecycle("drain.retry-required",e,{},"error"),this.retries.schedule(e,!0)})}drainIdleQueues(){if(this.disposed)return;const e=m();for(const s of Object.keys(e.sessions))this.wakeSession(s)}async drainNext(e){if(this.disposed||this.draining.has(e)||this.promoting.has(e))return;this.draining.add(e);let s=!1,t=!1,i=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const d=m().sessions[e]?.length??0,c=this.deferredLogState.get(e),l=Date.now();(!c||c.pendingCount!==d||l-c.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:d}),this.deferredLogState.set(e,{at:l,pendingCount:d}));return}this.deferredLogState.delete(e);const n=m(),o=n.sessions[e]??[],u=o.filter(d=>{const c=d.clientMessageId??d.id,l=R(e,c).state;return l==="queued"||l==="not_found"});u.length!==o.length&&(u.length?n.sessions[e]=u:delete n.sessions[e],M(n),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:o.length-u.length,pendingCount:u.length}));const a=U(u);if(a.length===0){this.broadcast(e);return}i=a,this.inFlightMessageIds.set(e,new Set(a.map(d=>d.id))),this.logLifecycle("dispatch.start",e,{batchCount:a.length,queueMessageId:a[0].id,origin:a[0].origin??"app"});for(const d of a)x(e,w(d),"queued");await this.dispatchQueuedMessages(a);const r=this.removeBatch(e,a);this.logLifecycle("dispatch.accepted",e,{batchCount:a.length,queueMessageId:a[0].id,pendingCount:r}),this.deferredLogState.delete(e),s=r>0,this.broadcast(e)}catch(n){const o=F(n instanceof Error?n.message:String(n),n instanceof _?"terminal_dispatch_failed":"transient_dispatch_failed"),u=i[0],a=!!(u&&!u.trigger&&!u.clientMessageId&&Date.now()-Date.parse(u.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(n instanceof _||o==="managed_claim_source_binding_conflict"||a){const d=this.removeBatch(e,i);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:i.length,queueMessageId:i[0]?.id,pendingCount:d,failureClass:a?"stale_legacy_managed_worker_dispatch_failure":o},"error"),s=d>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:o},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e),m().sessions[e]?.length?s?queueMicrotask(()=>this.wakeSession(e)):this.retries.schedule(e,t):this.retries.cancel(e)}}canInjectActiveBatch(e,s){if(!e.adapter.supportsActiveInput||!e.adapter.injectActiveInput||!e.journalRunId)return!1;const t=this.opts.getRuntime().runJournal?.get(s[0].sessionId,e.journalRunId),i=s[0].trigger?.kind==="managed_direct"||s[0].managedAgentDirect?null:"personal";return!!(i&&s[0].systemPrompt==null&&t&&t.source===i&&t.deliveryMode==="direct"&&t.agentType===s[0].agentType&&t.workDir===e.workDir&&t.agentSessionId===e.agentSessionId&&t.modelId===(s[0].modelId??null)&&t.reasoningEffort===(s[0].reasoningEffort??null))}removeBatch(e,s){if(s.length===0)return m().sessions[e]?.length??0;const t=m(),i=new Set(s.map(o=>o.id)),n=(t.sessions[e]??[]).filter(o=>!i.has(o.id));return n.length?t.sessions[e]=n:delete t.sessions[e],M(t),n.length}logLifecycle(e,s,t,i="info"){const n=`[chat.queue] ${JSON.stringify({at:K(),event:e,sessionId:s,...t})}`;i==="error"?console.error(n):console.info(n)}async dispatchQueuedMessages(e){const s=e[0],t=s.trigger?.kind==="scheduled"?s.trigger:null;if(t&&await this.validateScheduledTarget?.({sessionId:s.sessionId,...t})!=="ready")throw new _("schedule_target_unavailable");const i=e.flatMap(g=>{if(g.trigger?.kind==="room_activation"){const{kind:p,...D}=g.trigger;return[D]}return!g.trigger&&g.managedRoom?[g.managedRoom]:[]}),n=e.find(g=>g.trigger?.kind==="manager_event")?.trigger,o=e.some(g=>g.trigger?.kind==="managed_direct"||!!g.managedAgentDirect),u=o||!!t?.bindingId,a=e.find(g=>g.trigger?.kind==="manager_worker")?.trigger,r=n?.kind==="manager_event"||o,d=i.length>0?await this.prepareManagedRoomTurn?.(i):u||n?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(s.sessionId):void 0;if(i.length>0&&!d)throw new Error("managed_room_turn_preparer_unavailable");if(u&&!d)throw new Error("managed_agent_context_unavailable");if(n?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(s.sessionId)&&!d)throw new Error("managed_agent_context_unavailable");const c=new Set((m().sessions[s.sessionId]??[]).map(g=>g.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(g=>!c.has(g.id)))throw d?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(d.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new _("queued_dispatch_cancelled");r&&d?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(d.managedRoomContextToken);const l=d??null,I=l?.sessionMode==="manager"?l.managerConfig:null,f=e.length===1?s.text:B(e),y=Q(e.flatMap(g=>g.attachments??[])),h=u||n?.kind==="manager_event"&&!!d,v=h?d.agentSessionId?.startsWith("managed:")?q(this.opts.getRuntime(),s):d.agentSessionId:i.length>0?q(this.opts.getRuntime(),s):s.agentSessionId??null,S=d?N().find(g=>g.sessionId===s.sessionId):null;await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...A(E()),sessionId:s.sessionId,text:f,agentType:h?d.agentType:s.agentType,sessionMode:l?.sessionMode??s.sessionMode,managerConfig:l?I:s.managerConfig,workDir:h?d.workDir:s.workDir,agentSessionId:v,modelId:b(S?.modelId)??b(s.modelId),systemPrompt:r?null:d?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:l?l.managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:l?l.managerDefaultWorkerModelId:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:y,externalChannel:r?void 0:s.externalChannel,replyTarget:r?void 0:s.replyTarget,roomContextRegistryKey:r?void 0:d?.roomContextRegistryKey,activationCauseMessageId:r?void 0:i.at(-1)?.causeMessageId,rootCauseMessageId:r?void 0:i.at(-1)?.rootCauseMessageId,activationSequence:r?void 0:i.at(-1)?.sequence,activationHopCount:r?void 0:i.at(-1)?.hopCount,managedRecoverySource:n?.kind==="manager_event"?"manager_event":u?"managed_direct":i.length>0?"managed_room":void 0,managerEventId:n?.kind==="manager_event"?n.eventId:void 0,managedWorkerParentSessionId:a?.kind==="manager_worker"?a.parentSessionId:void 0,managerWorkerNativeFull:a?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isNativeSkillManager(a.parentSessionId,s.workDir)||void 0,managedRoomContextToken:r?void 0:d?.managedRoomContextToken,scheduledDelivery:!!t,suppressExternalOwnerEcho:i.length>0||!!t||void 0,queuedBatchItems:e.map(g=>{const p=T(g);return r?{...p,systemPrompt:null,externalChannel:null,replyTarget:null}:p}),waitForDispatch:!0}},d??void 0)}async isSessionBusy(e){const s=this.opts.getRuntime().recoveryBarrier;if(s?.isDispatchFrozen()||s?.isRecovering(e))return!0;const t=this.opts.getRuntime().sessions.get(e);if(!t)return!1;if(t.currentRunId){if(!(t.currentRunId.startsWith("dispatch:")&&!!this.opts.getRuntime().runJournal&&!t.journalRunId)||(t.adapter.getStatus?await t.adapter.getStatus().catch(()=>null):null)?.active)return!0;this.logLifecycle("drain.recovered-stale-dispatch",e,{staleRunId:t.currentRunId}),t.currentRunId=null,t.currentRunPhase=null}if(!t.adapter.getStatus)return!1;const i=await t.adapter.getStatus().catch(()=>null);return i?.active?(i.runId&&(t.currentRunId=i.runId,t.currentRunPhase=i.runPhase??t.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}export{ye as ChatQueueManager,Re as TerminalQueuedChatDispatchError,Se as purgeQueuedSessionData};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{handleChatAbort as m,handleChatSend as u}from"./handlers/chat.js";import{TerminalQueuedChatDispatchError as s}from"./queue.js";function d(e){return{source:"room_activation",managedAgentPolicy:e.managedAgentPolicy,managedRoomPromptSnapshot:e.managedRoomPromptSnapshot}}function h(e){return{source:"manager_event",managedAgentPolicy:e.managedAgentPolicy,managedRoomPolicyVersion:e.managedRoomPromptSnapshot.managedRoomPolicyVersion,managedRuntimePolicyHash:e.managedRoomPromptSnapshot.runtimePermissionPolicyHash}}async function v(e,i,a){await u(e,i,d(a))}async function y(e,i,a){const o={value:null},n=new Proxy(e.client,{get:(t,l)=>{if(l==="sendRes")return r=>{o.value=r};const c=Reflect.get(t,l,t);return typeof c=="function"?c.bind(t):c}});if(await u({...e,client:n},i,a?i.params.managedRecoverySource==="
|
|
1
|
+
import{handleChatAbort as m,handleChatSend as u}from"./handlers/chat.js";import{TerminalQueuedChatDispatchError as s}from"./queue.js";function d(e){return{source:"room_activation",managedAgentPolicy:e.managedAgentPolicy,managedRoomPromptSnapshot:e.managedRoomPromptSnapshot}}function h(e){return{source:"manager_event",managedAgentPolicy:e.managedAgentPolicy,managedRoomPolicyVersion:e.managedRoomPromptSnapshot.managedRoomPolicyVersion,managedRuntimePolicyHash:e.managedRoomPromptSnapshot.runtimePermissionPolicyHash}}async function v(e,i,a){await u(e,i,d(a))}async function y(e,i,a){const o={value:null},n=new Proxy(e.client,{get:(t,l)=>{if(l==="sendRes")return r=>{o.value=r};const c=Reflect.get(t,l,t);return typeof c=="function"?c.bind(t):c}});if(await u({...e,client:n},i,a?i.params.managedRecoverySource==="managed_room"||i.params.scheduledDelivery===!0?d(a):h(a):void 0),o.value?.payload?.deliveryState==="failed")throw new s(o.value.payload.failureMessage??o.value.error??"queued_chat_dispatch_failed");if(!o.value?.ok)throw new Error(o.value?.error??"queued_chat_dispatch_failed")}async function P(e,i){const a={value:null},o=new Proxy(e.client,{get:(n,t)=>t==="sendRes"?l=>{a.value=l}:typeof Reflect.get(n,t,n)=="function"?Reflect.get(n,t,n).bind(n):Reflect.get(n,t,n)});if(await m({...e,client:o},{type:"req",id:`queue-promote-abort-${Date.now()}`,method:"chat.abort",params:{sessionId:i,discardPending:!1}}),!a.value?.ok)throw new Error(a.value?.error??"agent_stop_failed")}export{v as dispatchManagedChat,y as dispatchQueuedChat,P as interruptQueuedChat,d as managedChatContext,h as managedManagerEventContext};
|