shennian 0.3.34 → 0.3.36

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.
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "jsMinified": true,
4
4
  "minifier": "esbuild",
5
- "fileCount": 129,
5
+ "fileCount": 130,
6
6
  "files": [
7
7
  {
8
8
  "file": "bin/shennian.js",
@@ -31,8 +31,8 @@
31
31
  },
32
32
  {
33
33
  "file": "src/agents/codex.js",
34
- "beforeBytes": 42123,
35
- "afterBytes": 20861
34
+ "beforeBytes": 43923,
35
+ "afterBytes": 22006
36
36
  },
37
37
  {
38
38
  "file": "src/agents/command-spec.js",
@@ -356,8 +356,8 @@
356
356
  },
357
357
  {
358
358
  "file": "src/native-fusion/parser-common.js",
359
- "beforeBytes": 7572,
360
- "afterBytes": 3488
359
+ "beforeBytes": 7754,
360
+ "afterBytes": 3606
361
361
  },
362
362
  {
363
363
  "file": "src/native-fusion/parsers.js",
@@ -566,8 +566,8 @@
566
566
  },
567
567
  {
568
568
  "file": "src/session/handlers/chat.js",
569
- "beforeBytes": 29425,
570
- "afterBytes": 12272
569
+ "beforeBytes": 29613,
570
+ "afterBytes": 12377
571
571
  },
572
572
  {
573
573
  "file": "src/session/handlers/control.js",
@@ -624,6 +624,11 @@
624
624
  "beforeBytes": 3925,
625
625
  "afterBytes": 1918
626
626
  },
627
+ {
628
+ "file": "src/session/tool-summary.js",
629
+ "beforeBytes": 1401,
630
+ "afterBytes": 731
631
+ },
627
632
  {
628
633
  "file": "src/session/types.js",
629
634
  "beforeBytes": 159,
@@ -3,6 +3,8 @@ import type { ChatAttachmentMeta, ExternalChannelSessionStatus } from '@shennian
3
3
  import type { SessionRunPhase } from '@shennian/wire';
4
4
  export { isCodexUnsupportedEffortError, isMissingCodexRolloutError, normalizeCodexModelId, normalizeCodexReasoningEffort, } from './codex-utils.js';
5
5
  export declare function isCodexActiveWriterError(error: unknown): boolean;
6
+ export declare function isCodexPaginatedHistoryUnsupportedError(error: unknown): boolean;
7
+ export declare function isCodexExcludeTurnsUnsupportedError(error: unknown): boolean;
6
8
  export declare function buildCodexQueuedText(text: string, attachments?: ChatAttachmentMeta[]): string;
7
9
  export declare function buildCodexQueueArgs(threadId: string, text: string, attachments?: ChatAttachmentMeta[]): string[];
8
10
  export declare class CodexAdapter extends AgentAdapter {
@@ -1,11 +1,11 @@
1
- import{spawnSync as C}from"node:child_process";import{createInterface as S}from"node:readline";import{randomUUID as _}from"node:crypto";import A from"node:path";import{AgentAdapter as R,registerAgent as P}from"./adapter.js";import{resolveBuiltinCommand as g,spawnAgentCommand as v}from"./command-spec.js";import{buildAgentProcessEnv as x}from"../agent-env.js";import{ensurePlatformInstructionsFile as q}from"./platform-instructions.js";import{CODEX_APP_SERVER_CLIENT_INFO as O,extractAppServerErrorMessage as $,formatCodexErrorMessage as b,formatTransientCodexStatus as F,isCodexCollabAgentItem as L,isCodexCollabAgentToolName as f,isCodexLegacyCollabAgentItem as M,isCodexUnsupportedEffortError as U,isMissingCodexRolloutError as N,isTransientCodexErrorMessage as D,looksLikeCodexInteractiveAuthPrompt as W,looksLikeFatalCodexStderr as z,makeThreadTitle as B,normalizeCodexModelId as y,normalizeCodexReasoningEffort as G,normalizeCodexStderr as T,normalizeTerminalText as J,safeStringify as w,stripGitDirectiveArtifacts as H}from"./codex-utils.js";import{isCodexUnsupportedEffortError as ce,isMissingCodexRolloutError as he,normalizeCodexModelId as me,normalizeCodexReasoningEffort as fe}from"./codex-utils.js";function V(a){const e=a instanceof Error?a.message:String(a);return/already has an active writer|thread-store conflict/i.test(e)}function Q(a,e){const t=e?.flatMap(r=>K(r)).filter(r=>!/^https?:\/\//i.test(r.path)).filter(r=>r.path);return{type:"text",text:Y(a,e),text_elements:[],...t?.length?{local_images:t}:{}}}function K(a){if(a.kind==="image")return a.previewData?[{path:a.path,data:a.previewData,mimeType:a.mimeType}]:[{path:a.path}];if(a.mimeType.startsWith("video/")){const e=X(a.path);return e?[{path:e}]:[]}return[]}function X(a){if(!a||/^https?:\/\//i.test(a))return null;const e=A.extname(a),s=`${e?a.slice(0,-e.length):a}.preview.png`;return C("ffmpeg",["-y","-ss","0","-i",a,"-frames:v","1","-vf","scale=320:-1",s],{stdio:"ignore",timeout:15e3}).status===0?s:null}function Y(a,e){const t=e?.filter(i=>i.kind!=="image").filter(i=>i.path&&!/^https?:\/\//i.test(i.path));if(!t?.length||t.every(i=>a.includes(i.path)))return a;const r=t.map((i,n)=>{const o=i.name||`attachment-${n+1}`,d=i.mimeType?` (${i.mimeType})`:"";return`[${k(o)}${d}](<${i.path}>)`});return[a,"",...r].join(`
2
- `)}function Z(a,e){const t=e?.filter(r=>r.path&&!/^https?:\/\//i.test(r.path)&&!a.includes(r.path));if(!t?.length)return a;const s=t.map((r,i)=>{const n=k(r.name||`attachment-${i+1}`);return r.kind==="image"?`![${n}](<${r.path}>)`:`[${n}](<${r.path}>)`});return[a,"",...s].join(`
3
- `)}function j(a,e,t){return["queue","--thread",a,"--message",Z(e,t)]}function k(a){return a.replace(/[[\]\\]/g,"\\$&")}class ee extends R{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;namedThread=!1;deltaItemIds=new Set;lastTransientStatus="";agentSystemPrompt="";constructor(e={}){super(),this.options=e}externalChannel=null;shennianSessionId=null;extraEnv={};configure(e){this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{}}async start(e,t,s){this.workDir=t,this.seq=0,s&&(this.agentSessionId=s)}async send(e,t,s,r,i){this.activeTurnId&&(await this.interruptActiveTurn().catch(()=>{}),await this.killProcess());const n=y(t),o=G(s),d=i?.trim()??"";d!==this.agentSystemPrompt&&(this.agentSystemPrompt=d,this.process&&await this.killProcess()),this.runId=_(),this.seq=0,this.resetRunState();try{await this.ensureAppServer(n)}catch(c){if(!this.agentSessionId||!V(c))throw c;return{deliveryMode:"external_owner_queue",queuedSubmissionId:await this.enqueueForExternalOwner(this.agentSessionId,e,r)}}const u=this.agentSessionId;if(!u)throw new Error("codex app-server did not create a thread");this.emitEvent({state:"start",agentSessionId:u}),this.namedThread||(await this.sendRpc("thread/name/set",{threadId:u,name:B(e)}).catch(()=>{}),this.namedThread=!0);const p=await this.startTurnWithRecovery(u,e,n,o,r);return this.activeTurnId=p.turn?.id??null,{deliveryMode:"direct"}}async enqueueForExternalOwner(e,t,s){const r=g("codex");if(!r)throw new Error('Command "codex" not found. Is OpenAI Codex CLI installed?');const i=j(e,t,s),n=v(r,i,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:x({NO_COLOR:"1",...this.extraEnv})});return await new Promise((o,d)=>{let u="",p="",c=!1;const h=(l,m)=>{c||(c=!0,clearTimeout(I),l?d(l):o(m))},I=setTimeout(()=>{n.kill("SIGTERM"),h(new Error("Codex Desktop message queue timed out"))},6e4);I.unref?.(),n.stdout?.on("data",l=>{u+=l.toString()}),n.stderr?.on("data",l=>{p+=l.toString()}),n.once("error",l=>h(l)),n.once("close",l=>{const m=`${u}
4
- ${p}`.trim();if(l!==0){h(new Error(m||`codex queue exited with code ${l??"unknown"}`));return}const E=m.match(/Queued message\s+([^\s]+)\s+for thread/i)?.[1];h(void 0,E)})})}async resume(e){await this.killProcess(),this.agentSessionId=e,this.namedThread=!0,this.resetTextState()}async setTitle(e,t,s){const r=e.trim(),i=t.replace(/\s+/g," ").trim();!r||!i||(this.agentSessionId=r,s?.trim()&&(this.workDir=s),await this.ensureAppServer(),await this.sendRpc("thread/name/set",{threadId:r,name:i}),this.namedThread=!0)}async stop(){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 s=null;try{s=(await this.sendRpc("thread/read",{threadId:e,includeTurns:!1},5e3)).thread?.status??null}catch{}const r=s?.type==="active"||!!t;return{active:r,runId:t,runPhase:r?this.mapThreadStatusToRunPhase(s):null,canStop:!!(r&&t&&this.process)}}spawnCodex(e){const t=g("codex");if(!t){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const s=v(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:x(this.extraEnv)});this.process=s,S({input:s.stdout}).on("line",n=>{if(n.trim())try{const o=JSON.parse(n);this.handleStreamEvent(o)}catch{this.emitEvent({state:"delta",text:n})}});let i="";s.stderr?.on("data",n=>{i+=n.toString()}),s.on("close",n=>{this.process===s&&(this.process=null,this.clearForceCloseTimer(),this.handleProcessClose(n,i))}),s.on("error",n=>{this.process===s&&(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=g("codex");if(!e){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const t=["app-server","--listen","stdio://"],s=this.options.modelInstructionsFile??q(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,this.agentSystemPrompt);s&&t.push("-c",`model_instructions_file=${JSON.stringify(s)}`);const r=v(e,t,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:x({NO_COLOR:"1",...this.extraEnv})});this.process=r,this.stderrBuf="",this.appServerStartupError=null,S({input:r.stdout}).on("line",n=>{if(!n.trim())return;let o;try{o=JSON.parse(n)}catch{this.handleAppServerNonJsonStdout(n);return}this.handleAppServerMessage(o)}),r.stderr?.on("data",n=>{this.stderrBuf+=n.toString()}),r.on("close",n=>{if(this.process!==r)return;const o=this.activeTurnId!=null;this.process=null,this.appServerReady=null,this.activeTurnId=null,this.rejectAllPending(new Error(T(this.stderrBuf)||`codex app-server exited with code ${n}`)),this.terminalState==="open"&&o&&this.handleProcessClose(n,this.stderrBuf)}),r.on("error",n=>{this.process===r&&(this.process=null,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=y(e);if(await this.sendRpc("initialize",{clientInfo:O,capabilities:{experimentalApi:!0}}),this.agentSessionId){const i=await this.sendRpc("thread/resume",{threadId:this.agentSessionId,cwd:this.workDir,approvalPolicy:"never",sandbox:"danger-full-access",persistExtendedHistory:!0,...this.options.hidden?{threadSource:"subagent"}:{},...t?{model:t}:{}});this.agentSessionId=i.thread?.id??this.agentSessionId,this.namedThread=!!i.thread?.name;return}const s=await this.sendRpc("thread/start",{cwd:this.workDir,approvalPolicy:"never",sandbox:"danger-full-access",serviceName:"Shennian",threadSource:this.options.hidden?"subagent":"user",experimentalRawEvents:!1,persistExtendedHistory:!0,...t?{model:t}:{}}),r=s.thread?.id;if(!r)throw new Error("codex app-server thread/start returned no thread id");this.agentSessionId=r,this.namedThread=!!s.thread?.name}async startTurnWithRecovery(e,t,s,r,i){try{return await this.startTurn(e,t,s,r,i)}catch(n){if(!N(n))throw n;return await this.killProcess(),await this.ensureAppServer(s),await this.startTurn(e,t,s,r,i)}}async startTurn(e,t,s,r,i){try{return await this.sendRpc("turn/start",{threadId:e,input:[Q(t,i)],approvalPolicy:"never",sandboxPolicy:{type:"dangerFullAccess"},...s?{model:s}:{},...r?{effort:r}:{}})}catch(n){throw r&&U(n)?new Error(`Codex app-server does not accept reasoning effort "${r}" for this turn. Refresh models or upgrade Codex CLI, then retry.`,{cause:n}):n}}async interruptActiveTurn(){const e=this.agentSessionId,t=this.activeTurnId;!e||!t||!this.process||(await this.sendRpc("turn/interrupt",{threadId:e,turnId:t},5e3),this.activeTurnId=null)}sendRpc(e,t,s=6e4){if(this.appServerStartupError)return Promise.reject(this.appServerStartupError);const r=this.process;if(!r?.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((o,d)=>{const u=setTimeout(()=>{this.pendingRequests.delete(i),d(new Error(`codex app-server request timed out: ${e}`))},s);this.pendingRequests.set(i,{resolve:o,reject:d,timer:u}),r.stdin.write(`${n}
5
- `,p=>{p&&(clearTimeout(u),this.pendingRequests.delete(i),d(p))})})}handleAppServerMessage(e){if(e.id!=null){const s=this.pendingRequests.get(e.id);s&&(clearTimeout(s.timer),this.pendingRequests.delete(e.id),e.error?s.reject(new Error(e.error.message||`codex app-server error ${e.error.code??""}`.trim())):s.resolve(e.result))}if(!e.method)return;const t=e.params??{};switch(e.method){case"turn/started":{const s=typeof t.turn=="object"&&t.turn!==null?t.turn:null,r=typeof s?.id=="string"?s.id:null;r&&(this.activeTurnId=r);break}case"thread/status/changed":{const s=typeof t.threadId=="string"?t.threadId:"";if(s&&this.agentSessionId&&s!==this.agentSessionId)break;const r=typeof t.status=="object"&&t.status!==null?t.status:null;r?.type==="active"&&this.emitEvent({state:"heartbeat",runPhase:this.mapThreadStatusToRunPhase(r)});break}case"item/agentMessage/delta":{const s=typeof t.itemId=="string"?t.itemId:"";s&&this.deltaItemIds.add(s),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 s=typeof t.turn=="object"&&t.turn!==null?t.turn:null,r=typeof s?.id=="string"?s.id:null;(!this.activeTurnId||r===this.activeTurnId)&&(this.activeTurnId=null,this.emitFinalIfOpen());break}case"error":{const s=$(t);if(D(s)){this.emitTransientStatus(s);return}this.emitErrorIfOpen(s);break}}}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=J(e);W(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")&&!L(t))switch(t.type){case"agentMessage":{const s=typeof t.id=="string"?t.id:"";if(s&&this.deltaItemIds.has(s))return;typeof t.text=="string"&&this.emitText(t.text,"block");break}case"commandExecution":{const s=typeof t.command=="string"?t.command:"",r=typeof t.aggregatedOutput=="string"?t.aggregatedOutput:"";s&&this.emitEvent({state:"tool-call",name:"command_execution",args:{command:s}}),this.emitEvent({state:"tool-result",name:"command_execution",result:[r,t.exitCode!=null?`
1
+ import{spawnSync as C}from"node:child_process";import{createInterface as I}from"node:readline";import{randomUUID as _}from"node:crypto";import A from"node:path";import{AgentAdapter as R,registerAgent as P}from"./adapter.js";import{resolveBuiltinCommand as g,spawnAgentCommand as v}from"./command-spec.js";import{buildAgentProcessEnv as x}from"../agent-env.js";import{ensurePlatformInstructionsFile as q}from"./platform-instructions.js";import{CODEX_APP_SERVER_CLIENT_INFO as O,extractAppServerErrorMessage as $,formatCodexErrorMessage as b,formatTransientCodexStatus as L,isCodexCollabAgentItem as F,isCodexCollabAgentToolName as f,isCodexLegacyCollabAgentItem as M,isCodexUnsupportedEffortError as U,isMissingCodexRolloutError as N,isTransientCodexErrorMessage as D,looksLikeCodexInteractiveAuthPrompt as W,looksLikeFatalCodexStderr as z,makeThreadTitle as B,normalizeCodexModelId as S,normalizeCodexReasoningEffort as G,normalizeCodexStderr as T,normalizeTerminalText as J,safeStringify as w,stripGitDirectiveArtifacts as H}from"./codex-utils.js";import{isCodexUnsupportedEffortError as me,isMissingCodexRolloutError as fe,normalizeCodexModelId as ge,normalizeCodexReasoningEffort as ve}from"./codex-utils.js";function V(a){const e=a instanceof Error?a.message:String(a);return/already has an active writer|thread-store conflict/i.test(e)}function Q(a){const e=a instanceof Error?a.message:String(a);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 K(a){const e=a instanceof Error?a.message:String(a);return/unknown field [`'"]?excludeTurns/i.test(e)||/excludeTurns.*(?:not supported|unsupported|unavailable)/i.test(e)}function X(a,e){const t=e?.flatMap(r=>Y(r)).filter(r=>!/^https?:\/\//i.test(r.path)).filter(r=>r.path);return{type:"text",text:j(a,e),text_elements:[],...t?.length?{local_images:t}:{}}}function Y(a){if(a.kind==="image")return a.previewData?[{path:a.path,data:a.previewData,mimeType:a.mimeType}]:[{path:a.path}];if(a.mimeType.startsWith("video/")){const e=Z(a.path);return e?[{path:e}]:[]}return[]}function Z(a){if(!a||/^https?:\/\//i.test(a))return null;const e=A.extname(a),s=`${e?a.slice(0,-e.length):a}.preview.png`;return C("ffmpeg",["-y","-ss","0","-i",a,"-frames:v","1","-vf","scale=320:-1",s],{stdio:"ignore",timeout:15e3}).status===0?s:null}function j(a,e){const t=e?.filter(i=>i.kind!=="image").filter(i=>i.path&&!/^https?:\/\//i.test(i.path));if(!t?.length||t.every(i=>a.includes(i.path)))return a;const r=t.map((i,n)=>{const o=i.name||`attachment-${n+1}`,l=i.mimeType?` (${i.mimeType})`:"";return`[${k(o)}${l}](<${i.path}>)`});return[a,"",...r].join(`
2
+ `)}function ee(a,e){const t=e?.filter(r=>r.path&&!/^https?:\/\//i.test(r.path)&&!a.includes(r.path));if(!t?.length)return a;const s=t.map((r,i)=>{const n=k(r.name||`attachment-${i+1}`);return r.kind==="image"?`![${n}](<${r.path}>)`:`[${n}](<${r.path}>)`});return[a,"",...s].join(`
3
+ `)}function te(a,e,t){return["queue","--thread",a,"--message",ee(e,t)]}function k(a){return a.replace(/[[\]\\]/g,"\\$&")}class se extends R{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;namedThread=!1;deltaItemIds=new Set;lastTransientStatus="";agentSystemPrompt="";constructor(e={}){super(),this.options=e}externalChannel=null;shennianSessionId=null;extraEnv={};configure(e){this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{}}async start(e,t,s){this.workDir=t,this.seq=0,s&&(this.agentSessionId=s)}async send(e,t,s,r,i){this.activeTurnId&&(await this.interruptActiveTurn().catch(()=>{}),await this.killProcess());const n=S(t),o=G(s),l=i?.trim()??"";l!==this.agentSystemPrompt&&(this.agentSystemPrompt=l,this.process&&await this.killProcess()),this.runId=_(),this.seq=0,this.resetRunState();try{await this.ensureAppServer(n)}catch(c){if(!this.agentSessionId||!V(c))throw c;return{deliveryMode:"external_owner_queue",queuedSubmissionId:await this.enqueueForExternalOwner(this.agentSessionId,e,r)}}const u=this.agentSessionId;if(!u)throw new Error("codex app-server did not create a thread");this.emitEvent({state:"start",agentSessionId:u}),this.namedThread||(await this.sendRpc("thread/name/set",{threadId:u,name:B(e)}).catch(()=>{}),this.namedThread=!0);const p=await this.startTurnWithRecovery(u,e,n,o,r);return this.activeTurnId=p.turn?.id??null,{deliveryMode:"direct"}}async enqueueForExternalOwner(e,t,s){const r=g("codex");if(!r)throw new Error('Command "codex" not found. Is OpenAI Codex CLI installed?');const i=te(e,t,s),n=v(r,i,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:x({NO_COLOR:"1",...this.extraEnv})});return await new Promise((o,l)=>{let u="",p="",c=!1;const h=(d,m)=>{c||(c=!0,clearTimeout(y),d?l(d):o(m))},y=setTimeout(()=>{n.kill("SIGTERM"),h(new Error("Codex Desktop message queue timed out"))},6e4);y.unref?.(),n.stdout?.on("data",d=>{u+=d.toString()}),n.stderr?.on("data",d=>{p+=d.toString()}),n.once("error",d=>h(d)),n.once("close",d=>{const m=`${u}
4
+ ${p}`.trim();if(d!==0){h(new Error(m||`codex queue exited with code ${d??"unknown"}`));return}const E=m.match(/Queued message\s+([^\s]+)\s+for thread/i)?.[1];h(void 0,E)})})}async resume(e){await this.killProcess(),this.agentSessionId=e,this.namedThread=!0,this.resetTextState()}async setTitle(e,t,s){const r=e.trim(),i=t.replace(/\s+/g," ").trim();!r||!i||(this.agentSessionId=r,s?.trim()&&(this.workDir=s),await this.ensureAppServer(),await this.sendRpc("thread/name/set",{threadId:r,name:i}),this.namedThread=!0)}async stop(){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 s=null;try{s=(await this.sendRpc("thread/read",{threadId:e,includeTurns:!1},5e3)).thread?.status??null}catch{}const r=s?.type==="active"||!!t;return{active:r,runId:t,runPhase:r?this.mapThreadStatusToRunPhase(s):null,canStop:!!(r&&t&&this.process)}}spawnCodex(e){const t=g("codex");if(!t){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const s=v(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:x(this.extraEnv)});this.process=s,I({input:s.stdout}).on("line",n=>{if(n.trim())try{const o=JSON.parse(n);this.handleStreamEvent(o)}catch{this.emitEvent({state:"delta",text:n})}});let i="";s.stderr?.on("data",n=>{i+=n.toString()}),s.on("close",n=>{this.process===s&&(this.process=null,this.clearForceCloseTimer(),this.handleProcessClose(n,i))}),s.on("error",n=>{this.process===s&&(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=g("codex");if(!e){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const t=["app-server","--listen","stdio://"],s=this.options.modelInstructionsFile??q(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,this.agentSystemPrompt);s&&t.push("-c",`model_instructions_file=${JSON.stringify(s)}`);const r=v(e,t,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:x({NO_COLOR:"1",...this.extraEnv})});this.process=r,this.stderrBuf="",this.appServerStartupError=null,I({input:r.stdout}).on("line",n=>{if(!n.trim())return;let o;try{o=JSON.parse(n)}catch{this.handleAppServerNonJsonStdout(n);return}this.handleAppServerMessage(o)}),r.stderr?.on("data",n=>{this.stderrBuf+=n.toString()}),r.on("close",n=>{if(this.process!==r)return;const o=this.activeTurnId!=null;this.process=null,this.appServerReady=null,this.activeTurnId=null,this.rejectAllPending(new Error(T(this.stderrBuf)||`codex app-server exited with code ${n}`)),this.terminalState==="open"&&o&&this.handleProcessClose(n,this.stderrBuf)}),r.on("error",n=>{this.process===r&&(this.process=null,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=S(e);if(await this.sendRpc("initialize",{clientInfo:O,capabilities:{experimentalApi:!0}}),this.agentSessionId){const n={threadId:this.agentSessionId,cwd:this.workDir,approvalPolicy:"never",sandbox:"danger-full-access",persistExtendedHistory:!0,...this.options.hidden?{threadSource:"subagent"}:{},...t?{model:t}:{}};let o;try{o=await this.sendRpc("thread/resume",{...n,excludeTurns:!0})}catch(l){if(!K(l))throw l;o=await this.sendRpc("thread/resume",n)}this.agentSessionId=o.thread?.id??this.agentSessionId,this.namedThread=!!o.thread?.name;return}const s={cwd:this.workDir,approvalPolicy:"never",sandbox:"danger-full-access",serviceName:"Shennian",threadSource:this.options.hidden?"subagent":"user",experimentalRawEvents:!1,persistExtendedHistory:!0,...t?{model:t}:{}};let r;if(this.options.hidden)r=await this.sendRpc("thread/start",s);else try{r=await this.sendRpc("thread/start",{...s,historyMode:"paginated"})}catch(n){throw Q(n)?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:n}):n}const i=r.thread?.id;if(!i)throw new Error("codex app-server thread/start returned no thread id");this.agentSessionId=i,this.namedThread=!!r.thread?.name}async startTurnWithRecovery(e,t,s,r,i){try{return await this.startTurn(e,t,s,r,i)}catch(n){if(!N(n))throw n;return await this.killProcess(),await this.ensureAppServer(s),await this.startTurn(e,t,s,r,i)}}async startTurn(e,t,s,r,i){try{return await this.sendRpc("turn/start",{threadId:e,input:[X(t,i)],approvalPolicy:"never",sandboxPolicy:{type:"dangerFullAccess"},...s?{model:s}:{},...r?{effort:r}:{}})}catch(n){throw r&&U(n)?new Error(`Codex app-server does not accept reasoning effort "${r}" for this turn. Refresh models or upgrade Codex CLI, then retry.`,{cause:n}):n}}async interruptActiveTurn(){const e=this.agentSessionId,t=this.activeTurnId;!e||!t||!this.process||(await this.sendRpc("turn/interrupt",{threadId:e,turnId:t},5e3),this.activeTurnId=null)}sendRpc(e,t,s=6e4){if(this.appServerStartupError)return Promise.reject(this.appServerStartupError);const r=this.process;if(!r?.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((o,l)=>{const u=setTimeout(()=>{this.pendingRequests.delete(i),l(new Error(`codex app-server request timed out: ${e}`))},s);this.pendingRequests.set(i,{resolve:o,reject:l,timer:u}),r.stdin.write(`${n}
5
+ `,p=>{p&&(clearTimeout(u),this.pendingRequests.delete(i),l(p))})})}handleAppServerMessage(e){if(e.id!=null){const s=this.pendingRequests.get(e.id);s&&(clearTimeout(s.timer),this.pendingRequests.delete(e.id),e.error?s.reject(new Error(e.error.message||`codex app-server error ${e.error.code??""}`.trim())):s.resolve(e.result))}if(!e.method)return;const t=e.params??{};switch(e.method){case"turn/started":{const s=typeof t.turn=="object"&&t.turn!==null?t.turn:null,r=typeof s?.id=="string"?s.id:null;r&&(this.activeTurnId=r);break}case"thread/status/changed":{const s=typeof t.threadId=="string"?t.threadId:"";if(s&&this.agentSessionId&&s!==this.agentSessionId)break;const r=typeof t.status=="object"&&t.status!==null?t.status:null;r?.type==="active"&&this.emitEvent({state:"heartbeat",runPhase:this.mapThreadStatusToRunPhase(r)});break}case"item/agentMessage/delta":{const s=typeof t.itemId=="string"?t.itemId:"";s&&this.deltaItemIds.add(s),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 s=typeof t.turn=="object"&&t.turn!==null?t.turn:null,r=typeof s?.id=="string"?s.id:null;(!this.activeTurnId||r===this.activeTurnId)&&(this.activeTurnId=null,this.emitFinalIfOpen());break}case"error":{const s=$(t);if(D(s)){this.emitTransientStatus(s);return}this.emitErrorIfOpen(s);break}}}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=J(e);W(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")&&!F(t))switch(t.type){case"agentMessage":{const s=typeof t.id=="string"?t.id:"";if(s&&this.deltaItemIds.has(s))return;typeof t.text=="string"&&this.emitText(t.text,"block");break}case"commandExecution":{const s=typeof t.command=="string"?t.command:"",r=typeof t.aggregatedOutput=="string"?t.aggregatedOutput:"";s&&this.emitEvent({state:"tool-call",name:"command_execution",args:{command:s}}),this.emitEvent({state:"tool-result",name:"command_execution",result:[r,t.exitCode!=null?`
6
6
  (exit ${t.exitCode})`:""].join("").trim()});break}case"mcpToolCall":case"dynamicToolCall":{const s=typeof t.tool=="string"?t.tool:t.type;if(f(s))return;const r=typeof t.arguments=="object"&&t.arguments!==null?t.arguments:{};this.emitEvent({state:"tool-call",name:s,args:r}),this.emitEvent({state:"tool-result",name:s,result:w(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,s=typeof t?.last=="object"&&t.last!==null?t.last:null,r=typeof t?.total=="object"&&t.total!==null?t.total:null,i=s??r;if(!i)return;const n=typeof i.inputTokens=="number"?i.inputTokens:void 0,o=typeof i.outputTokens=="number"?i.outputTokens:void 0;(n!=null||o!=null)&&(this.pendingUsage={inputTokens:n??0,outputTokens:o??0})}handleCodexItemLine(e){const t=e.item;if(t?.type&&!M(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 s=[t.aggregated_output??"",t.exit_code!=null?`
7
7
  (exit ${t.exit_code})`:""];this.emitEvent({state:"tool-result",name:"command_execution",result:s.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 s=t.error&&typeof t.error=="object"?t.error.message:void 0;this.emitEvent({state:"tool-result",name:t.tool??"mcp_tool",result:s??w(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 s=t.items.map(r=>`- [${r.completed?"x":" "}] ${r.text}`).join(`
8
- `);this.emitText(s,"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 r=e.usage;if(r){const i=r.input_tokens??r.prompt_tokens,n=r.output_tokens??r.completion_tokens;(i!=null||n!=null)&&(this.pendingUsage={inputTokens:i??0,outputTokens:n??0})}this.emitFinalIfOpen();return}case"turn.failed":{const r=typeof e.error=="object"&&e.error?.message?e.error.message:typeof e.error=="string"?e.error:"turn failed";this.emitErrorIfOpen(r);return}case"error":{const r=typeof e.message=="string"?e.message:"";if(r.includes("Reconnecting"))return;this.emitErrorIfOpen(r||(typeof e.error=="string"?e.error:"stream error"));return}case"thread.started":{const r=e.thread_id??e.session_id??e.id;typeof r=="string"&&r&&(this.agentSessionId=r,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 r=e.content??e.text;r&&this.emitText(r);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:"",s=typeof e.title=="string"&&e.title.trim()?e.title:"\u9700\u8981\u7528\u6237\u786E\u8BA4",r=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:s,content:r,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(){const e=this.process;if(this.appServerReady=null,this.activeTurnId=null,!e){this.rejectAllPending(new Error("codex app-server stopped"));return}this.process=null,this.clearForceCloseTimer(),e.kill("SIGTERM"),await new Promise(t=>{e.on("close",t),setTimeout(()=>{e.kill("SIGKILL"),t()},3e3)}),this.rejectAllPending(new Error("codex app-server stopped"))}rejectAllPending(e){for(const[t,s]of this.pendingRequests.entries())clearTimeout(s.timer),s.reject(e),this.pendingRequests.delete(t)}handleProcessClose(e,t){const s=T(t);if(e!==0&&e!==null){const r=s||`codex exited with code ${e}`;this.emitErrorIfOpen(r)}else z(s)?this.emitErrorIfOpen(s):this.emitFinalIfOpen();this.pendingUsage=void 0}emitFinalIfOpen(){this.terminalState==="open"&&(this.terminalState="final",this.emitEvent({state:"final",usage:this.pendingUsage,agentSessionId:this.agentSessionId??void 0}),this.killProcess().catch(()=>{}))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="error",this.emitEvent({state:"error",message:b(e)}),this.scheduleProcessClose())}resetRunState(){this.pendingUsage=void 0,this.terminalState="open",this.deltaItemIds.clear(),this.lastTransientStatus="",this.clearForceCloseTimer(),this.resetTextState()}emitTransientStatus(e){const t=F(e);!t||t===this.lastTransientStatus||this.terminalState!=="open"||(this.lastTransientStatus=t,this.emitEvent({state:"delta",text:`${t}
8
+ `);this.emitText(s,"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 r=e.usage;if(r){const i=r.input_tokens??r.prompt_tokens,n=r.output_tokens??r.completion_tokens;(i!=null||n!=null)&&(this.pendingUsage={inputTokens:i??0,outputTokens:n??0})}this.emitFinalIfOpen();return}case"turn.failed":{const r=typeof e.error=="object"&&e.error?.message?e.error.message:typeof e.error=="string"?e.error:"turn failed";this.emitErrorIfOpen(r);return}case"error":{const r=typeof e.message=="string"?e.message:"";if(r.includes("Reconnecting"))return;this.emitErrorIfOpen(r||(typeof e.error=="string"?e.error:"stream error"));return}case"thread.started":{const r=e.thread_id??e.session_id??e.id;typeof r=="string"&&r&&(this.agentSessionId=r,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 r=e.content??e.text;r&&this.emitText(r);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:"",s=typeof e.title=="string"&&e.title.trim()?e.title:"\u9700\u8981\u7528\u6237\u786E\u8BA4",r=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:s,content:r,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(){const e=this.process;if(this.appServerReady=null,this.activeTurnId=null,!e){this.rejectAllPending(new Error("codex app-server stopped"));return}this.process=null,this.clearForceCloseTimer(),e.kill("SIGTERM"),await new Promise(t=>{e.on("close",t),setTimeout(()=>{e.kill("SIGKILL"),t()},3e3)}),this.rejectAllPending(new Error("codex app-server stopped"))}rejectAllPending(e){for(const[t,s]of this.pendingRequests.entries())clearTimeout(s.timer),s.reject(e),this.pendingRequests.delete(t)}handleProcessClose(e,t){const s=T(t);if(e!==0&&e!==null){const r=s||`codex exited with code ${e}`;this.emitErrorIfOpen(r)}else z(s)?this.emitErrorIfOpen(s):this.emitFinalIfOpen();this.pendingUsage=void 0}emitFinalIfOpen(){this.terminalState==="open"&&(this.terminalState="final",this.emitEvent({state:"final",usage:this.pendingUsage,agentSessionId:this.agentSessionId??void 0}),this.killProcess().catch(()=>{}))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="error",this.emitEvent({state:"error",message:b(e)}),this.scheduleProcessClose())}resetRunState(){this.pendingUsage=void 0,this.terminalState="open",this.deltaItemIds.clear(),this.lastTransientStatus="",this.clearForceCloseTimer(),this.resetTextState()}emitTransientStatus(e){const t=L(e);!t||t===this.lastTransientStatus||this.terminalState!=="open"||(this.lastTransientStatus=t,this.emitEvent({state:"delta",text:`${t}
9
9
  `,thinking:!0}))}resetTextState(){this.hasEmittedText=!1,this.textTail=""}emitText(e,t="delta"){if(!e)return;let s=H(e);s&&(t==="block"&&this.hasEmittedText&&!s.startsWith(`
10
10
  `)&&(this.textTail.endsWith(`
11
11
  `)?s=`
@@ -13,4 +13,4 @@ ${s}`:this.textTail.endsWith(`
13
13
 
14
14
  `)||(s=`
15
15
 
16
- ${s}`)),this.hasEmittedText=!0,this.textTail=(this.textTail+s).slice(-4),this.emitEvent({state:"delta",text:s}))}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)}}P("codex",()=>new ee);export{ee as CodexAdapter,j as buildCodexQueueArgs,Z as buildCodexQueuedText,V as isCodexActiveWriterError,ce as isCodexUnsupportedEffortError,he as isMissingCodexRolloutError,me as normalizeCodexModelId,fe as normalizeCodexReasoningEffort};
16
+ ${s}`)),this.hasEmittedText=!0,this.textTail=(this.textTail+s).slice(-4),this.emitEvent({state:"delta",text:s}))}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)}}P("codex",()=>new se);export{se as CodexAdapter,te as buildCodexQueueArgs,ee as buildCodexQueuedText,V as isCodexActiveWriterError,K as isCodexExcludeTurnsUnsupportedError,Q as isCodexPaginatedHistoryUnsupportedError,me as isCodexUnsupportedEffortError,fe as isMissingCodexRolloutError,ge as normalizeCodexModelId,ve as normalizeCodexReasoningEffort};
@@ -1,4 +1,4 @@
1
- import{createHash as N}from"node:crypto";import g from"node:fs";import C from"node:os";import n from"node:path";const h=64*1024*1024,x=["/root/.claude-mem/observer-session"],E=x.map(z);function B(e){return _(e.replace(/\r\n/g,`
2
- `).trim())}const I=/^\s*::git-[a-z-]+\{[^\n]*\}\s*$/gm;function _(e){if(!e)return"";if(!/(^|\n)\s*::git-[a-z-]+\{[^\n]*\}\s*(?=\n|$)/.test(e))return e;const r=e.replace(I,"");return r.trim()?r.replace(/[ \t]+\n/g,`
3
- `).replace(/\n+$/g,""):""}function J(e){if(typeof e!="string")return null;const t=Date.parse(e);return Number.isFinite(t)?t:null}function R(e){try{const t=JSON.parse(e);return typeof t=="object"&&t!==null?t:null}catch{return null}}function v(e,t,r){const u=g.statSync(e).size;if(t>=u)return u;const y=g.openSync(e,"r");try{const S=Buffer.allocUnsafe(262144);let l=t,p=t,o=Buffer.alloc(0),f=!1;for(;l<u;){const m=g.readSync(y,S,0,Math.min(262144,u-l),l);if(m<=0)break;l+=m;let s=S.subarray(0,m);o.length>0&&(s=Buffer.concat([o,s]),o=Buffer.alloc(0));let c=0;for(;c<s.length;){const a=s.indexOf(10,c);if(a<0)break;const d=s.subarray(c,a),T=p;p+=a-c+1,c=a+1,!f&&d.length>0&&d.length<=h&&r(d.toString("utf8"),T),f=!1}if(c<s.length){const a=s.subarray(c);f||a.length>h?(f=!0,p+=a.length,o=Buffer.alloc(0)):o=Buffer.from(a)}}return!f&&o.length>0&&o.length<=h&&r(o.toString("utf8"),p),u}finally{g.closeSync(y)}}function F(e){return typeof e.cwd=="string"&&e.cwd.trim()?e.cwd:null}function G(e){return n.basename(n.dirname(e))==="subagents"}function b(e){const t=n.resolve(e);return process.platform==="win32"?t.toLowerCase():t}function w(e,t){const r=b(e),i=b(t);return r===i||r.startsWith(i+n.sep)}function z(e){return n.resolve(e).replace(/\//g,"-")}function M(e){return x.some(t=>w(e,t))}function D(e){return E.includes(e)}function U(e){const t=n.join(C.homedir(),".claude","projects"),r=n.relative(t,e);if(!r||r.startsWith("..")||n.isAbsolute(r))return!1;const[i]=r.split(n.sep);return i?D(i):!1}function V(e,t){return`${e}:${t}`}function H(e,t,r){return`${N("sha1").update(e).digest("hex").slice(0,16)}:${t}:${r}`}function W(e){if(e==null)return"";if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}function K(e){return e.replace(/\r\n/g,`
4
- `).trim()}function X(e){if(typeof e!="string")return e;const t=e.trim();if(!t)return"";try{return JSON.parse(t)}catch{return e}}function Y(e,t,r){return JSON.stringify({v:1,type:"tool",name:e,status:r!==void 0?"completed":"running",resultOmitted:!0})}function O(e){return e.replace(/[[\]\\]/g,"\\$&")}function k(e,t="Generated image"){return`![${O(t)}](<${e}>)`}function q(e){const t=n.basename(e)||"generated-image.png";return JSON.stringify({v:1,type:"image",content:k(e,t),attachments:[{path:e,name:t,mimeType:"image/png",kind:"image"}]})}function Q(e){const t=n.extname(e).toLowerCase();return t===".png"?"image/png":t===".jpg"||t===".jpeg"?"image/jpeg":t===".webp"?"image/webp":t===".gif"?"image/gif":"application/octet-stream"}export{q as buildImagePayload,k as buildMarkdownImageRef,Y as buildToolPayload,z as encodeClaudeProjectDir,O as escapeMarkdownLabel,Q as inferMimeType,G as isClaudeSubagentTranscript,w as isSameOrChildPath,V as makeCursor,H as makeEventKey,b as normalizePathForCompare,B as normalizeText,K as normalizeToolText,X as parseStructuredString,F as readClaudeEventCwd,v as readJsonlLines,J as readTimestamp,R as safeParse,W as safeStringify,D as shouldIgnoreClaudeProjectDir,U as shouldIgnoreClaudeTranscriptPath,M as shouldIgnoreNativeScanPath};
1
+ import{createHash as N}from"node:crypto";import m from"node:fs";import C from"node:os";import n from"node:path";import{buildToolArgsSummary as E}from"../session/tool-summary.js";const h=64*1024*1024,x=["/root/.claude-mem/observer-session"],I=x.map(D);function R(e){return w(e.replace(/\r\n/g,`
2
+ `).trim())}const _=/^\s*::git-[a-z-]+\{[^\n]*\}\s*$/gm;function w(e){if(!e)return"";if(!/(^|\n)\s*::git-[a-z-]+\{[^\n]*\}\s*(?=\n|$)/.test(e))return e;const r=e.replace(_,"");return r.trim()?r.replace(/[ \t]+\n/g,`
3
+ `).replace(/\n+$/g,""):""}function v(e){if(typeof e!="string")return null;const t=Date.parse(e);return Number.isFinite(t)?t:null}function F(e){try{const t=JSON.parse(e);return typeof t=="object"&&t!==null?t:null}catch{return null}}function G(e,t,r){const s=m.statSync(e).size;if(t>=s)return s;const y=m.openSync(e,"r");try{const S=Buffer.allocUnsafe(262144);let l=t,p=t,o=Buffer.alloc(0),f=!1;for(;l<s;){const g=m.readSync(y,S,0,Math.min(262144,s-l),l);if(g<=0)break;l+=g;let c=S.subarray(0,g);o.length>0&&(c=Buffer.concat([o,c]),o=Buffer.alloc(0));let u=0;for(;u<c.length;){const a=c.indexOf(10,u);if(a<0)break;const d=c.subarray(u,a),T=p;p+=a-u+1,u=a+1,!f&&d.length>0&&d.length<=h&&r(d.toString("utf8"),T),f=!1}if(u<c.length){const a=c.subarray(u);f||a.length>h?(f=!0,p+=a.length,o=Buffer.alloc(0)):o=Buffer.from(a)}}return!f&&o.length>0&&o.length<=h&&r(o.toString("utf8"),p),s}finally{m.closeSync(y)}}function M(e){return typeof e.cwd=="string"&&e.cwd.trim()?e.cwd:null}function U(e){return n.basename(n.dirname(e))==="subagents"}function b(e){const t=n.resolve(e);return process.platform==="win32"?t.toLowerCase():t}function z(e,t){const r=b(e),i=b(t);return r===i||r.startsWith(i+n.sep)}function D(e){return n.resolve(e).replace(/\//g,"-")}function V(e){return x.some(t=>z(e,t))}function O(e){return I.includes(e)}function H(e){const t=n.join(C.homedir(),".claude","projects"),r=n.relative(t,e);if(!r||r.startsWith("..")||n.isAbsolute(r))return!1;const[i]=r.split(n.sep);return i?O(i):!1}function W(e,t){return`${e}:${t}`}function K(e,t,r){return`${N("sha1").update(e).digest("hex").slice(0,16)}:${t}:${r}`}function X(e){if(e==null)return"";if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}function Y(e){return e.replace(/\r\n/g,`
4
+ `).trim()}function q(e){if(typeof e!="string")return e;const t=e.trim();if(!t)return"";try{return JSON.parse(t)}catch{return e}}function Q(e,t,r){const i=E(t),s={v:1,type:"tool",name:e,status:r!==void 0?"completed":"running",resultOmitted:!0,...i!==void 0?{argsSummary:i}:{}};return JSON.stringify(s)}function k(e){return e.replace(/[[\]\\]/g,"\\$&")}function A(e,t="Generated image"){return`![${k(t)}](<${e}>)`}function Z(e){const t=n.basename(e)||"generated-image.png";return JSON.stringify({v:1,type:"image",content:A(e,t),attachments:[{path:e,name:t,mimeType:"image/png",kind:"image"}]})}function ee(e){const t=n.extname(e).toLowerCase();return t===".png"?"image/png":t===".jpg"||t===".jpeg"?"image/jpeg":t===".webp"?"image/webp":t===".gif"?"image/gif":"application/octet-stream"}export{Z as buildImagePayload,A as buildMarkdownImageRef,Q as buildToolPayload,D as encodeClaudeProjectDir,k as escapeMarkdownLabel,ee as inferMimeType,U as isClaudeSubagentTranscript,z as isSameOrChildPath,W as makeCursor,K as makeEventKey,b as normalizePathForCompare,R as normalizeText,Y as normalizeToolText,q as parseStructuredString,M as readClaudeEventCwd,G as readJsonlLines,v as readTimestamp,F as safeParse,X as safeStringify,O as shouldIgnoreClaudeProjectDir,H as shouldIgnoreClaudeTranscriptPath,V as shouldIgnoreNativeScanPath};
@@ -1,2 +1,2 @@
1
- import F from"node:os";import{createAgent as Q}from"../../agents/adapter.js";import{buildApprovalPendingPayload as Y,buildUserMessagePayload as V}from"@shennian/wire";import{reportLog as T}from"../../log-reporter.js";import{lookupClaudeTranscriptCwd as G}from"../../native-fusion/parsers.js";import{appendMessage as x,recordSession as O}from"../store.js";import{mergeProjectedSessions as X}from"../projection.js";import{buildManagedAgentEnv as Z}from"../../agents/config-status.js";import{materializeRemoteChatAttachments as ee}from"../remote-attachments.js";function te(e){if(!Array.isArray(e))return;const t=e.map(s=>{if(!s||typeof s!="object")return null;const a=s,d=typeof a.path=="string"?a.path:"",l=typeof a.name=="string"?a.name:"",g=typeof a.mimeType=="string"?a.mimeType:"";if(!d||!l||!g)return null;const i=typeof a.previewData=="string"&&a.previewData.trim()?a.previewData.trim():void 0;return{path:d,name:l,mimeType:g,kind:g.startsWith("image/")?"image":"file",...i?{previewData:i}:{}}}).filter(s=>s!=null);return t.length?t:void 0}function ne(e){const t=e.indexOf(`
2
- `),s=t>0?Math.min(t,80):Math.min(e.length,80);return e.slice(0,s)}function ae(e,t,s={}){if(e.state==="tool-call"||e.state==="tool-result"){const a={runId:e.runId,sourceSeq:e.seq};return{state:e.state,runId:e.runId,seq:e.seq,sessionId:t,detailRef:a,...e.name?{name:e.name}:{},...e.source?{source:e.source}:{},...e.agentSessionId?{agentSessionId:e.agentSessionId}:{},...s}}return{...e,sessionId:t,...s}}const se=3e4;function re(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 oe(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function W(e,t){return e!=="manager"?e:t==="claude"?"claude":"codex"}function L(e,t,s){e.client.sendEvent({type:"event",event:"session.message",payload:{sessionId:t.sessionId,message:t,session:{id:t.sessionId,agentType:s.agentType,agentSessionId:s.agentSessionId??null,modelId:s.modelId??null,workDir:s.workDir,status:"active",externalChannel:null}}})}function de(e,t){O({sessionId:t.sessionId,agentType:t.agentType,workDir:t.workDir,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t.sessionId,agentType:t.agentType,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,workDir:t.workDir,status:"active",externalChannel:null}}})}function H(e){return Z(e)}function U(e,t,s){return{}}function j(e,t,s,a,d,l){e.configure?.({sessionId:t,externalChannel:a??null,env:{...H(s),...U(t,a,d),...l?{SHENNIAN_ROOM_REGISTRY_KEY:l}:{}}})}function ie(e,t,s){if(e!=="claude"||!s)return t;const a=t.trim();return!!a&&a.startsWith("-")&&!a.includes("/")?G(s)??t:t}function le(e,t,s,a){let d=null;function l(n,r={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:ae(n,t,r),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function g(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function i(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 f(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{i(n)},se),n.heartbeatTimer.unref?.())}function y(n){const r=n?.pendingTextEvent;!n||!r||!r.text||(l({state:"delta",runId:r.runId,seq:r.seq,text:r.text,thinking:r.thinking||void 0}),n.pendingTextEvent=null)}a.on("agentEvent",n=>{const r=e.sessions.get(t),S=re(n),w=n.state==="final"||n.state==="error"||n.state==="aborted";if(r&&(r.nextEventSeq=n.seq+1,w||(r.currentRunId=n.runId),!w&&S&&(r.currentRunPhase=S,f(r)),n.agentSessionId&&(r.agentSessionId=n.agentSessionId)),e.managerRuntime?.noteAgentEvent(t,n),n.state==="delta"&&n.thinking)return;n.state!=="delta"&&T({level:"info",sessionId:t,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:s}}),n.state==="delta"&&n.text&&!n.thinking?x(t,{id:`agent-${n.runId}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.text}):n.state==="tool-call"||n.state==="tool-result"?x(t,{id:`agent-${n.runId}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:n.state==="tool-call"?"tool_use":"tool_result",name:n.name,status:n.state==="tool-call"?"running":"completed",detailRef:{runId:n.runId,sourceSeq:n.seq},args:n.args,result:n.result})}):n.state==="approval-pending"?x(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:Y(n.approval)}):(n.state==="error"||n.state==="aborted")&&n.message&&x(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.message});const I=`${t}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&e.runTextAcc.set(I,(e.runTextAcc.get(I)??"")+n.text),n.agentSessionId&&n.agentSessionId!==d&&(d=n.agentSessionId,e.nativeFusion?.noteManagedSourceSession(t,W(s,n.source),n.agentSessionId),r&&O({sessionId:t,agentType:s,workDir:r.workDir,agentSessionId:n.agentSessionId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t,agentType:s,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const m=n.text??"";if(!m)return;r?.pendingTextEvent&&r.pendingTextEvent.runId!==n.runId&&y(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 R={};if(n.state==="final"){y(r);const m=e.runTextAcc.get(I)??"";m&&(R={messageSummary:ne(m)}),e.runTextAcc.delete(I)}else n.state==="error"||n.state==="aborted"?(y(r),e.runTextAcc.delete(I)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&y(r);w&&r?.currentRunId===n.runId&&(r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,g(r),e.chatQueue?.noteTerminal(t)),l(n,R)}),a.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${t} agentType=${s}: ${n.message}`),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 ce(e,t){if(e.processedReqIds.add(t),e.processedReqIds.size>1e3){const s=e.processedReqIds.values().next().value;e.processedReqIds.delete(s)}}async function P(e){e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{})}function B(e,t,s,a,d,l,g){return g?e.send(t,s,a,d,l,g):l?.trim()?e.send(t,s,a,d,l):d?.length?e.send(t,s,a,d):a?e.send(t,s,a):e.send(t,s)}async function ue(e,t,s,a,d,l,g){e.evictIdleSessions();const i=Q(s);if(!i)throw new Error(`Unsupported agent: ${s}`);j(i,t,s,l,g),await i.start(t,a,d);const f={adapter:i,workDir:a,agentType:s,agentSessionId:d??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:l??null,externalReplyTarget:g??null,externalChannelEnv:{...H(s),...U(t,l,g)}};return e.sessions.set(t,f),le(e,t,s,i),f}function ge(e,t){const s=e.sessions.get(t),a=s?.currentRunId;if(!s||!a)return;const d=s.nextEventSeq;e.runTextAcc.delete(`${t}:${a}`),s.pendingTextEvent=null,s.currentRunId=null,s.currentRunPhase=null,s.nextEventSeq=0,s.heartbeatTimer&&(clearInterval(s.heartbeatTimer),s.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:t,runId:a,seq:d},seq:d,id:`agent-evt-${a}-${d}`}),e.chatQueue?.noteTerminal(t)}async function xe(e,t,s){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}ce(e,t.id);const{sessionId:a,text:d,agentType:l,workDir:g,agentSessionId:i,modelId:f,systemPrompt:y,managerDefaultWorkerAgentType:n,managerDefaultWorkerModelId:r,reasoningEffort:S,clientMessageId:w,sessionListProjection:I,waitForDispatch:R,responseId:m,roomContextRegistryKey:$,suppressExternalOwnerEcho:J}=t.params,A=m||t.id;X(I);const b=null,M="";if(!a||!d){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:A,ok:!1,error:"sessionId and text are required"});return}const u=l;u==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(a,n??null,r??null);const q=(u==="claude"||u==="codex"||u==="workbuddy")&&typeof S=="string"&&S.trim()?S.trim():void 0,E=e.resolvePath(ie(u,g||F.homedir(),i)||F.homedir()),D=E,_=te(t.params.attachments),p=_?.length?await ee({text:d,attachments:_,workDir:E}):{text:d,attachments:_,localized:!1};let o=e.sessions.get(a);if(o){if(o.lastActiveAt=Date.now(),o.agentType!==u||o.workDir!==E||JSON.stringify(o.externalChannel??null)!==JSON.stringify(b??null)){e.sessions.delete(a);try{await P(o)}catch{e.processedReqIds.delete(t.id)}o=void 0}else if(i&&o.agentSessionId!==i)try{await o.adapter.resume(i),o.agentSessionId=i}catch{e.sessions.delete(a);try{await P(o)}catch{e.processedReqIds.delete(t.id)}o=void 0}}if(!o)try{o=await ue(e,a,u,E,i,b,M)}catch(c){const h=c instanceof Error&&c.message.startsWith("Unsupported agent:")?c.message:`Failed to start ${l}: ${c instanceof Error?c.message:String(c)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${a} agentType=${l} workDir=${E} agentSessionId=${i??""}: ${h}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:a,message:h,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:A,ok:!1,error:h});return}$&&/^[a-f0-9]{64}$/.test($)&&j(o.adapter,a,u,b,M,$);const C={id:w??`user-${t.id}`,sessionId:a,role:"user",ts:Date.now(),payload:V(p.text,p.attachments)};T({level:"info",sessionId:a,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:u,modelId:f,reasoningEffort:q}});const z=c=>{de(e,{sessionId:a,agentType:u,workDir:D,agentSessionId:o.agentSessionId??i??null,modelId:f}),o.currentRunId||(o.nextEventSeq=0),e.nativeFusion?.registerManagedSend({sessionId:a,agentType:u,sourceAgentType:W(u,f),canonicalMessageId:w??null,sourceSessionKey:o.agentSessionId??i??null,text:p.text,managedEchoPolicy:c?.deliveryMode==="external_owner_queue"&&!J?"import_following":"suppress_following"}),x(a,C),L(e,C,{agentType:u,workDir:D,agentSessionId:o.agentSessionId??i??null,modelId:f})},v=async(c,h)=>{const k=oe(u,c);console.error(`[chat.send] send failed reqId=${t.id} sessionId=${a} agentType=${l} workDir=${E} agentSessionId=${o.agentSessionId??i??""}: ${k}`),e.sessions.delete(a);try{await P(o)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:a,message:k,runId:"",seq:0}}),!h){const N={id:`agent-error-${t.id}-${Date.now()}`,sessionId:a,role:"agent",ts:Date.now(),payload:k};x(a,N),L(e,N,{agentType:u,workDir:D,agentSessionId:o.agentSessionId??i??null,modelId:f})}h&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:A,ok:!1,error:k}))};if(R){let c;try{const h=p.attachments;c=await B(o.adapter,p.text,f,q,h,y,s),T({level:"info",sessionId:a,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(h){await v(h,!0);return}z(c??void 0),e.client.sendRes({type:"res",id:A,ok:!0,...p.localized||c?.deliveryMode==="external_owner_queue"?{payload:{...p.localized?{localizedAttachments:!0}:{},...c?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:c.deliveryMode,queuedSubmissionId:c.queuedSubmissionId}:{}}}:{}}),T({level:"info",sessionId:a,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}z(),e.client.sendRes({type:"res",id:A,ok:!0,...p.localized?{payload:{localizedAttachments:!0}}:{}}),T({level:"info",sessionId:a,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const K=p.attachments;B(o.adapter,p.text,f,q,K,y,s).then(()=>{T({level:"info",sessionId:a,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(c=>{v(c,!1)})}async function Ae(e,t){const{sessionId:s}=t.params,a=e.sessions.get(s);if(a){try{await a.adapter.stop()}catch{}ge(e,s),e.activityPublisher?.publish(s,null)}e.client.sendRes({type:"res",id:t.id,ok:!0})}export{Ae as handleChatAbort,xe as handleChatSend,de as sendSessionUpdateEvent};
1
+ import F from"node:os";import{createAgent as Q}from"../../agents/adapter.js";import{buildApprovalPendingPayload as Y,buildUserMessagePayload as V}from"@shennian/wire";import{reportLog as T}from"../../log-reporter.js";import{lookupClaudeTranscriptCwd as G}from"../../native-fusion/parsers.js";import{appendMessage as x,recordSession as O}from"../store.js";import{mergeProjectedSessions as X}from"../projection.js";import{buildManagedAgentEnv as Z}from"../../agents/config-status.js";import{materializeRemoteChatAttachments as ee}from"../remote-attachments.js";import{buildToolArgsSummary as te}from"../tool-summary.js";function ne(e){if(!Array.isArray(e))return;const t=e.map(r=>{if(!r||typeof r!="object")return null;const a=r,o=typeof a.path=="string"?a.path:"",l=typeof a.name=="string"?a.name:"",g=typeof a.mimeType=="string"?a.mimeType:"";if(!o||!l||!g)return null;const i=typeof a.previewData=="string"&&a.previewData.trim()?a.previewData.trim():void 0;return{path:o,name:l,mimeType:g,kind:g.startsWith("image/")?"image":"file",...i?{previewData:i}:{}}}).filter(r=>r!=null);return t.length?t:void 0}function ae(e){const t=e.indexOf(`
2
+ `),r=t>0?Math.min(t,80):Math.min(e.length,80);return e.slice(0,r)}function re(e,t,r={}){if(e.state==="tool-call"||e.state==="tool-result"){const a={runId:e.runId,sourceSeq:e.seq},o=te(e.args);return{state:e.state,runId:e.runId,seq:e.seq,sessionId:t,detailRef:a,...e.name?{name:e.name}:{},...o!==void 0?{argsSummary:o}:{},...e.source?{source:e.source}:{},...e.agentSessionId?{agentSessionId:e.agentSessionId}:{},...r}}return{...e,sessionId:t,...r}}const se=3e4;function oe(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 de(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function W(e,t){return e!=="manager"?e:t==="claude"?"claude":"codex"}function L(e,t,r){e.client.sendEvent({type:"event",event:"session.message",payload:{sessionId:t.sessionId,message:t,session:{id:t.sessionId,agentType:r.agentType,agentSessionId:r.agentSessionId??null,modelId:r.modelId??null,workDir:r.workDir,status:"active",externalChannel:null}}})}function ie(e,t){O({sessionId:t.sessionId,agentType:t.agentType,workDir:t.workDir,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t.sessionId,agentType:t.agentType,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,workDir:t.workDir,status:"active",externalChannel:null}}})}function H(e){return Z(e)}function U(e,t,r){return{}}function j(e,t,r,a,o,l){e.configure?.({sessionId:t,externalChannel:a??null,env:{...H(r),...U(t,a,o),...l?{SHENNIAN_ROOM_REGISTRY_KEY:l}:{}}})}function le(e,t,r){if(e!=="claude"||!r)return t;const a=t.trim();return!!a&&a.startsWith("-")&&!a.includes("/")?G(r)??t:t}function ce(e,t,r,a){let o=null;function l(n,s={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:re(n,t,s),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function g(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function i(n){if(!n.currentRunId||!n.currentRunPhase)return;const s=n.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:t,runId:n.currentRunId,seq:s,runPhase:n.currentRunPhase},seq:s,id:`agent-heartbeat-${n.currentRunId}-${s}-${Date.now()}`})}function f(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{i(n)},se),n.heartbeatTimer.unref?.())}function y(n){const s=n?.pendingTextEvent;!n||!s||!s.text||(l({state:"delta",runId:s.runId,seq:s.seq,text:s.text,thinking:s.thinking||void 0}),n.pendingTextEvent=null)}a.on("agentEvent",n=>{const s=e.sessions.get(t),S=oe(n),w=n.state==="final"||n.state==="error"||n.state==="aborted";if(s&&(s.nextEventSeq=n.seq+1,w||(s.currentRunId=n.runId),!w&&S&&(s.currentRunPhase=S,f(s)),n.agentSessionId&&(s.agentSessionId=n.agentSessionId)),e.managerRuntime?.noteAgentEvent(t,n),n.state==="delta"&&n.thinking)return;n.state!=="delta"&&T({level:"info",sessionId:t,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:r}}),n.state==="delta"&&n.text&&!n.thinking?x(t,{id:`agent-${n.runId}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.text}):n.state==="tool-call"||n.state==="tool-result"?x(t,{id:`agent-${n.runId}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:n.state==="tool-call"?"tool_use":"tool_result",name:n.name,status:n.state==="tool-call"?"running":"completed",detailRef:{runId:n.runId,sourceSeq:n.seq},args:n.args,result:n.result})}):n.state==="approval-pending"?x(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:Y(n.approval)}):(n.state==="error"||n.state==="aborted")&&n.message&&x(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.message});const m=`${t}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&e.runTextAcc.set(m,(e.runTextAcc.get(m)??"")+n.text),n.agentSessionId&&n.agentSessionId!==o&&(o=n.agentSessionId,e.nativeFusion?.noteManagedSourceSession(t,W(r,n.source),n.agentSessionId),s&&O({sessionId:t,agentType:r,workDir:s.workDir,agentSessionId:n.agentSessionId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t,agentType:r,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const I=n.text??"";if(!I)return;s?.pendingTextEvent&&s.pendingTextEvent.runId!==n.runId&&y(s),s&&!s.pendingTextEvent&&(s.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:!1}),s?.pendingTextEvent&&(s.pendingTextEvent.text+=I,s.pendingTextEvent.seq=n.seq);return}let R={};if(n.state==="final"){y(s);const I=e.runTextAcc.get(m)??"";I&&(R={messageSummary:ae(I)}),e.runTextAcc.delete(m)}else n.state==="error"||n.state==="aborted"?(y(s),e.runTextAcc.delete(m)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&y(s);w&&s?.currentRunId===n.runId&&(s.currentRunId=null,s.currentRunPhase=null,s.nextEventSeq=0,g(s),e.chatQueue?.noteTerminal(t)),l(n,R)}),a.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${t} agentType=${r}: ${n.message}`),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 ue(e,t){if(e.processedReqIds.add(t),e.processedReqIds.size>1e3){const r=e.processedReqIds.values().next().value;e.processedReqIds.delete(r)}}async function P(e){e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{})}function B(e,t,r,a,o,l,g){return g?e.send(t,r,a,o,l,g):l?.trim()?e.send(t,r,a,o,l):o?.length?e.send(t,r,a,o):a?e.send(t,r,a):e.send(t,r)}async function ge(e,t,r,a,o,l,g){e.evictIdleSessions();const i=Q(r);if(!i)throw new Error(`Unsupported agent: ${r}`);j(i,t,r,l,g),await i.start(t,a,o);const f={adapter:i,workDir:a,agentType:r,agentSessionId:o??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:l??null,externalReplyTarget:g??null,externalChannelEnv:{...H(r),...U(t,l,g)}};return e.sessions.set(t,f),ce(e,t,r,i),f}function fe(e,t){const r=e.sessions.get(t),a=r?.currentRunId;if(!r||!a)return;const o=r.nextEventSeq;e.runTextAcc.delete(`${t}:${a}`),r.pendingTextEvent=null,r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,r.heartbeatTimer&&(clearInterval(r.heartbeatTimer),r.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:t,runId:a,seq:o},seq:o,id:`agent-evt-${a}-${o}`}),e.chatQueue?.noteTerminal(t)}async function Re(e,t,r){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}ue(e,t.id);const{sessionId:a,text:o,agentType:l,workDir:g,agentSessionId:i,modelId:f,systemPrompt:y,managerDefaultWorkerAgentType:n,managerDefaultWorkerModelId:s,reasoningEffort:S,clientMessageId:w,sessionListProjection:m,waitForDispatch:R,responseId:I,roomContextRegistryKey:$,suppressExternalOwnerEcho:J}=t.params,A=I||t.id;X(m);const b=null,M="";if(!a||!o){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:A,ok:!1,error:"sessionId and text are required"});return}const u=l;u==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(a,n??null,s??null);const q=(u==="claude"||u==="codex"||u==="workbuddy")&&typeof S=="string"&&S.trim()?S.trim():void 0,E=e.resolvePath(le(u,g||F.homedir(),i)||F.homedir()),D=E,_=ne(t.params.attachments),p=_?.length?await ee({text:o,attachments:_,workDir:E}):{text:o,attachments:_,localized:!1};let d=e.sessions.get(a);if(d){if(d.lastActiveAt=Date.now(),d.agentType!==u||d.workDir!==E||JSON.stringify(d.externalChannel??null)!==JSON.stringify(b??null)){e.sessions.delete(a);try{await P(d)}catch{e.processedReqIds.delete(t.id)}d=void 0}else if(i&&d.agentSessionId!==i)try{await d.adapter.resume(i),d.agentSessionId=i}catch{e.sessions.delete(a);try{await P(d)}catch{e.processedReqIds.delete(t.id)}d=void 0}}if(!d)try{d=await ge(e,a,u,E,i,b,M)}catch(c){const h=c instanceof Error&&c.message.startsWith("Unsupported agent:")?c.message:`Failed to start ${l}: ${c instanceof Error?c.message:String(c)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${a} agentType=${l} workDir=${E} agentSessionId=${i??""}: ${h}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:a,message:h,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:A,ok:!1,error:h});return}$&&/^[a-f0-9]{64}$/.test($)&&j(d.adapter,a,u,b,M,$);const C={id:w??`user-${t.id}`,sessionId:a,role:"user",ts:Date.now(),payload:V(p.text,p.attachments)};T({level:"info",sessionId:a,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:u,modelId:f,reasoningEffort:q}});const z=c=>{ie(e,{sessionId:a,agentType:u,workDir:D,agentSessionId:d.agentSessionId??i??null,modelId:f}),d.currentRunId||(d.nextEventSeq=0),e.nativeFusion?.registerManagedSend({sessionId:a,agentType:u,sourceAgentType:W(u,f),canonicalMessageId:w??null,sourceSessionKey:d.agentSessionId??i??null,text:p.text,managedEchoPolicy:c?.deliveryMode==="external_owner_queue"&&!J?"import_following":"suppress_following"}),x(a,C),L(e,C,{agentType:u,workDir:D,agentSessionId:d.agentSessionId??i??null,modelId:f})},N=async(c,h)=>{const k=de(u,c);console.error(`[chat.send] send failed reqId=${t.id} sessionId=${a} agentType=${l} workDir=${E} agentSessionId=${d.agentSessionId??i??""}: ${k}`),e.sessions.delete(a);try{await P(d)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:a,message:k,runId:"",seq:0}}),!h){const v={id:`agent-error-${t.id}-${Date.now()}`,sessionId:a,role:"agent",ts:Date.now(),payload:k};x(a,v),L(e,v,{agentType:u,workDir:D,agentSessionId:d.agentSessionId??i??null,modelId:f})}h&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:A,ok:!1,error:k}))};if(R){let c;try{const h=p.attachments;c=await B(d.adapter,p.text,f,q,h,y,r),T({level:"info",sessionId:a,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(h){await N(h,!0);return}z(c??void 0),e.client.sendRes({type:"res",id:A,ok:!0,...p.localized||c?.deliveryMode==="external_owner_queue"?{payload:{...p.localized?{localizedAttachments:!0}:{},...c?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:c.deliveryMode,queuedSubmissionId:c.queuedSubmissionId}:{}}}:{}}),T({level:"info",sessionId:a,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}z(),e.client.sendRes({type:"res",id:A,ok:!0,...p.localized?{payload:{localizedAttachments:!0}}:{}}),T({level:"info",sessionId:a,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const K=p.attachments;B(d.adapter,p.text,f,q,K,y,r).then(()=>{T({level:"info",sessionId:a,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(c=>{N(c,!1)})}async function ke(e,t){const{sessionId:r}=t.params,a=e.sessions.get(r);if(a){try{await a.adapter.stop()}catch{}fe(e,r),e.activityPublisher?.publish(r,null)}e.client.sendRes({type:"res",id:t.id,ok:!0})}export{ke as handleChatAbort,Re as handleChatSend,ie as sendSessionUpdateEvent};
@@ -0,0 +1 @@
1
+ export declare function buildToolArgsSummary(value: unknown): unknown;
@@ -0,0 +1 @@
1
+ const o=240;function c(t){if(t==null)return;if(!t||typeof t!="object"||Array.isArray(t))return r(t);const n={};for(const[i,e]of Object.entries(t))if(!(e==null||e==="")&&(n[i]=r(e),Object.keys(n).length===2))break;return Object.keys(n).length?n:void 0}function r(t){if(typeof t=="string")return s(t)?"[SYSTEM CONTEXT REDACTED]":t.replace(/\s+/g," ").trim().slice(0,240);const n=JSON.stringify(t);return String(n??t).replace(/\s+/g," ").slice(0,240)}function s(t){const n=t.trim();return/^<environment_context>[\s\S]*<\/environment_context>$/i.test(n)||/^# AGENTS\.md instructions for [^\n]+[\s\S]*<INSTRUCTIONS>[\s\S]*<\/INSTRUCTIONS>/i.test(n)||/^<turn_aborted>[\s\S]*<\/turn_aborted>$/i.test(n)}export{c as buildToolArgsSummary};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.3.34",
3
+ "version": "0.3.36",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {