shennian 0.3.75 → 0.3.77
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/adapter.d.ts +1 -0
- package/dist/src/agents/codex.d.ts +5 -1
- package/dist/src/agents/codex.js +14 -14
- package/dist/src/native-fusion/codex-files.d.ts +8 -0
- package/dist/src/native-fusion/codex-files.js +2 -1
- package/dist/src/session/handlers/chat.js +2 -2
- package/dist/src/session/queue.js +8 -8
- package/node_modules/@shennian/wire/dist/session.d.ts +2 -0
- package/node_modules/@shennian/wire/dist/session.js +2 -0
- package/package.json +1 -1
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
"file": "src/agents/codex.js",
|
|
49
|
-
"beforeBytes":
|
|
50
|
-
"afterBytes":
|
|
49
|
+
"beforeBytes": 57245,
|
|
50
|
+
"afterBytes": 30017
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
53
|
"file": "src/agents/command-spec.js",
|
|
@@ -346,8 +346,8 @@
|
|
|
346
346
|
},
|
|
347
347
|
{
|
|
348
348
|
"file": "src/native-fusion/codex-files.js",
|
|
349
|
-
"beforeBytes":
|
|
350
|
-
"afterBytes":
|
|
349
|
+
"beforeBytes": 4582,
|
|
350
|
+
"afterBytes": 2163
|
|
351
351
|
},
|
|
352
352
|
{
|
|
353
353
|
"file": "src/native-fusion/codex-parser.js",
|
|
@@ -606,8 +606,8 @@
|
|
|
606
606
|
},
|
|
607
607
|
{
|
|
608
608
|
"file": "src/session/handlers/chat.js",
|
|
609
|
-
"beforeBytes":
|
|
610
|
-
"afterBytes":
|
|
609
|
+
"beforeBytes": 42754,
|
|
610
|
+
"afterBytes": 17891
|
|
611
611
|
},
|
|
612
612
|
{
|
|
613
613
|
"file": "src/session/handlers/control.js",
|
|
@@ -686,8 +686,8 @@
|
|
|
686
686
|
},
|
|
687
687
|
{
|
|
688
688
|
"file": "src/session/queue.js",
|
|
689
|
-
"beforeBytes":
|
|
690
|
-
"afterBytes":
|
|
689
|
+
"beforeBytes": 26251,
|
|
690
|
+
"afterBytes": 11986
|
|
691
691
|
},
|
|
692
692
|
{
|
|
693
693
|
"file": "src/session/store.js",
|
|
@@ -61,6 +61,7 @@ export declare abstract class AgentAdapter extends EventEmitter<AgentAdapterEven
|
|
|
61
61
|
}>;
|
|
62
62
|
setTitle?(agentSessionId: string, title: string, workDir?: string): Promise<void>;
|
|
63
63
|
abstract start(sessionId: string, workDir: string, agentSessionId?: string | null, options?: AgentStartOptions): Promise<void>;
|
|
64
|
+
prepareSend?(text: string, modelId?: string, reasoningEffort?: string, attachments?: ChatAttachmentMeta[], systemPrompt?: string | null, context?: AgentSendContext): Promise<void>;
|
|
64
65
|
abstract send(text: string, modelId?: string, reasoningEffort?: string, attachments?: ChatAttachmentMeta[], systemPrompt?: string | null, context?: AgentSendContext): Promise<void | AgentSendResult>;
|
|
65
66
|
abstract resume(agentSessionId: string): Promise<void>;
|
|
66
67
|
abstract stop(): Promise<void>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AgentAdapter, type AgentSendContext, type AgentSendResult } from './adapter.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type ChatAttachmentMeta, type ExternalChannelSessionStatus } from '@shennian/wire';
|
|
3
3
|
import type { SessionRunPhase } from '@shennian/wire';
|
|
4
4
|
import { type CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
5
5
|
import type { ManagedRoomExecutor } from '../room/managed-room-ipc.js';
|
|
@@ -46,9 +46,11 @@ export declare class CodexAdapter extends AgentAdapter {
|
|
|
46
46
|
private configuredManagedRoomContextToken;
|
|
47
47
|
private configuredManagedRoomExecutor;
|
|
48
48
|
private managedRoomToolThreadId;
|
|
49
|
+
private preparedExternalOwnerThreadId;
|
|
49
50
|
constructor(options?: {
|
|
50
51
|
modelInstructionsFile?: string;
|
|
51
52
|
hidden?: boolean;
|
|
53
|
+
isThreadTailInterrupted?: (threadId: string) => boolean;
|
|
52
54
|
});
|
|
53
55
|
private externalChannel;
|
|
54
56
|
private shennianSessionId;
|
|
@@ -64,7 +66,9 @@ export declare class CodexAdapter extends AgentAdapter {
|
|
|
64
66
|
private configurationFingerprint;
|
|
65
67
|
private buildProcessEnv;
|
|
66
68
|
start(_sessionId: string, workDir: string, agentSessionId?: string | null): Promise<void>;
|
|
69
|
+
prepareSend(_text: string, modelId?: string, _reasoningEffort?: string, _attachments?: ChatAttachmentMeta[], _agentSystemPrompt?: string | null, context?: AgentSendContext): Promise<void>;
|
|
67
70
|
send(text: string, modelId?: string, reasoningEffort?: string, attachments?: ChatAttachmentMeta[], agentSystemPrompt?: string | null, context?: AgentSendContext): Promise<void | AgentSendResult>;
|
|
71
|
+
private isThreadTailInterrupted;
|
|
68
72
|
private enqueueForExternalOwner;
|
|
69
73
|
resume(agentSessionId: string): Promise<void>;
|
|
70
74
|
setTitle(agentSessionId: string, title: string, workDir?: string): Promise<void>;
|
package/dist/src/agents/codex.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import{spawnSync as
|
|
2
|
-
`)}function
|
|
3
|
-
`)}function
|
|
4
|
-
${u}`.trim();if(c!==0){p(new Error(g||`codex queue exited with code ${c??"unknown"}`));return}const
|
|
5
|
-
`,u=>{u&&(clearTimeout(l),this.pendingRequests.delete(i),d(u))})})}handleAppServerMessage(e){if(e.method==="item/tool/call"&&e.id!=null){this.handleManagedRoomToolCall(e.id,e.params??{});return}if(e.id!=null){const
|
|
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=
|
|
7
|
-
(exit ${t.exitCode})`:""].join("").trim()});break}case"mcpToolCall":case"dynamicToolCall":{const
|
|
8
|
-
(exit ${t.exit_code})`:""];this.emitEvent({state:"tool-result",name:"command_execution",result:
|
|
9
|
-
`);this.emitText(
|
|
10
|
-
`,thinking:!0}))}resetTextState(){this.hasEmittedText=!1,this.textTail=""}emitText(e,t="delta"){if(!e)return;let
|
|
1
|
+
import{spawnSync as A}from"node:child_process";import{createInterface as y}from"node:readline";import{createHash as w,randomUUID as O}from"node:crypto";import h from"node:path";import{AgentAdapter as M,registerAgent as b}from"./adapter.js";import{resolveBuiltinCommand as v,spawnAgentCommand as T}from"./command-spec.js";import{buildAgentProcessEnv as q,buildManagedAgentProcessEnv as D}from"../agent-env.js";import{ensurePlatformInstructionsFile as N}from"./platform-instructions.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as S}from"@shennian/wire";import{isCodexThreadTailInterrupted as $}from"../native-fusion/codex-files.js";import{resolveManagedAgentAdapterEnforcement as L}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as F}from"../security/managed-agent-runtime.js";import{CODEX_MANAGED_ROOM_DYNAMIC_TOOLS as U,CodexManagedRoomToolBinding as W}from"./codex-managed-room-tools.js";import{assertCodexManagedPermissionProfile as C,CODEX_MANAGED_RUNTIME_SECURITY as z,compileCodexManagedPermissions as B,extractCodexMcpServerNames as G}from"./codex-managed-permissions.js";import{CODEX_APP_SERVER_CLIENT_INFO as H,extractAppServerErrorMessage as J,formatCodexErrorMessage as V,formatTransientCodexStatus as X,isCodexCollabAgentItem as K,isCodexCollabAgentToolName as f,isCodexLegacyCollabAgentItem as Q,isCodexUnsupportedEffortError as Y,isMissingCodexRolloutError as j,isTransientCodexErrorMessage as Z,looksLikeCodexInteractiveAuthPrompt as ee,looksLikeFatalCodexStderr as te,normalizeCodexModelId as I,normalizeCodexReasoningEffort as re,normalizeCodexStderr as E,normalizeTerminalText as se,safeStringify as _,stripGitDirectiveArtifacts as ne}from"./codex-utils.js";import{isCodexUnsupportedEffortError as be,isMissingCodexRolloutError as qe,normalizeCodexModelId as De,normalizeCodexReasoningEffort as Ne}from"./codex-utils.js";F("codex",z);function R(o){const e=o instanceof Error?o.message:String(o);return/already has an active writer|thread-store conflict/i.test(e)}function ie(o){const e=o instanceof Error?o.message:String(o);return/paginated(?:_threads|\s+(?:thread\s+)?history)?.*(?:not supported|unsupported|unavailable)/i.test(e)||/(?:not supported|unsupported|unavailable).*paginated(?:_threads|\s+(?:thread\s+)?history)?/i.test(e)}function oe(o){const e=o instanceof Error?o.message:String(o);return/unknown field [`'"]?excludeTurns/i.test(e)||/excludeTurns.*(?:not supported|unsupported|unavailable)/i.test(e)}function ae(o){const e=o instanceof Error?o.message:String(o);return/(?:method not found|unknown method).*(?:project\/list|project\/create)/i.test(e)||/(?:project\/list|project\/create).*(?:not supported|unsupported|unavailable)/i.test(e)}function de(o,e){const t=h.resolve(e);let r;for(const s of o)if(s.id)for(const i of s.roots??[]){if(!i.path)continue;const n=h.resolve(i.path),a=h.relative(n,t);!(a===""||!a.startsWith("..")&&!h.isAbsolute(a))||n.length<=(r?.rootLength??-1)||(r={id:s.id,rootLength:n.length})}return r?.id}function le(o,e){const t=e?.flatMap(s=>ce(s)).filter(s=>!/^https?:\/\//i.test(s.path)).filter(s=>s.path);return{type:"text",text:pe(o,e),text_elements:[],...t?.length?{local_images:t}:{}}}function ce(o){if(o.kind==="image")return o.previewData?[{path:o.path,data:o.previewData,mimeType:o.mimeType}]:[{path:o.path}];if(o.mimeType.startsWith("video/")){const e=ue(o.path);return e?[{path:e}]:[]}return[]}function ue(o){if(!o||/^https?:\/\//i.test(o))return null;const e=h.extname(o),r=`${e?o.slice(0,-e.length):o}.preview.png`;return A("ffmpeg",["-y","-ss","0","-i",o,"-frames:v","1","-vf","scale=320:-1",r],{stdio:"ignore",timeout:15e3}).status===0?r:null}function pe(o,e){const t=e?.filter(i=>i.kind!=="image").filter(i=>i.path&&!/^https?:\/\//i.test(i.path));if(!t?.length||t.every(i=>o.includes(i.path)))return o;const s=t.map((i,n)=>{const a=i.name||`attachment-${n+1}`,d=i.mimeType?` (${i.mimeType})`:"";return`[${k(a)}${d}](<${i.path}>)`});return[o,"",...s].join(`
|
|
2
|
+
`)}function he(o,e){const t=e?.filter(s=>s.path&&!/^https?:\/\//i.test(s.path)&&!o.includes(s.path));if(!t?.length)return o;const r=t.map((s,i)=>{const n=k(s.name||`attachment-${i+1}`);return s.kind==="image"?``:`[${n}](<${s.path}>)`});return[o,"",...r].join(`
|
|
3
|
+
`)}function me(o,e,t){return["queue","--thread",o,"--message",he(e,t)]}function k(o){return o.replace(/[[\]\\]/g,"\\$&")}class ge extends M{options;type="codex";process=null;workDir=null;seq=0;runId="";pendingUsage;agentSessionId=null;hasEmittedText=!1;textTail="";terminalState="open";forceCloseTimer=null;stderrBuf="";rpcSeq=1;pendingRequests=new Map;appServerReady=null;appServerStartupError=null;activeTurnId=null;deltaItemIds=new Set;lastTransientStatus="";agentSystemPrompt="";managedAgentPolicy=null;managedPermissionConfig=null;configurationRestartRequired=!1;managedRoomTools=new W;configuredManagedRoomContextToken=null;configuredManagedRoomExecutor=null;managedRoomToolThreadId=null;preparedExternalOwnerThreadId=null;constructor(e={}){super(),this.options=e}externalChannel=null;shennianSessionId=null;extraEnv={};configure(e){const t=this.configurationFingerprint();this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{},"managedAgentPolicy"in e&&(this.managedAgentPolicy=e.managedAgentPolicy??null),("managedRoomContextToken"in e||"managedRoomExecutor"in e)&&(this.managedRoomTools.deactivateTurn(),this.configuredManagedRoomContextToken=e.managedRoomContextToken?.trim()||null,this.configuredManagedRoomExecutor=e.managedRoomExecutor??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir);const r=this.configurationFingerprint();t!==r&&this.process&&(this.configurationRestartRequired=!0)}configurationFingerprint(){const e=Object.keys(this.extraEnv).sort().map(t=>[t,this.extraEnv[t]??null]);return w("sha256").update(JSON.stringify({sessionId:this.shennianSessionId,externalChannel:this.externalChannel,env:e,managedAgentPolicy:this.managedAgentPolicy})).digest("hex")}buildProcessEnv(e={}){if(!this.managedAgentPolicy)return q({...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 D({agentType:"codex",providerEnv:t,sessionEnv:t,runtimeEnv:e})}async start(e,t,r){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,this.seq=0,r&&(this.agentSessionId=r)}async prepareSend(e,t,r,s,i,n){if(!(!this.agentSessionId||this.activeTurnId)){this.preparedExternalOwnerThreadId=null;try{await this.ensureAppServer(I(t))}catch(a){if(!R(a))throw a;if(n?.source==="room_activation")throw new Error("codex_managed_thread_owned_externally",{cause:a});if(this.isThreadTailInterrupted(this.agentSessionId))throw new Error(S,{cause:a});this.preparedExternalOwnerThreadId=this.agentSessionId}}}async send(e,t,r,s,i,n){if(n?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");if(n?.source==="room_activation"&&(!this.configuredManagedRoomContextToken||!this.configuredManagedRoomExecutor))throw new Error("codex_managed_room_tool_context_unavailable");this.activeTurnId&&(await this.interruptActiveTurn().catch(()=>{}),await this.killProcess(!0)),this.configurationRestartRequired&&this.process&&await this.killProcess(!0),this.configurationRestartRequired=!1;const a=I(t),d=re(r),l=i?.trim()??"";if(l!==this.agentSystemPrompt&&(this.agentSystemPrompt=l,this.process&&await this.killProcess(!0)),n?.source==="room_activation"&&this.managedRoomTools.configure(this.configuredManagedRoomContextToken,this.configuredManagedRoomExecutor),this.runId=O(),this.seq=0,this.resetRunState(),this.agentSessionId&&this.preparedExternalOwnerThreadId===this.agentSessionId)return this.preparedExternalOwnerThreadId=null,{deliveryMode:"external_owner_queue",queuedSubmissionId:await this.enqueueForExternalOwner(this.agentSessionId,e,s)};try{await this.ensureAppServer(a)}catch(p){if(!this.agentSessionId||!R(p))throw p;if(n?.source==="room_activation")throw new Error("codex_managed_thread_owned_externally",{cause:p});if(this.isThreadTailInterrupted(this.agentSessionId))throw new Error(S,{cause:p});return{deliveryMode:"external_owner_queue",queuedSubmissionId:await this.enqueueForExternalOwner(this.agentSessionId,e,s)}}const u=this.agentSessionId;if(!u)throw new Error("codex app-server did not create a thread");this.emitEvent({state:"start",agentSessionId:u});const m=await this.startTurnWithRecovery(u,e,a,d,s);if(this.activeTurnId=m.turn?.id??null,this.managedPermissionConfig&&this.activeTurnId&&!this.managedRoomTools.bindTurn(u,this.activeTurnId))throw await this.killProcess(),new Error("codex_managed_room_turn_binding_failed");return{deliveryMode:"direct"}}isThreadTailInterrupted(e){return this.options.isThreadTailInterrupted?.(e)??$(e)}async enqueueForExternalOwner(e,t,r){const s=v("codex");if(!s)throw new Error('Command "codex" not found. Is OpenAI Codex CLI installed?');const i=me(e,t,r),n=T(s,i,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});return await new Promise((a,d)=>{let l="",u="",m=!1;const p=(c,g)=>{m||(m=!0,clearTimeout(x),c?d(c):a(g))},x=setTimeout(()=>{n.kill("SIGTERM"),p(new Error("Codex Desktop message queue timed out"))},6e4);x.unref?.(),n.stdout?.on("data",c=>{l+=c.toString()}),n.stderr?.on("data",c=>{u+=c.toString()}),n.once("error",c=>p(c)),n.once("close",c=>{const g=`${l}
|
|
4
|
+
${u}`.trim();if(c!==0){p(new Error(g||`codex queue exited with code ${c??"unknown"}`));return}const P=g.match(/Queued message\s+([^\s]+)\s+for thread/i)?.[1];p(void 0,P)})})}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=v("codex");if(!t){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const r=T(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv()});this.process=r,y({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=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??N(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,this.agentSystemPrompt);r&&t.push("-c",`model_instructions_file=${JSON.stringify(r)}`);const s=T(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,y({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(E(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);if(await this.sendRpc("initialize",{clientInfo:H,capabilities:{experimentalApi:!0}}),this.managedPermissionConfig=this.managedAgentPolicy?await this.buildManagedPermissionConfig():null,this.managedPermissionConfig&&!this.managedRoomTools.isConfigured()&&(this.managedRoomTools.configure(this.configuredManagedRoomContextToken,this.configuredManagedRoomExecutor),!this.managedRoomTools.isConfigured()))throw new Error("codex_managed_room_tool_context_unavailable");if(this.agentSessionId&&this.managedPermissionConfig&&this.agentSessionId!==this.managedRoomToolThreadId){if(!this.managedRoomTools.isConfigured())throw new Error("codex_managed_room_tool_context_unavailable");this.agentSessionId=null}if(this.agentSessionId){const a={threadId:this.agentSessionId,cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},persistExtendedHistory:!0,...this.options.hidden?{threadSource:"subagent"}:{},...t?{model:t}:{}};let d;try{d=await this.sendRpc("thread/resume",{...a,excludeTurns:!0})}catch(l){if(!oe(l))throw l;d=await this.sendRpc("thread/resume",a)}this.agentSessionId=d.thread?.id??this.agentSessionId,this.managedPermissionConfig&&(C(d,this.managedPermissionConfig.profileId),this.managedRoomTools.bindThread(this.agentSessionId),this.managedRoomToolThreadId=this.agentSessionId);return}const r=this.options.hidden?void 0:await this.resolveCodexProjectId(),s={cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},serviceName:"Shennian",threadSource:this.options.hidden?"subagent":"user",experimentalRawEvents:!1,persistExtendedHistory:!0,...this.managedPermissionConfig?{dynamicTools:U}:{},...r?{projectId:r}:{},...t?{model:t}:{}};let i;if(this.options.hidden)i=await this.sendRpc("thread/start",s);else try{i=await this.sendRpc("thread/start",{...s,historyMode:"paginated"})}catch(a){throw ie(a)?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:a}):a}const n=i.thread?.id;if(!n)throw new Error("codex app-server thread/start returned no thread id");this.managedPermissionConfig&&(C(i,this.managedPermissionConfig.profileId),this.managedRoomTools.bindThread(n),this.managedRoomToolThreadId=n),this.agentSessionId=n}async buildManagedPermissionConfig(){const e=this.managedAgentPolicy;if(!e)throw new Error("managed_agent_policy_missing");const t=e.adapterPolicy.externalCapabilities.mcp?[]:G(await this.sendRpc("config/read",{cwd:e.adapterPolicy.workDir}));return B(L(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 s=de(t,e);if(s)return s;const i=h.resolve(e),n=`shennian-project-${w("sha256").update(i).digest("hex")}`,d=(await this.sendRpc("project/create",{idempotencyKey:n,name:h.basename(i)||i,roots:[{path:i}],metadata:{createdBy:"Shennian"}})).project?.id;if(!d)throw new Error("codex app-server project/create returned no project id");return d}catch(t){throw ae(t)?new Error("\u5F53\u524D Codex CLI \u4E0D\u652F\u6301\u521B\u5EFA\u53EF\u5F52\u5C5E\u5230 Codex Desktop \u9879\u76EE\u7684\u65B0\u4F1A\u8BDD\uFF0C\u8BF7\u624B\u52A8\u5347\u7EA7 Codex CLI \u540E\u91CD\u8BD5\u3002\u795E\u5FF5\u4E0D\u4F1A\u81EA\u52A8\u5347\u7EA7\uFF0C\u4E5F\u4E0D\u4F1A\u76F4\u63A5\u4FEE\u6539 Codex Desktop \u7684\u79C1\u6709\u4FA7\u680F\u7D22\u5F15\u3002",{cause:t}):t}}async startTurnWithRecovery(e,t,r,s,i){try{return await this.startTurn(e,t,r,s,i)}catch(n){if(!j(n))throw n;if(this.managedPermissionConfig)throw new Error("codex_managed_thread_rollout_missing",{cause:n});return await this.killProcess(),await this.ensureAppServer(r),await this.startTurn(e,t,r,s,i)}}async startTurn(e,t,r,s,i){try{return await this.sendRpc("turn/start",{threadId:e,input:[le(t,i)],approvalPolicy:"never",...this.managedPermissionConfig?{}:{sandboxPolicy:{type:"dangerFullAccess"}},...r?{model:r}:{},...s?{effort:s}:{}})}catch(n){throw s&&Y(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 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}),s.stdin.write(`${n}
|
|
5
|
+
`,u=>{u&&(clearTimeout(l),this.pendingRequests.delete(i),d(u))})})}handleAppServerMessage(e){if(e.method==="item/tool/call"&&e.id!=null){this.handleManagedRoomToolCall(e.id,e.params??{});return}if(e.id!=null){const r=typeof e.id=="number"?this.pendingRequests.get(e.id):void 0;r&&(clearTimeout(r.timer),this.pendingRequests.delete(e.id),e.error?r.reject(new Error(e.error.message||`codex app-server error ${e.error.code??""}`.trim())):r.resolve(e.result))}if(!e.method)return;const t=e.params??{};switch(e.method){case"turn/started":{const r=typeof t.turn=="object"&&t.turn!==null?t.turn:null,s=typeof r?.id=="string"?r.id:null,i=typeof t.threadId=="string"?t.threadId:"";s&&(this.activeTurnId=s,this.managedPermissionConfig&&!this.managedRoomTools.bindTurn(i,s)&&this.emitErrorIfOpen("codex_managed_room_turn_binding_failed"));break}case"thread/status/changed":{const r=typeof t.threadId=="string"?t.threadId:"";if(r&&this.agentSessionId&&r!==this.agentSessionId)break;const s=typeof t.status=="object"&&t.status!==null?t.status:null;s?.type==="active"&&this.emitEvent({state:"heartbeat",runPhase:this.mapThreadStatusToRunPhase(s)});break}case"item/agentMessage/delta":{const r=typeof t.itemId=="string"?t.itemId:"";r&&this.deltaItemIds.add(r),typeof t.delta=="string"&&this.emitText(t.delta);break}case"item/completed":this.handleAppServerCompletedItem(t);break;case"thread/tokenUsage/updated":this.handleAppServerUsage(t);break;case"turn/completed":{const r=typeof t.turn=="object"&&t.turn!==null?t.turn:null,s=typeof r?.id=="string"?r.id:null;(!this.activeTurnId||s===this.activeTurnId)&&(this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),this.emitFinalIfOpen());break}case"error":{const r=J(t);if(Z(r)){this.emitTransientStatus(r);return}this.emitErrorIfOpen(r);break}}}async handleManagedRoomToolCall(e,t){const r=await this.managedRoomTools.execute(t);this.writeAppServerResponse(e,r)}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=se(e);ee(t)&&this.failAppServerStartup(new Error("Codex \u672A\u767B\u5F55\u3002\u8BF7\u5148\u5728\u8FD9\u53F0\u7535\u8111\u7684\u7EC8\u7AEF\u8FD0\u884C `codex`\uFF0C\u6309\u63D0\u793A\u5B8C\u6210 ChatGPT \u767B\u5F55\u6216\u914D\u7F6E API key\uFF0C\u7136\u540E\u518D\u56DE\u5230\u795E\u5FF5\u53D1\u9001\u6D88\u606F\u3002"))}failAppServerStartup(e){this.appServerStartupError||(this.appServerStartupError=e,this.rejectAllPending(e),this.process?.kill("SIGTERM"))}handleAppServerCompletedItem(e){const t=typeof e.item=="object"&&e.item!==null?e.item:null;if(!(!t||typeof t.type!="string")&&!K(t))switch(t.type){case"agentMessage":{const r=typeof t.id=="string"?t.id:"";if(r&&this.deltaItemIds.has(r))return;typeof t.text=="string"&&this.emitText(t.text,"block");break}case"commandExecution":{const r=typeof t.command=="string"?t.command:"",s=typeof t.aggregatedOutput=="string"?t.aggregatedOutput:"";r&&this.emitEvent({state:"tool-call",name:"command_execution",args:{command:r}}),this.emitEvent({state:"tool-result",name:"command_execution",result:[s,t.exitCode!=null?`
|
|
7
|
+
(exit ${t.exitCode})`:""].join("").trim()});break}case"mcpToolCall":case"dynamicToolCall":{const r=typeof t.tool=="string"?t.tool:t.type;if(f(r))return;const s=typeof t.arguments=="object"&&t.arguments!==null?t.arguments:{};this.emitEvent({state:"tool-call",name:r,args:s}),this.emitEvent({state:"tool-result",name:r,result:_(t.error??t.result??t.contentItems??"")});break}case"fileChange":break;case"approvalRequest":case"approval_request":case"permissionRequest":case"permission_request":case"userInputRequired":case"user_input_required":{this.emitApprovalPending(t);break}}}handleAppServerUsage(e){const t=typeof e.tokenUsage=="object"&&e.tokenUsage!==null?e.tokenUsage:null,r=typeof t?.last=="object"&&t.last!==null?t.last:null,s=typeof t?.total=="object"&&t.total!==null?t.total:null,i=r??s;if(!i)return;const n=typeof i.inputTokens=="number"?i.inputTokens:void 0,a=typeof i.outputTokens=="number"?i.outputTokens:void 0;(n!=null||a!=null)&&(this.pendingUsage={inputTokens:n??0,outputTokens:a??0})}handleCodexItemLine(e){const t=e.item;if(t?.type&&!Q(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&&f(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??_(t.result)})}break}case"file_change":break;case"web_search":{e.type==="item.completed"&&t.query&&this.emitText(`[web_search] ${t.query}`,"block");break}case"todo_list":{if(e.type==="item.completed"&&t.items?.length){const r=t.items.map(s=>`- [${s.completed?"x":" "}] ${s.text}`).join(`
|
|
9
|
+
`);this.emitText(r,"block")}break}case"approval_request":case"approvalRequest":case"permission_request":case"permissionRequest":case"user_input_required":case"userInputRequired":{(e.type==="item.started"||e.type==="item.completed")&&this.emitApprovalPending(t);break}case"error":{e.type==="item.completed"&&t.message&&this.emitEvent({state:"error",message:t.message});break}default:break}}handleStreamEvent(e){const t=e.type??e.event;switch(t?.startsWith("item.")&&this.handleCodexItemLine(e),t){case"turn.completed":{const s=e.usage;if(s){const i=s.input_tokens??s.prompt_tokens,n=s.output_tokens??s.completion_tokens;(i!=null||n!=null)&&(this.pendingUsage={inputTokens:i??0,outputTokens:n??0})}this.emitFinalIfOpen();return}case"turn.failed":{const s=typeof e.error=="object"&&e.error?.message?e.error.message:typeof e.error=="string"?e.error:"turn failed";this.emitErrorIfOpen(s);return}case"error":{const s=typeof e.message=="string"?e.message:"";if(s.includes("Reconnecting"))return;this.emitErrorIfOpen(s||(typeof e.error=="string"?e.error:"stream error"));return}case"thread.started":{const s=e.thread_id??e.session_id??e.id;typeof s=="string"&&s&&(this.agentSessionId=s,this.emitEvent({state:"start",agentSessionId:this.agentSessionId}));return}case"turn.started":return}if(t?.startsWith("item."))return;switch(e.type??e.event){case"message":case"text":case"content":{const s=e.content??e.text;s&&this.emitText(s);break}case"function_call":case"tool_call":{if(this.isApprovalLikeName(e.name)){this.emitApprovalPending(e);break}if(e.name&&f(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&&f(e.name))return;this.emitEvent({state:"tool-result",name:e.name,result:e.output??e.result});break}case"approval_request":case"approvalRequest":case"permission_request":case"permissionRequest":case"user_input_required":case"userInputRequired":{this.emitApprovalPending(e);break}case"error":break;case"done":case"completed":{e.usage&&(this.pendingUsage={inputTokens:e.usage.prompt_tokens??e.usage.input_tokens??0,outputTokens:e.usage.completion_tokens??e.usage.output_tokens??0}),this.emitFinalIfOpen();break}}}emitEvent(e){if(this.terminalState!=="open"&&e.state!=="final"&&e.state!=="error"&&e.state!=="aborted")return;const t={...e,runId:this.runId,seq:this.seq++};this.emit("agentEvent",t)}isApprovalLikeName(e){return typeof e!="string"?!1:/approval|permission|confirm|user[_-]?input|request[_-]?user/i.test(e)}emitApprovalPending(e){const t=typeof e.command=="string"?e.command:"",r=typeof e.title=="string"&&e.title.trim()?e.title:"\u9700\u8981\u7528\u6237\u786E\u8BA4",s=typeof e.prompt=="string"?e.prompt:typeof e.reason=="string"?e.reason:typeof e.message=="string"?e.message:t?`Codex \u8BF7\u6C42\u6267\u884C\u547D\u4EE4\uFF1A${t}`:"Codex \u6B63\u5728\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u540E\u7EE7\u7EED\u3002";this.emitEvent({state:"approval-pending",approval:{title:r,content:s,source:"codex",actionHint:"\u5F53\u524D\u795E\u5FF5\u6682\u4E0D\u652F\u6301\u8FDC\u7A0B\u786E\u8BA4\uFF0C\u8BF7\u56DE\u5230\u8FD0\u884C Codex \u7684\u7535\u8111/\u7EC8\u7AEF\u5B8C\u6210\u786E\u8BA4\u3002"}})}async killProcess(e=!1){const t=this.process;if(this.appServerReady=null,this.activeTurnId=null,this.managedRoomTools.reset(),e||this.clearConfiguredManagedRoomContext(),!t){this.rejectAllPending(new Error("codex app-server stopped"));return}this.process=null,this.clearForceCloseTimer(),t.kill("SIGTERM"),await new Promise(r=>{t.on("close",r),setTimeout(()=>{t.kill("SIGKILL"),r()},3e3)}),this.rejectAllPending(new Error("codex app-server stopped"))}rejectAllPending(e){for(const[t,r]of this.pendingRequests.entries())clearTimeout(r.timer),r.reject(e),this.pendingRequests.delete(t)}handleProcessClose(e,t){const r=E(t);if(e!==0&&e!==null){const s=r||`codex exited with code ${e}`;this.emitErrorIfOpen(s)}else te(r)?this.emitErrorIfOpen(r):this.emitFinalIfOpen();this.pendingUsage=void 0}emitFinalIfOpen(){this.terminalState==="open"&&(this.terminalState="final",this.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.managedRoomTools.deactivateTurn(),this.clearConfiguredManagedRoomContext(),this.emitEvent({state:"error",message:V(e)}),this.scheduleProcessClose())}resetRunState(){this.pendingUsage=void 0,this.terminalState="open",this.deltaItemIds.clear(),this.lastTransientStatus="",this.clearForceCloseTimer(),this.resetTextState()}clearConfiguredManagedRoomContext(){this.configuredManagedRoomContextToken=null,this.configuredManagedRoomExecutor=null}emitTransientStatus(e){const t=X(e);!t||t===this.lastTransientStatus||this.terminalState!=="open"||(this.lastTransientStatus=t,this.emitEvent({state:"delta",text:`${t}
|
|
10
|
+
`,thinking:!0}))}resetTextState(){this.hasEmittedText=!1,this.textTail=""}emitText(e,t="delta"){if(!e)return;let r=ne(e);r&&(t==="block"&&this.hasEmittedText&&!r.startsWith(`
|
|
11
11
|
`)&&(this.textTail.endsWith(`
|
|
12
|
-
`)?
|
|
13
|
-
${
|
|
12
|
+
`)?r=`
|
|
13
|
+
${r}`:this.textTail.endsWith(`
|
|
14
14
|
|
|
15
|
-
`)||(
|
|
15
|
+
`)||(r=`
|
|
16
16
|
|
|
17
|
-
${
|
|
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)}}b("codex",()=>new ge);export{ge as CodexAdapter,me as buildCodexQueueArgs,he as buildCodexQueuedText,R as isCodexActiveWriterError,oe as isCodexExcludeTurnsUnsupportedError,ie as isCodexPaginatedHistoryUnsupportedError,ae as isCodexProjectApiUnsupportedError,be as isCodexUnsupportedEffortError,qe as isMissingCodexRolloutError,De as normalizeCodexModelId,Ne as normalizeCodexReasoningEffort,de as selectCodexProjectIdForWorkDir};
|
|
@@ -1,2 +1,10 @@
|
|
|
1
1
|
export declare function lookupCodexThreadName(sourceSessionKey: string): string | null;
|
|
2
2
|
export declare function listCodexRolloutFiles(): string[];
|
|
3
|
+
export declare function isCodexRolloutTailInterrupted(content: string): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Codex keeps an interrupted externally-owned thread loaded, but pauses its
|
|
6
|
+
* persistent queue. Inspect only the rollout tail: lifecycle events are
|
|
7
|
+
* appended after the turn output, so a later resume naturally supersedes an
|
|
8
|
+
* earlier abort.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isCodexThreadTailInterrupted(sourceSessionKey: string): boolean;
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import
|
|
1
|
+
import s from"node:fs";import a from"node:os";import l from"node:path";import{readJsonlLines as d,safeParse as f}from"./parser-common.js";const u=new Set(["untitled","new chat","new conversation","new task","new thread","codex","\u65E0\u6807\u9898","\u672A\u547D\u540D","\u65B0\u4EFB\u52A1","\u65B0\u4F1A\u8BDD","\u65B0\u5BF9\u8BDD"]);function m(e,i){if((typeof e.id=="string"?e.id:typeof e.thread_id=="string"?e.thread_id:typeof e.session_id=="string"?e.session_id:"")!==i)return null;const t=(typeof e.thread_name=="string"?e.thread_name:typeof e.title=="string"?e.title:"").trim();return t&&!p(t)?t.slice(0,120):null}function p(e){const i=e.trim().toLocaleLowerCase().replace(/[\s_-]+/g," ");return u.has(i)}function T(e){if(!e)return null;const i=l.join(a.homedir(),".codex","session_index.jsonl");if(!s.existsSync(i))return null;let r=null;return d(i,0,n=>{if(!n.trim())return;const t=f(n);if(!t)return;const o=m(t,e);o&&(r=o)}),r}function y(){const e=[l.join(a.homedir(),".codex","sessions"),l.join(a.homedir(),".codex","archived_sessions")],i=[],r=n=>{for(const t of s.readdirSync(n,{withFileTypes:!0})){const o=l.join(n,t.name);t.isDirectory()?r(o):t.isFile()&&t.name.endsWith(".jsonl")&&i.push(o)}};for(const n of e)s.existsSync(n)&&r(n);return[...new Set(i)].sort()}function h(e){const i=e.split(`
|
|
2
|
+
`);for(let r=i.length-1;r>=0;r-=1){const n=f(i[r]??"");if(n?.type!=="event_msg")continue;const t=typeof n.payload=="object"&&n.payload!==null?n.payload:null,o=typeof t?.type=="string"?t.type:"";if(o==="turn_aborted")return!0;if(o==="task_started"||o==="turn_started"||o==="task_complete"||o==="turn_complete"||o==="turn_completed")return!1}return!1}function w(e){if(!e)return!1;const r=y().filter(t=>l.basename(t).includes(e)).map(t=>({filePath:t,mtimeMs:s.statSync(t).mtimeMs})).sort((t,o)=>o.mtimeMs-t.mtimeMs)[0]?.filePath;if(!r)return!1;const n=s.openSync(r,"r");try{const t=s.fstatSync(n).size,o=Math.min(t,512*1024),c=Buffer.allocUnsafe(o);return s.readSync(n,c,0,o,t-o),h(c.toString("utf8"))}finally{s.closeSync(n)}}export{h as isCodexRolloutTailInterrupted,w as isCodexThreadTailInterrupted,y as listCodexRolloutFiles,T as lookupCodexThreadName};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
`),a=t>0?Math.min(t,80):Math.min(e.length,80);return e.slice(0,a)}function de(e,t,a){return`${e}:${Q("sha256").update(`${t}\0${a??""}`).digest("hex")}`}function Oe(e,t,a={}){if(e.state==="tool-call"||e.state==="tool-result"){const s={runId:e.runId,sourceSeq:e.seq};return{state:e.state,runId:e.runId,seq:e.seq,sessionId:t,detailRef:s,status:e.state==="tool-call"?"running":"completed",detailAvailable:!0,...e.durationMs!==void 0?{durationMs:e.durationMs}:{},...e.name?{name:e.name}:{},...e.source?{source:e.source}:{},...e.agentSessionId?{agentSessionId:e.agentSessionId}:{},...a}}return{...e,sessionId:t,...a}}const We=3e4;function Ne(e){return e.state==="heartbeat"?e.runPhase??null:e.state==="tool-call"||e.state==="tool-result"?"tool_running":e.state==="approval-pending"?"waiting_approval":e.state==="delta"?e.thinking?"thinking":"streaming_text":e.state==="init"||e.state==="start"?"thinking":null}function ie(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function le(e,t){return e!=="manager"?e:t==="claude"?"claude":"codex"}function Fe(e,t){W({sessionId:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,workDir:t.workDir,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:t.managerDefaultWorkerModelId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,workDir:t.workDir,status:"active",externalChannel:null}}})}function ce(e){return Me(e)}function ue(e,t,a){return{}}function V(e,t,a,s,o,c,u,p){const I=a==="codex";e.configure?.({sessionId:t,externalChannel:s??null,env:{...ce(a),...ue(t,s,o),...c&&!I?{SHENNIAN_MANAGED_ROOM_CONTEXT:c,[Pe]:$e()}:{}},...u?{managedAgentPolicy:u}:{},...I?{managedRoomContextToken:c??null,managedRoomExecutor:p??null}:{}})}function ge(e,t,a){const s=t.managedRoomContextToken;s&&s!==a&&e.revokeManagedRoomContextToken?.(s),t.managedRoomContextToken=a}function P(e,t){const a=t?.managedRoomContextToken;!t||!a||(t.managedRoomContextToken=null,e.revokeManagedRoomContextToken?.(a))}function He(e,t,a){if(e!=="claude"||!a)return t;const s=t.trim();return!!s&&s.startsWith("-")&&!s.includes("/")?we(a)??t:t}function ze(e,t,a,s){let o=null;function c(n,r={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:Oe(n,t,r),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function u(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function p(n){if(!n.currentRunId||!n.currentRunPhase)return;const r=n.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:t,runId:n.currentRunId,seq:r,runPhase:n.currentRunPhase},seq:r,id:`agent-heartbeat-${n.currentRunId}-${r}-${Date.now()}`})}function I(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{p(n)},We),n.heartbeatTimer.unref?.())}function h(n){const r=n?.pendingTextEvent;!n||!r||!r.text||(b(t,{id:`agent-${r.runId}-${r.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:r.text}),x(e.client),c({state:"delta",runId:r.runId,seq:r.seq,text:r.text,thinking:r.thinking||void 0}),n.pendingTextEvent=null)}s.on("agentEvent",n=>{const r=e.sessions.get(t),_=Ne(n),T=n.state==="final"||n.state==="error"||n.state==="aborted";if(r&&(r.nextEventSeq=n.seq+1,T||(r.currentRunId=n.runId),!T&&_&&(r.currentRunPhase=_,I(r)),n.agentSessionId&&(r.agentSessionId=n.agentSessionId)),e.managerRuntime?.noteAgentEvent(t,n),n.state==="delta"&&n.thinking)return;n.state!=="delta"&&D({level:"info",sessionId:t,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:a}});let k=!1;if(n.state==="tool-call"||n.state==="tool-result"){const m=`agent-${n.runId}-${n.seq}`;qe({sessionId:t,toolCallId:m,detailRevision:n.seq,ts:Date.now(),name:n.name??"tool",...n.args!==void 0?{args:n.args}:{},...n.result!==void 0?{result:n.result}:{},status:n.state==="tool-call"?"running":"completed",...n.durationMs!==void 0?{durationMs:n.durationMs}:{},detailRef:{runId:n.runId,sourceSeq:n.seq}}),b(t,{id:m,sessionId:t,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:n.state==="tool-call"?"tool_use":"tool_result",name:n.name,toolCallId:m,status:n.state==="tool-call"?"running":"completed",...n.durationMs!==void 0?{durationMs:n.durationMs}:{},detailRef:{runId:n.runId,sourceSeq:n.seq},detailRevision:n.seq,detailAvailable:!0,resultOmitted:n.state==="tool-result"})}),k=!0}else n.state==="approval-pending"?(b(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:ke(n.approval)}),k=!0,r&&W({sessionId:t,agentType:a,workDir:r.workDir,agentSessionId:r.agentSessionId,attentionId:de("approval",n.runId,n.seq)})):(n.state==="error"||n.state==="aborted")&&n.message&&(b(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.message}),k=!0);k&&x(e.client);const R=`${t}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&e.runTextAcc.set(R,(e.runTextAcc.get(R)??"")+n.text),n.agentSessionId&&n.agentSessionId!==o&&(o=n.agentSessionId,e.nativeFusion?.noteManagedSourceSession(t,le(a,n.source),n.agentSessionId),r&&W({sessionId:t,agentType:a,workDir:r.workDir,agentSessionId:n.agentSessionId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t,agentType:a,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const m=n.text??"";if(!m)return;r?.pendingTextEvent&&r.pendingTextEvent.runId!==n.runId&&h(r),r&&!r.pendingTextEvent&&(r.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:!1}),r?.pendingTextEvent&&(r.pendingTextEvent.text+=m,r.pendingTextEvent.seq=n.seq);return}let C={};if(n.state==="final"){h(r);const m=e.runTextAcc.get(R)??"";m&&(C={messageSummary:ve(m)}),e.runTextAcc.delete(R)}else n.state==="error"||n.state==="aborted"?(h(r),e.runTextAcc.delete(R)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&h(r);T&&r?.currentRunId===n.runId&&(P(e,r),r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,u(r),e.chatQueue?.noteTerminal(t),W({sessionId:t,agentType:a,workDir:r.workDir,agentSessionId:r.agentSessionId,status:n.state==="final"?"completed":"failed",attentionId:de("terminal",n.runId),lastActivityAt:new Date().toISOString()}),x(e.client)),c(n,C)}),s.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${t} agentType=${a}: ${n.message}`),P(e,e.sessions.get(t)),e.sessions.delete(t),e.chatQueue?.noteTerminal(t),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:t,message:n.message,runId:"",seq:0}})})}function Be(e,t){if(e.processedReqIds.add(t),e.processedReqIds.size>1e3){const a=e.processedReqIds.values().next().value;e.processedReqIds.delete(a)}}async function j(e,t){P(e,t),t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})}function fe(e,t,a,s,o,c,u){return u?e.send(t,a,s,o,c,u):c?.trim()?e.send(t,a,s,o,c):o?.length?e.send(t,a,s,o):s?e.send(t,a,s):e.send(t,a)}async function Le(e,t,a,s,o,c,u,p,I){e.evictIdleSessions();const h=Ae(a);if(!h)throw new Error(`Unsupported agent: ${a}`);const n=s==="manager"&&a!=="manager"?new Ee(h,a==="claude"?"claude":"codex"):h;V(n,t,a,u,p,null,I,e.managedRoomExecutor),await n.start(t,o,c);const r={adapter:n,workDir:o,agentType:a,sessionMode:s,agentSessionId:c??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:u??null,externalReplyTarget:p??null,externalChannelEnv:{...ce(a),...ue(t,u,p)},managedRoomContextToken:null};return e.sessions.set(t,r),ze(e,t,a,n),r}function Ue(e,t){const a=e.sessions.get(t),s=a?.currentRunId;if(!a||!s)return;const o=a.nextEventSeq;e.runTextAcc.delete(`${t}:${s}`),P(e,a),a.pendingTextEvent=null,a.currentRunId=null,a.currentRunPhase=null,a.nextEventSeq=0,a.heartbeatTimer&&(clearInterval(a.heartbeatTimer),a.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:t,runId:s,seq:o},seq:o,id:`agent-evt-${s}-${o}`}),e.chatQueue?.noteTerminal(t)}async function lt(e,t,a){const s=a?t.params.managedRoomContextToken??null:null;let o=!1;try{await Je(e,t,a,()=>{o=!0})}finally{s&&!o&&e.revokeManagedRoomContextToken?.(s)}}async function Je(e,t,a,s){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}const{sessionId:o,text:c,agentType:u,sessionMode:p,managerConfig:I,workDir:h,agentSessionId:n,modelId:r,systemPrompt:_,managerDefaultWorkerAgentType:T,managerDefaultWorkerModelId:k,reasoningEffort:R,clientMessageId:C,sessionListProjection:m,waitForDispatch:me,responseId:pe,managedRoomContextToken:q,suppressExternalOwnerEcho:he,queuedBatchItems:F}=t.params,S=pe||t.id;Te(m);const v=null,H="";if(a?.source==="room_activation"&&(Ce(a,_,q),u==="manager"||p!=="standard"))throw new Error("managed_agent_manager_mode_unsupported");if(Be(e,t.id),!o||!c){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:S,ok:!1,error:"sessionId and text are required"});return}const g=u,$=g==="manager"||p==="manager"?"manager":"standard";$==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(o,T??null,k??null);const z=(g==="claude"||g==="codex"||g==="workbuddy")&&typeof R=="string"&&R.trim()?R.trim():void 0,M=e.resolvePath(He(g,h||ae.homedir(),n)||ae.homedir()),ye=M,O=_e(t.params.attachments);O?.length&&De(M,O);const A={text:c,attachments:O,localized:!1};let i=e.sessions.get(o);if(i){if(i.lastActiveAt=Date.now(),i.agentType!==g||(i.sessionMode??(i.agentType==="manager"?"manager":"standard"))!==$||i.workDir!==M||JSON.stringify(i.externalChannel??null)!==JSON.stringify(v??null)){e.sessions.delete(o);try{await j(e,i)}catch{e.processedReqIds.delete(t.id)}i=void 0}else if(n&&i.agentSessionId!==n)try{await i.adapter.resume(n),i.agentSessionId=n}catch{e.sessions.delete(o);try{await j(e,i)}catch{e.processedReqIds.delete(t.id)}i=void 0}}if(!i)try{i=await Le(e,o,g,$,M,n,v,H,a?.managedAgentPolicy)}catch(d){const f=d instanceof Error&&d.message.startsWith("Unsupported agent:")?d.message:`Failed to start ${u}: ${d instanceof Error?d.message:String(d)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${o} agentType=${u} workDir=${M} agentSessionId=${n??""}: ${f}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:f,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:S,ok:!1,error:f});return}const y={id:C??`user-${t.id}`,sessionId:o,role:"user",ts:Date.now(),payload:re(A.text,A.attachments)},G=Array.isArray(F)&&F.length>0?F.map(d=>({id:d.clientMessageId??d.id,sessionId:o,role:"user",ts:Date.parse(d.createdAt),payload:re(d.text,d.attachments)})):null,B=G??[y],E=xe(o,y.id),K=E.state==="queued";if(E.state!=="not_found"&&!K){e.client.sendRes({type:"res",id:S,ok:!0,payload:E});return}D({level:"info",sessionId:o,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:g,sessionMode:$,managerConfig:I??null,modelId:r,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:k??null,reasoningEffort:z,...a?.source==="room_activation"?{managedRoomPromptSnapshot:a.managedRoomPromptSnapshot}:{}}});const L=(d,f)=>({...se(oe()),sessionId:o,clientMessageId:y.id,messageId:y.id,deliveryState:d,bodyRevision:f,committedAt:new Date(y.ts).toISOString()}),X=d=>{e.nativeFusion?.registerManagedSend({sessionId:o,agentType:g,sourceAgentType:le(g,r),canonicalMessageId:y.id,sourceSessionKey:i.agentSessionId??n??null,text:A.text,managedEchoPolicy:d?.deliveryMode==="external_owner_queue"&&!he?"import_following":"suppress_following",externalRunId:d?.deliveryMode==="external_owner_queue"?`codex-external-${d.queuedSubmissionId??y.id}`:null})},Y=()=>{i.currentRunId||(i.currentRunId=`dispatch:${t.id}`,i.currentRunPhase="thinking",i.nextEventSeq=0)},Z=()=>{if(X(),K)return{...se(oe()),sessionId:o,clientMessageId:E.clientMessageId,messageId:E.messageId,deliveryState:E.deliveryState,bodyRevision:E.bodyRevision,committedAt:E.committedAt};try{Fe(e,{sessionId:o,agentType:g,sessionMode:$,managerConfig:I??null,workDir:ye,agentSessionId:i.agentSessionId??n??null,modelId:r,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:k??null});let d=0;for(const[f,l]of B.entries()){const w=G?N(o,l,"committed"):b(o,l,"committed");f===0&&(d=w)}return be(M,O??[]),x(e.client),L("committed",d)}catch(d){throw e.nativeFusion?.cancelManagedSend?.(y.id),d}},ee=d=>{i.currentRunId||(i.nextEventSeq=0),X(d);const f=d?.deliveryMode==="external_owner_queue"?"queued":"executing";let l=0;for(const[w,U]of B.entries()){const J=N(o,U,f);w===0&&(l=J)}return x(e.client),L(f,l)},te=async(d,f)=>{e.nativeFusion?.cancelManagedSend?.(y.id);const l=ie(g,d);console.error(`[chat.send] send failed reqId=${t.id} sessionId=${o} agentType=${u} workDir=${M} agentSessionId=${i.agentSessionId??n??""}: ${l}`),e.sessions.delete(o);try{await j(e,i)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:l,runId:"",seq:0}}),!f){const w={id:`agent-error-${t.id}-${Date.now()}`,sessionId:o,role:"agent",ts:Date.now(),payload:l};b(o,w),x(e.client)}f&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:S,ok:!1,error:l}))};if(me){try{Z()}catch(l){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:S,ok:!1,error:`Durable message commit failed: ${l instanceof Error?l.message:String(l)}`});return}Y();let d;try{a?.source==="room_activation"&&(V(i.adapter,o,g,v,H,q,a.managedAgentPolicy,e.managedRoomExecutor),ge(e,i,q),s());const l=A.attachments;d=await fe(i.adapter,A.text,r,z,l,_,a?{source:a.source}:void 0),D({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(l){let w=0;for(const[J,Re]of B.entries()){const Se=N(o,Re,"failed");J===0&&(w=Se)}x(e.client);const U=ie(g,l);await te(l,!1),e.client.sendRes({type:"res",id:S,ok:!0,payload:{...L("failed",w),failureMessage:U}});return}const f=ee(d??void 0);e.client.sendRes({type:"res",id:S,ok:!0,payload:{...f,...A.localized?{localizedAttachments:!0}:{},...d?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:d.deliveryMode,queuedSubmissionId:d.queuedSubmissionId}:{}}}),D({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}let ne;try{ne=Z()}catch(d){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:S,ok:!1,error:`Durable message commit failed: ${d instanceof Error?d.message:String(d)}`});return}Y(),a?.source==="room_activation"&&(V(i.adapter,o,g,v,H,q,a.managedAgentPolicy,e.managedRoomExecutor),ge(e,i,q),s()),ee(),e.client.sendRes({type:"res",id:S,ok:!0,payload:{...ne,...A.localized?{localizedAttachments:!0}:{}}}),D({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const Ie=A.attachments;fe(i.adapter,A.text,r,z,Ie,_,a?{source:a.source}:void 0).then(()=>{D({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(d=>{N(o,y,"failed"),x(e.client),te(d,!1)})}async function ct(e,t){const{sessionId:a}=t.params,s=e.sessions.get(a);if(s){P(e,s);try{await s.adapter.stop()}catch{e.client.sendRes({type:"res",id:t.id,ok:!1,error:"agent_stop_failed"});return}Ue(e,a),e.activityPublisher?.publish(a,null)}e.client.sendRes({type:"res",id:t.id,ok:!0})}export{ct as handleChatAbort,lt as handleChatSend,Fe as sendSessionUpdateEvent};
|
|
1
|
+
import re from"node:os";import{createHash as V}from"node:crypto";import{createAgent as Ae}from"../../agents/adapter.js";import{buildApprovalPendingPayload as ke,buildUserMessagePayload as oe}from"@shennian/wire";import{ManagerModeAdapter as we}from"../../agents/manager.js";import{reportLog as D}from"../../log-reporter.js";import{lookupClaudeTranscriptCwd as Ee}from"../../native-fusion/parsers.js";import{appendMessage as b,getDaemonInstallationId as se,getMessageDeliveryStatus as xe,recordSession as N,upsertMessage as F}from"../store.js";import{mergeProjectedSessions as Te}from"../projection.js";import{buildManagedAgentEnv as Me}from"../../agents/config-status.js";import{parsePersonalAttachmentReferences as _e}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as De,markAttachmentReferences as be}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as x}from"../index-publisher.js";import{writeToolDetail as qe}from"../tool-detail-store.js";import{getPersonalSyncIdentity as de}from"../../personal-sync/epoch.js";import{getManagedRoomIpcRoot as $e,MANAGED_ROOM_IPC_ROOT_ENV as Pe}from"../../daemon-ipc/paths.js";function Ce(e,t,a){if(!e.managedAgentPolicy)throw new Error("managed_agent_policy_missing");const s=e.managedRoomPromptSnapshot;if(!s)throw new Error("managed_room_prompt_snapshot_missing");if(!t?.trim())throw new Error("managed_room_prompt_incomplete");if(!a||!/^mrc_[A-Za-z0-9_-]{43}$/.test(a))throw new Error("managed_room_context_unavailable");const r=V("sha256").update(JSON.stringify({requestedPolicy:e.managedAgentPolicy.requestedPolicy,enforcement:e.managedAgentPolicy.enforcement})).digest("hex"),l=V("sha256").update(e.managedAgentPolicy.adapterPolicy.workDir).digest("hex");if(s.runtimePermissionPolicyHash!==r||s.resolvedWorkDirHash!==l||s.enforcementLevel!==e.managedAgentPolicy.enforcement.level)throw new Error("managed_room_prompt_snapshot_mismatch")}function ve(e){const t=e.indexOf(`
|
|
2
|
+
`),a=t>0?Math.min(t,80):Math.min(e.length,80);return e.slice(0,a)}function ie(e,t,a){return`${e}:${V("sha256").update(`${t}\0${a??""}`).digest("hex")}`}function Oe(e,t,a={}){if(e.state==="tool-call"||e.state==="tool-result"){const s={runId:e.runId,sourceSeq:e.seq};return{state:e.state,runId:e.runId,seq:e.seq,sessionId:t,detailRef:s,status:e.state==="tool-call"?"running":"completed",detailAvailable:!0,...e.durationMs!==void 0?{durationMs:e.durationMs}:{},...e.name?{name:e.name}:{},...e.source?{source:e.source}:{},...e.agentSessionId?{agentSessionId:e.agentSessionId}:{},...a}}return{...e,sessionId:t,...a}}const We=3e4;function Ne(e){return e.state==="heartbeat"?e.runPhase??null:e.state==="tool-call"||e.state==="tool-result"?"tool_running":e.state==="approval-pending"?"waiting_approval":e.state==="delta"?e.thinking?"thinking":"streaming_text":e.state==="init"||e.state==="start"?"thinking":null}function le(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function ce(e,t){return e!=="manager"?e:t==="claude"?"claude":"codex"}function Fe(e,t){N({sessionId:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,workDir:t.workDir,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:t.managerDefaultWorkerModelId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,workDir:t.workDir,status:"active",externalChannel:null}}})}function ue(e){return Me(e)}function ge(e,t,a){return{}}function j(e,t,a,s,r,l,c,h){const S=a==="codex";e.configure?.({sessionId:t,externalChannel:s??null,env:{...ue(a),...ge(t,s,r),...l&&!S?{SHENNIAN_MANAGED_ROOM_CONTEXT:l,[Pe]:$e()}:{}},...c?{managedAgentPolicy:c}:{},...S?{managedRoomContextToken:l??null,managedRoomExecutor:h??null}:{}})}function fe(e,t,a){const s=t.managedRoomContextToken;s&&s!==a&&e.revokeManagedRoomContextToken?.(s),t.managedRoomContextToken=a}function P(e,t){const a=t?.managedRoomContextToken;!t||!a||(t.managedRoomContextToken=null,e.revokeManagedRoomContextToken?.(a))}function He(e,t,a){if(e!=="claude"||!a)return t;const s=t.trim();return!!s&&s.startsWith("-")&&!s.includes("/")?Ee(a)??t:t}function ze(e,t,a,s){let r=null;function l(n,o={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:Oe(n,t,o),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function c(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function h(n){if(!n.currentRunId||!n.currentRunPhase)return;const o=n.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:t,runId:n.currentRunId,seq:o,runPhase:n.currentRunPhase},seq:o,id:`agent-heartbeat-${n.currentRunId}-${o}-${Date.now()}`})}function S(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{h(n)},We),n.heartbeatTimer.unref?.())}function y(n){const o=n?.pendingTextEvent;!n||!o||!o.text||(b(t,{id:`agent-${o.runId}-${o.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:o.text}),x(e.client),l({state:"delta",runId:o.runId,seq:o.seq,text:o.text,thinking:o.thinking||void 0}),n.pendingTextEvent=null)}s.on("agentEvent",n=>{const o=e.sessions.get(t),T=Ne(n),M=n.state==="final"||n.state==="error"||n.state==="aborted";if(o&&(o.nextEventSeq=n.seq+1,M||(o.currentRunId=n.runId),!M&&T&&(o.currentRunPhase=T,S(o)),n.agentSessionId&&(o.agentSessionId=n.agentSessionId)),e.managerRuntime?.noteAgentEvent(t,n),n.state==="delta"&&n.thinking)return;n.state!=="delta"&&D({level:"info",sessionId:t,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:a}});let k=!1;if(n.state==="tool-call"||n.state==="tool-result"){const m=`agent-${n.runId}-${n.seq}`;qe({sessionId:t,toolCallId:m,detailRevision:n.seq,ts:Date.now(),name:n.name??"tool",...n.args!==void 0?{args:n.args}:{},...n.result!==void 0?{result:n.result}:{},status:n.state==="tool-call"?"running":"completed",...n.durationMs!==void 0?{durationMs:n.durationMs}:{},detailRef:{runId:n.runId,sourceSeq:n.seq}}),b(t,{id:m,sessionId:t,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:n.state==="tool-call"?"tool_use":"tool_result",name:n.name,toolCallId:m,status:n.state==="tool-call"?"running":"completed",...n.durationMs!==void 0?{durationMs:n.durationMs}:{},detailRef:{runId:n.runId,sourceSeq:n.seq},detailRevision:n.seq,detailAvailable:!0,resultOmitted:n.state==="tool-result"})}),k=!0}else n.state==="approval-pending"?(b(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:ke(n.approval)}),k=!0,o&&N({sessionId:t,agentType:a,workDir:o.workDir,agentSessionId:o.agentSessionId,attentionId:ie("approval",n.runId,n.seq)})):(n.state==="error"||n.state==="aborted")&&n.message&&(b(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.message}),k=!0);k&&x(e.client);const A=`${t}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&e.runTextAcc.set(A,(e.runTextAcc.get(A)??"")+n.text),n.agentSessionId&&n.agentSessionId!==r&&(r=n.agentSessionId,e.nativeFusion?.noteManagedSourceSession(t,ce(a,n.source),n.agentSessionId),o&&N({sessionId:t,agentType:a,workDir:o.workDir,agentSessionId:n.agentSessionId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t,agentType:a,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const m=n.text??"";if(!m)return;o?.pendingTextEvent&&o.pendingTextEvent.runId!==n.runId&&y(o),o&&!o.pendingTextEvent&&(o.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:!1}),o?.pendingTextEvent&&(o.pendingTextEvent.text+=m,o.pendingTextEvent.seq=n.seq);return}let C={};if(n.state==="final"){y(o);const m=e.runTextAcc.get(A)??"";m&&(C={messageSummary:ve(m)}),e.runTextAcc.delete(A)}else n.state==="error"||n.state==="aborted"?(y(o),e.runTextAcc.delete(A)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&y(o);M&&o?.currentRunId===n.runId&&(P(e,o),o.currentRunId=null,o.currentRunPhase=null,o.nextEventSeq=0,c(o),e.chatQueue?.noteTerminal(t),N({sessionId:t,agentType:a,workDir:o.workDir,agentSessionId:o.agentSessionId,status:n.state==="final"?"completed":"failed",attentionId:ie("terminal",n.runId),lastActivityAt:new Date().toISOString()}),x(e.client)),l(n,C)}),s.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${t} agentType=${a}: ${n.message}`),P(e,e.sessions.get(t)),e.sessions.delete(t),e.chatQueue?.noteTerminal(t),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:t,message:n.message,runId:"",seq:0}})})}function Be(e,t){if(e.processedReqIds.add(t),e.processedReqIds.size>1e3){const a=e.processedReqIds.values().next().value;e.processedReqIds.delete(a)}}async function H(e,t){P(e,t),t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})}function me(e,t,a,s,r,l,c){return c?e.send(t,a,s,r,l,c):l?.trim()?e.send(t,a,s,r,l):r?.length?e.send(t,a,s,r):s?e.send(t,a,s):e.send(t,a)}function Le(e,t,a,s,r,l,c){return e.prepareSend?e.prepareSend(t,a,s,r,l,c):Promise.resolve()}async function Ue(e,t,a,s,r,l,c,h,S){e.evictIdleSessions();const y=Ae(a);if(!y)throw new Error(`Unsupported agent: ${a}`);const n=s==="manager"&&a!=="manager"?new we(y,a==="claude"?"claude":"codex"):y;j(n,t,a,c,h,null,S,e.managedRoomExecutor),await n.start(t,r,l);const o={adapter:n,workDir:r,agentType:a,sessionMode:s,agentSessionId:l??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:c??null,externalReplyTarget:h??null,externalChannelEnv:{...ue(a),...ge(t,c,h)},managedRoomContextToken:null};return e.sessions.set(t,o),ze(e,t,a,n),o}function Je(e,t){const a=e.sessions.get(t),s=a?.currentRunId;if(!a||!s)return;const r=a.nextEventSeq;e.runTextAcc.delete(`${t}:${s}`),P(e,a),a.pendingTextEvent=null,a.currentRunId=null,a.currentRunPhase=null,a.nextEventSeq=0,a.heartbeatTimer&&(clearInterval(a.heartbeatTimer),a.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:t,runId:s,seq:r},seq:r,id:`agent-evt-${s}-${r}`}),e.chatQueue?.noteTerminal(t)}async function ct(e,t,a){const s=a?t.params.managedRoomContextToken??null:null;let r=!1;try{await Qe(e,t,a,()=>{r=!0})}finally{s&&!r&&e.revokeManagedRoomContextToken?.(s)}}async function Qe(e,t,a,s){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}const{sessionId:r,text:l,agentType:c,sessionMode:h,managerConfig:S,workDir:y,agentSessionId:n,modelId:o,systemPrompt:T,managerDefaultWorkerAgentType:M,managerDefaultWorkerModelId:k,reasoningEffort:A,clientMessageId:C,sessionListProjection:m,waitForDispatch:G,responseId:pe,managedRoomContextToken:q,suppressExternalOwnerEcho:he,queuedBatchItems:z}=t.params,I=pe||t.id;Te(m);const v=null,B="";if(a?.source==="room_activation"&&(Ce(a,T,q),c==="manager"||h!=="standard"))throw new Error("managed_agent_manager_mode_unsupported");if(Be(e,t.id),!r||!l){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:I,ok:!1,error:"sessionId and text are required"});return}const g=c,$=g==="manager"||h==="manager"?"manager":"standard";$==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(r,M??null,k??null);const O=(g==="claude"||g==="codex"||g==="workbuddy")&&typeof A=="string"&&A.trim()?A.trim():void 0,_=e.resolvePath(He(g,y||re.homedir(),n)||re.homedir()),ye=_,W=_e(t.params.attachments);W?.length&&De(_,W);const p={text:l,attachments:W,localized:!1};let i=e.sessions.get(r);if(i){if(i.lastActiveAt=Date.now(),i.agentType!==g||(i.sessionMode??(i.agentType==="manager"?"manager":"standard"))!==$||i.workDir!==_||JSON.stringify(i.externalChannel??null)!==JSON.stringify(v??null)){e.sessions.delete(r);try{await H(e,i)}catch{e.processedReqIds.delete(t.id)}i=void 0}else if(n&&i.agentSessionId!==n)try{await i.adapter.resume(n),i.agentSessionId=n}catch{e.sessions.delete(r);try{await H(e,i)}catch{e.processedReqIds.delete(t.id)}i=void 0}}if(!i)try{i=await Ue(e,r,g,$,_,n,v,B,a?.managedAgentPolicy)}catch(d){const f=d instanceof Error&&d.message.startsWith("Unsupported agent:")?d.message:`Failed to start ${c}: ${d instanceof Error?d.message:String(d)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${r} agentType=${c} workDir=${_} agentSessionId=${n??""}: ${f}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:f,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:I,ok:!1,error:f});return}const R={id:C??`user-${t.id}`,sessionId:r,role:"user",ts:Date.now(),payload:oe(p.text,p.attachments)},K=Array.isArray(z)&&z.length>0?z.map(d=>({id:d.clientMessageId??d.id,sessionId:r,role:"user",ts:Date.parse(d.createdAt),payload:oe(d.text,d.attachments)})):null,L=K??[R],w=xe(r,R.id),X=w.state==="queued";if(w.state!=="not_found"&&!X){e.client.sendRes({type:"res",id:I,ok:!0,payload:w});return}if(G){a?.source==="room_activation"&&j(i.adapter,r,g,v,B,q,a.managedAgentPolicy,e.managedRoomExecutor);try{await Le(i.adapter,p.text,o,O,p.attachments,T,a?{source:a.source}:void 0)}catch(d){e.processedReqIds.delete(t.id),e.sessions.delete(r),await H(e,i),e.client.sendRes({type:"res",id:I,ok:!1,error:d instanceof Error?d.message:String(d)});return}}D({level:"info",sessionId:r,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:g,sessionMode:$,managerConfig:S??null,modelId:o,managerDefaultWorkerAgentType:M??null,managerDefaultWorkerModelId:k??null,reasoningEffort:O,...a?.source==="room_activation"?{managedRoomPromptSnapshot:a.managedRoomPromptSnapshot}:{}}});const U=(d,f)=>({...de(se()),sessionId:r,clientMessageId:R.id,messageId:R.id,deliveryState:d,bodyRevision:f,committedAt:new Date(R.ts).toISOString()}),Y=d=>{e.nativeFusion?.registerManagedSend({sessionId:r,agentType:g,sourceAgentType:ce(g,o),canonicalMessageId:R.id,sourceSessionKey:i.agentSessionId??n??null,text:p.text,managedEchoPolicy:d?.deliveryMode==="external_owner_queue"&&!he?"import_following":"suppress_following",externalRunId:d?.deliveryMode==="external_owner_queue"?`codex-external-${d.queuedSubmissionId??R.id}`:null})},Z=()=>{i.currentRunId||(i.currentRunId=`dispatch:${t.id}`,i.currentRunPhase="thinking",i.nextEventSeq=0)},ee=()=>{if(Y(),X)return{...de(se()),sessionId:r,clientMessageId:w.clientMessageId,messageId:w.messageId,deliveryState:w.deliveryState,bodyRevision:w.bodyRevision,committedAt:w.committedAt};try{Fe(e,{sessionId:r,agentType:g,sessionMode:$,managerConfig:S??null,workDir:ye,agentSessionId:i.agentSessionId??n??null,modelId:o,managerDefaultWorkerAgentType:M??null,managerDefaultWorkerModelId:k??null});let d=0;for(const[f,u]of L.entries()){const E=K?F(r,u,"committed"):b(r,u,"committed");f===0&&(d=E)}return be(_,W??[]),x(e.client),U("committed",d)}catch(d){throw e.nativeFusion?.cancelManagedSend?.(R.id),d}},te=d=>{i.currentRunId||(i.nextEventSeq=0),Y(d);const f=d?.deliveryMode==="external_owner_queue"?"queued":"executing";let u=0;for(const[E,J]of L.entries()){const Q=F(r,J,f);E===0&&(u=Q)}return x(e.client),U(f,u)},ne=async(d,f)=>{e.nativeFusion?.cancelManagedSend?.(R.id);const u=le(g,d);console.error(`[chat.send] send failed reqId=${t.id} sessionId=${r} agentType=${c} workDir=${_} agentSessionId=${i.agentSessionId??n??""}: ${u}`),e.sessions.delete(r);try{await H(e,i)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:u,runId:"",seq:0}}),!f){const E={id:`agent-error-${t.id}-${Date.now()}`,sessionId:r,role:"agent",ts:Date.now(),payload:u};b(r,E),x(e.client)}f&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:I,ok:!1,error:u}))};if(G){try{ee()}catch(u){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:I,ok:!1,error:`Durable message commit failed: ${u instanceof Error?u.message:String(u)}`});return}Z();let d;try{a?.source==="room_activation"&&(fe(e,i,q),s());const u=p.attachments;d=await me(i.adapter,p.text,o,O,u,T,a?{source:a.source}:void 0),D({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(u){let E=0;for(const[Q,Re]of L.entries()){const Se=F(r,Re,"failed");Q===0&&(E=Se)}x(e.client);const J=le(g,u);await ne(u,!1),e.client.sendRes({type:"res",id:I,ok:!0,payload:{...U("failed",E),failureMessage:J}});return}const f=te(d??void 0);e.client.sendRes({type:"res",id:I,ok:!0,payload:{...f,...p.localized?{localizedAttachments:!0}:{},...d?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:d.deliveryMode,queuedSubmissionId:d.queuedSubmissionId}:{}}}),D({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}let ae;try{ae=ee()}catch(d){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:I,ok:!1,error:`Durable message commit failed: ${d instanceof Error?d.message:String(d)}`});return}Z(),a?.source==="room_activation"&&(j(i.adapter,r,g,v,B,q,a.managedAgentPolicy,e.managedRoomExecutor),fe(e,i,q),s()),te(),e.client.sendRes({type:"res",id:I,ok:!0,payload:{...ae,...p.localized?{localizedAttachments:!0}:{}}}),D({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const Ie=p.attachments;me(i.adapter,p.text,o,O,Ie,T,a?{source:a.source}:void 0).then(()=>{D({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(d=>{F(r,R,"failed"),x(e.client),ne(d,!1)})}async function ut(e,t){const{sessionId:a}=t.params,s=e.sessions.get(a);if(s){P(e,s);try{await s.adapter.stop()}catch{e.client.sendRes({type:"res",id:t.id,ok:!1,error:"agent_stop_failed"});return}Je(e,a),e.activityPublisher?.publish(a,null)}e.client.sendRes({type:"res",id:t.id,ok:!0})}export{ut as handleChatAbort,ct as handleChatSend,Fe as sendSessionUpdateEvent};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import p from"node:fs";import{randomUUID as
|
|
2
|
-
`,{encoding:"utf8",mode:384,flag:"wx"});const i=p.openSync(t,"r");if(typeof i=="number")try{p.fsyncSync(i)}finally{p.closeSync(i)}p.renameSync(t,
|
|
3
|
-
${
|
|
1
|
+
import p from"node:fs";import{randomUUID as R}from"node:crypto";import{CODEX_DESKTOP_SEND_UNAVAILABLE as O,buildUserMessagePayload as j}from"@shennian/wire";import{isCodexThreadTailInterrupted as z}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as F}from"./projection.js";import{parsePersonalAttachmentReferences as D}from"./attachment-reference.js";import{assertFinishedAttachmentReferences as T,markAttachmentReferences as v}from"./attachment-transfer-store.js";import{resolvePersonalSyncV1Path as $}from"../personal-sync/paths.js";import{getPersonalSyncIdentity as M}from"../personal-sync/epoch.js";import{getDaemonInstallationId as S}from"../personal-sync/identity.js";import{appendMessage as E,getMessageDeliveryStatus as q,recordSession as b,tombstoneMessage as K,upsertMessage as P}from"./store.js";import{requestSessionIndexPublish as k}from"./index-publisher.js";function L(){return{sessions:{}}}function C(){return new Date().toISOString()}function U(n){return`enqueue-send-${n}-${R()}`}function g(){try{const n=JSON.parse(p.readFileSync(A(),"utf-8"));return{sessions:n.sessions&&typeof n.sessions=="object"?n.sessions:{}}}catch(n){if(n.code==="ENOENT"||n instanceof Error&&n.message.startsWith("ENOENT:"))return L();throw n}}function I(n){const e=$();p.mkdirSync(e,{recursive:!0,mode:448});const s=A(),t=`${s}.tmp-${process.pid}-${R()}`;try{p.writeFileSync(t,`${JSON.stringify(n,null,2)}
|
|
2
|
+
`,{encoding:"utf8",mode:384,flag:"wx"});const i=p.openSync(t,"r");if(typeof i=="number")try{p.fsyncSync(i)}finally{p.closeSync(i)}p.renameSync(t,s)}finally{try{p.unlinkSync(t)}catch{}}}function A(){return $("outbound-queue.json")}function _(n){const e=C();return{...M(S()),id:n.queueMessageId||n.clientMessageId||`queue-${R()}`,sessionId:n.sessionId,text:n.text,agentType:n.agentType,sessionMode:n.sessionMode,managerConfig:n.managerConfig,workDir:n.workDir,agentSessionId:n.agentSessionId??null,modelId:n.modelId??null,systemPrompt:n.systemPrompt??null,managerDefaultWorkerAgentType:n.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:n.managerDefaultWorkerModelId??null,reasoningEffort:n.reasoningEffort??null,clientMessageId:n.clientMessageId??null,attachments:D(n.attachments),externalChannel:n.externalChannel??null,replyTarget:n.replyTarget??null,origin:n.origin,createdAt:e,updatedAt:e}}function x(n){return{id:n.clientMessageId??n.id,sessionId:n.sessionId,role:"user",ts:Date.parse(n.createdAt),payload:j(n.text,n.attachments)}}class ce{opts;draining=new Set;prepareManagedRoomTurn=null;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}getSnapshot(e){return{...M(S()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:(g().sessions[e]??[]).map(W)}}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const s=this.opts.getRuntime(),t=g(),i=new Set,a=new Map;let d=!1;for(const o of e){const r=o.params;if(!r.sessionId||!r.text||!r.agentType||!r.workDir)throw new Error("sessionId, text, agentType and workDir are required");const l=_({...M(S()),...r});l.origin="room",l.managedRoom=o.context;const f=t.sessions[r.sessionId]??[],u=Q(f,l),y=l.clientMessageId??l.id,h=q(r.sessionId,y);if(!u&&h.state==="not_found")f.push(l),t.sessions[r.sessionId]=f,a.set(`${l.sessionId}\0${y}`,l),i.add(r.sessionId),d=!0;else if(u&&h.state==="not_found")a.set(`${u.sessionId}\0${y}`,u),i.add(r.sessionId);else if(u&&h.state==="queued")i.add(r.sessionId);else if(u){const m=f.filter(c=>c.id!==u.id);m.length?t.sessions[r.sessionId]=m:delete t.sessions[r.sessionId],d=!0}}d&&I(t);for(const o of a.values())E(o.sessionId,x(o),"queued"),b({sessionId:o.sessionId,agentType:o.agentType,sessionMode:o.sessionMode,managerConfig:o.managerConfig,workDir:o.workDir,agentSessionId:o.agentSessionId,modelId:o.modelId});if(i.size!==0){k(s.client);for(const o of i)this.broadcast(o),this.drainNext(o)}}async handleEnqueue(e){const s=this.opts.getRuntime(),t=e.params;if(F(t.sessionListProjection),!t.sessionId||!t.text||!t.agentType||!t.workDir){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"});return}const i=s.resolvePath(t.workDir),a=D(t.attachments);a?.length&&T(i,a);const d={text:t.text,attachments:a,localized:!1},o=s.sessions.get(t.sessionId);if(t.agentType==="codex"&&t.agentSessionId&&o?.currentRunId?.startsWith("dispatch:")&&z(t.agentSessionId)){s.client.sendRes({type:"res",id:e.id,ok:!1,error:O});return}const r=await this.isSessionBusy(t.sessionId),l=g(),f=l.sessions[t.sessionId]??[];if(!r&&!f.length){await this.opts.dispatchReq({...e,id:U(e.id),method:"chat.send",params:{...t,text:d.text,responseId:e.id,clientMessageId:t.clientMessageId??t.queueMessageId,waitForDispatch:!0,attachments:d.attachments}});return}const u=_({...t,text:d.text,attachments:d.attachments}),y=u.clientMessageId??u.id,h=q(t.sessionId,y);if(h.state!=="not_found"){s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:h.state==="queued",queueMessageId:u.id,...h,queue:this.getSnapshot(t.sessionId)}});return}const m=Q(f,u);m||(l.sessions[t.sessionId]=[...f,u],I(l));const c=m??u;b({sessionId:c.sessionId,agentType:c.agentType,sessionMode:c.sessionMode,managerConfig:c.managerConfig,workDir:c.workDir,agentSessionId:c.agentSessionId,modelId:c.modelId}),E(c.sessionId,x(c),"queued"),v(s.resolvePath(c.workDir),c.attachments??[]),k(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:m?.id??u.id,...q(t.sessionId,m?.clientMessageId??m?.id??u.clientMessageId??u.id),queue:this.getSnapshot(t.sessionId),...d.localized?{localizedAttachments:!0}:{}}}),r||this.drainNext(t.sessionId)}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)}}),this.getSnapshot(t.sessionId).busy||this.drainNext(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=g(),a=i.sessions[t.sessionId]??[],d=a.findIndex(l=>l.id===t.queueMessageId);if(d<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(a[d].managedRoom){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=D(t.attachments),r=s.resolvePath(a[d].workDir);o?.length&&T(r,o),a[d]={...a[d],text:t.text,attachments:o,updatedAt:C()},i.sessions[t.sessionId]=a,I(i),P(t.sessionId,x(a[d]),"queued"),a[d].attachments?.length&&v(r,a[d].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=g(),a=i.sessions[t.sessionId]??[],d=a.find(r=>r.id===t.queueMessageId);if(d?.managedRoom){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=a.filter(r=>r.id!==t.queueMessageId);if(o.length===a.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}o.length?i.sessions[t.sessionId]=o:delete i.sessions[t.sessionId],I(i),K(t.sessionId,d?.clientMessageId??d?.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)}})}noteTerminal(e){this.drainNext(e)}purgeSession(e){J(e),this.draining.delete(e)}drainIdleQueues(){const e=g();for(const s of Object.keys(e.sessions))this.drainNext(s)}async drainNext(e){if(!this.draining.has(e)){this.draining.add(e);try{if(await this.isSessionBusy(e))return;const t=g().sessions[e]??[],i=V(t);if(i.length===0){this.broadcast(e);return}for(const r of i)P(e,x(r),"queued");await this.dispatchQueuedMessages(i);const a=g(),d=new Set(i.map(r=>r.id)),o=(a.sessions[e]??[]).filter(r=>!d.has(r.id));o.length?a.sessions[e]=o:delete a.sessions[e],I(a),this.broadcast(e)}catch(s){console.error(`[chat.queue] dispatch failed sessionId=${e}: ${s instanceof Error?s.message:String(s)}`),this.broadcast(e)}finally{this.draining.delete(e)}}}async dispatchQueuedMessages(e){const s=e[0],t=e.flatMap(o=>o.managedRoom?[o.managedRoom]:[]),i=t.length>0?await this.prepareManagedRoomTurn?.(t):void 0;if(t.length>0&&!i)throw new Error("managed_room_turn_preparer_unavailable");const a=e.length===1?s.text:G(e),d=X(e.flatMap(o=>o.attachments??[]));await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...M(S()),sessionId:s.sessionId,text:a,agentType:s.agentType,sessionMode:t.length>0?"standard":s.sessionMode,managerConfig:t.length>0?null:s.managerConfig,workDir:s.workDir,agentSessionId:s.agentSessionId??null,modelId:s.modelId??void 0,systemPrompt:i?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:t.length>0?null:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:t.length>0?null:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:d,externalChannel:s.externalChannel,replyTarget:s.replyTarget,...i??{},roomActivation:t.length>0||void 0,suppressExternalOwnerEcho:t.length>0||void 0,queuedBatchItems:e.map(W),waitForDispatch:!0}})}async isSessionBusy(e){const s=this.opts.getRuntime().sessions.get(e);if(!s)return!1;if(s.currentRunId)return!0;if(!s.adapter.getStatus)return!1;const t=await s.adapter.getStatus().catch(()=>null);return t?.active?(t.runId&&(s.currentRunId=t.runId,s.currentRunPhase=t.runPhase??s.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}function J(n){const e=g();e.sessions[n]&&(delete e.sessions[n],I(e))}function Q(n,e){const s=w(e);if(s)return n.find(i=>w(i)===s)??null;if(e.origin!=="external")return null;const t=B(e);return n.find(i=>B(i)===t)??null}function W(n){const{origin:e,managedRoom:s,...t}=n;return t}function V(n){const e=n[0];if(!e)return[];const s=N(e),t=[];for(const i of n){if(N(i)!==s)break;t.push(i)}return t}function N(n){return JSON.stringify({agentType:n.agentType,sessionMode:n.sessionMode??null,workDir:n.workDir,agentSessionId:n.agentSessionId??null,modelId:n.modelId??null,systemPrompt:n.systemPrompt??null,reasoningEffort:n.reasoningEffort??null,managerConfig:n.managerConfig??null,managerDefaultWorkerAgentType:n.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:n.managerDefaultWorkerModelId??null,externalChannel:n.externalChannel??null,replyTarget:n.replyTarget??null,roomRegistryKey:n.managedRoom?.registryKey??null})}function G(n){return n.every(e=>e.managedRoom)?`[\u7FA4\u6D88\u606F]
|
|
3
|
+
${n.map(e=>H(e.text)).filter(Boolean).join(`
|
|
4
4
|
|
|
5
5
|
`)}`:`[\u8FDE\u7EED\u7528\u6237\u6D88\u606F]
|
|
6
|
-
${
|
|
6
|
+
${n.map((e,s)=>`<\u6D88\u606F ${s+1}>
|
|
7
7
|
${e.text.trim()}`).join(`
|
|
8
8
|
|
|
9
|
-
`)}`}function
|
|
10
|
-
${
|
|
11
|
-
`)?e.slice(6):e}function
|
|
12
|
-
`)}function
|
|
9
|
+
`)}`}function H(n){const e=n.trim(),s=e.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);return s?`<${s[1]}>
|
|
10
|
+
${s[2]}`:e.startsWith(`[\u7FA4\u6D88\u606F]
|
|
11
|
+
`)?e.slice(6):e}function B(n){const e=w(n);return e?`id:${e}`:["body",Y(n.text),...(n.attachments??[]).map(s=>s.path||`${s.name}:${s.mimeType}`)].join(`
|
|
12
|
+
`)}function w(n){const e=typeof n.clientMessageId=="string"?n.clientMessageId.trim():"";if(e)return e;const s=typeof n.id=="string"?n.id.trim():"";return s&&!s.startsWith("queue-")?s:""}function X(n){const e=new Set,s=[];for(const t of n){const i=t.path||`${t.name}:${t.mimeType}`;e.has(i)||(e.add(i),s.push(t))}return s.length?s:void 0}function Y(n){return String(n||"").replace(/\s+/g," ").trim()}export{ce as ChatQueueManager,J as purgeQueuedSessionData};
|
|
@@ -3,6 +3,8 @@ import type { PersonalAttachmentReference, PersonalSyncIdentity, SessionBodyStor
|
|
|
3
3
|
export type BuiltinAgentType = 'claude' | 'codex' | 'workbuddy' | 'gemini' | 'cursor' | 'openclaw' | 'opencode' | 'pi' | 'manager';
|
|
4
4
|
export type CustomAgentType = `custom:${string}`;
|
|
5
5
|
export type AgentType = BuiltinAgentType | CustomAgentType;
|
|
6
|
+
/** The external Codex owner stopped its last turn, so its native queue is paused. */
|
|
7
|
+
export declare const CODEX_DESKTOP_SEND_UNAVAILABLE = "codex_desktop_send_unavailable";
|
|
6
8
|
export declare const DISABLED_BUILTIN_AGENT_TYPES: readonly ["gemini", "openclaw"];
|
|
7
9
|
export declare const AVAILABLE_BUILTIN_AGENT_TYPES: readonly ["claude", "codex", "workbuddy", "cursor", "opencode", "pi"];
|
|
8
10
|
export type ModelInfo = {
|