@messenger-agent/codex-agent 0.37.3-alpha.0 → 0.38.0-alpha.1
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/app-server-protocol.d.ts +1 -0
- package/dist/codex.js +1 -1
- package/dist/routes/chat.js +2 -2
- package/package.json +2 -2
package/dist/codex.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import{mkdirSync as k,existsSync as P}from"node:fs";import{appServerClient as l}from"./app-server-client.js";import{dynamicToolSpecs as E}from"./dynamic-tools.js";import{loadAllSessions as U,persistSession as _}from"./db.js";import{appConfig as p}from"./config.js";import{logger as I}from"@messenger-agent/shared/logger";import{resolveAgentWorkdir as N}from"@messenger-agent/shared/agent-workspaces";import{loadCodexPlatformInstructions as C}from"./platform-instructions.js";const v=C(void 0,p.schedule?.enabled===!0);class T extends Error{path;constructor(e){super(`Workdir not found: ${e}`),this.path=e}}class x{id;model;workingDirectory;constructor(e,t,n){this.id=e,this.model=t,this.workingDirectory=n}async run(e,t){const n=await this.runStreamed(e,t),r=new Map,o=new Set;let a=null;for await(const u of n.events)if(u.type==="item.started")o.has(u.item.id)||r.set(u.item.id,u.item);else if(u.type==="item.completed")o.add(u.item.id),r.set(u.item.id,u.item);else if(u.type==="thread.tokenUsage.updated")a=u.usage;else if(u.type==="turn.failed")throw new Error(u.error.message);const d=Array.from(r.values()),i=d.filter(u=>u.type==="agentMessage").map(u=>u.text).join(`
|
|
2
2
|
`);return{items:d,finalResponse:i||void 0,usage:a}}async runStreamed(e,t){const n=l.request("turn/start",this.turnStartParams(e,t)),r=await this.waitForTurnStart(n,t?.signal),o=r.turn.id;return{turnId:o,events:this.eventsForTurn(o,r.turn,t?.signal)}}async waitForTurnStart(e,t){if(!t)return e;let n,r;const o=new Promise((d,i)=>{r=i}),a=()=>{n=t.reason??new DOMException("The operation was aborted","AbortError"),r(n)};t.addEventListener("abort",a,{once:!0}),t.aborted&&a();try{return await Promise.race([e,o])}catch(d){throw d===n&&e.then(i=>this.interrupt(i.turn.id)).catch(i=>I.error("Failed to clean up aborted Codex turn start:",i)),d}finally{t.removeEventListener("abort",a)}}async steerStreamed(e,t,n){const r={threadId:this.id,input:[{type:"text",text:e}],expectedTurnId:t},o=await l.request("turn/steer",r);return{turnId:o.turnId,events:this.eventsForTurn(o.turnId,void 0,n?.signal,void 0,!0)}}async continueWithUserInput(e,t,n){const r=l.pendingUserInputTurnId(this.id,e);return r?{turnId:r,events:this.eventsForTurn(r,void 0,n?.signal,async()=>{if(!await l.respondToUserInput(this.id,e,t))throw new Error(`No pending user input request found for item: ${e}`)})}:(await this.interruptStaleTurn(n?.sourceTurnId),this.runStreamed(A(e,t),n))}async submitPendingUserInput(e,t){const n=l.pendingUserInputTurnId(this.id,e);if(!(!n||!await l.respondToUserInput(this.id,e,t)))return n}async interrupt(e){await l.request("turn/interrupt",{threadId:this.id,turnId:e})}async interruptPendingUserInput(e,t){await this.interrupt(e),l.discardPendingUserInput(this.id,t)}async interruptStaleTurn(e){if(!e)return;(await l.request("thread/read",{threadId:this.id,includeTurns:!0})).thread.turns?.find(r=>r.id===e)?.status==="inProgress"&&await this.interrupt(e)}turnStartParams(e,t){return{threadId:this.id,input:[{type:"text",text:e,text_elements:[]}],cwd:this.workingDirectory,approvalPolicy:"never",model:this.model,...t?.collaborationMode?{collaborationMode:t.collaborationMode}:null}}async*eventsForTurn(e,t,n,r,o=!1){const a=[...t||o?[{type:"thread.started",thread_id:this.id},{type:"turn.started",turn_id:e}]:[],...t?M(t):[]];let d=t?.status==="completed"||t?.status==="failed"||t?.status==="interrupted",i,u=!1,c=()=>{};const f=({method:h,params:w})=>{if(u)return;const g=R(h,w,this.id,e);g&&(a.push(g),(g.type==="turn.completed"||g.type==="turn.failed")&&(d=!0),c())},y=h=>{i=h,d=!0,c()},m=()=>{u=!0,a.length=0,d=!0,c()};l.on("notification",f),l.on("exit",y),n?.addEventListener("abort",m,{once:!0}),n?.aborted&&m();try{for(await r?.();!d||a.length>0;){for(;a.length>0;)yield a.shift();if(d)break;await new Promise(h=>{const w=setTimeout(()=>{c=()=>{},h()},50);c=()=>{clearTimeout(w),c=()=>{},h()}})}i&&(yield{type:"error",message:i.message})}finally{l.off("notification",f),l.off("exit",y),n?.removeEventListener("abort",m)}}}function A(s,e){return[`The user answered a previous request_user_input prompt (${s}), but the original pending tool request is no longer active.`,"Continue the task using this answer:",JSON.stringify(e,null,2)].join(`
|
|
3
|
-
`)}class F{sessions=new Map;constructor(){for(const e of U())this.sessions.set(e.sessionId,{workdir:e.workdir,botName:e.botName??"main",llmProxyBinding:e.llmProxyBinding});I.info(`SessionManager: loaded ${this.sessions.size} persisted sessions`)}async getOrCreate(e,t,n,r,o,a,d){const i=this.resolveWorkdir(n,r);if(e){const c=this.sessions.get(e);if(c&&c.workdir===i){const f=c.llmProxyBinding??d,y=await l.request("thread/resume",this.threadResumeParams(e,o,i,a,f));if(!c.llmProxyBinding&&f){const m={...c,llmProxyBinding:f};this.sessions.set(e,m),_({sessionId:e,botName:m.botName,workdir:m.workdir,createdAt:Date.now(),llmProxyBinding:f})}return{thread:new x(y.thread.id,o,i),sessionId:e,isNewSession:!1}}}if(n&&!P(i))throw new T(i);k(i,{recursive:!0});const u=await l.request("thread/start",this.threadStartParams(o,i,a,d));return{thread:new x(u.thread.id,o,i),sessionId:void 0,isNewSession:!0}}async fork(e,t,n,r,o,a,d){const i=this.resolveWorkdir(n,r);if(n&&!P(i))throw new T(i);k(i,{recursive:!0});const c=(await l.request("thread/fork",this.threadForkParams(e.threadId,e.turnId,o,i,a,d))).thread.id;return this.persist(c,t,n,r,d),{thread:new x(c,o,i),sessionId:c,isNewSession:!1}}persist(e,t,n,r,o){const a=t??"main",d=this.resolveWorkdir(n,r);this.sessions.set(e,{botName:a,workdir:d,llmProxyBinding:o}),_({sessionId:e,botName:a,workdir:d,createdAt:Date.now(),llmProxyBinding:o})}bindingFor(e){return e?this.sessions.get(e)?.llmProxyBinding:void 0}workdirFor(e){return e?this.sessions.get(e)?.workdir:void 0}hasSession(e){return!!e&&this.sessions.has(e)}resolveWorkdir(e,t){return N(p,t,e)}threadStartParams(e,t,n,r){return{model:e,modelProvider:p.codex.baseUrl?"custom":null,cwd:t,approvalPolicy:"never",sandbox:n,config:this.codexConfig(r),developerInstructions:v,dynamicTools:E(p.schedule?.enabled===!0),experimentalRawEvents:!1,persistExtendedHistory:!1}}threadResumeParams(e,t,n,r,o){return{threadId:e,model:t,modelProvider:p.codex.baseUrl?"custom":null,cwd:n,approvalPolicy:"never",sandbox:r,config:this.codexConfig(o),developerInstructions:v,excludeTurns:!0,persistExtendedHistory:!1}}threadForkParams(e,t,n,r,o,a){return{threadId:e,lastTurnId:t,model:n,modelProvider:p.codex.baseUrl?"custom":null,cwd:r,approvalPolicy:"never",sandbox:o,config:this.codexConfig(a),developerInstructions:v,ephemeral:!1,excludeTurns:!0}}codexConfig(e){return{approval_policy:"never",...p.codex.baseUrl?{model_provider:"custom",model_reasoning_effort:"medium",disable_response_storage:!0,model_providers:{custom:{name:"custom",base_url:p.codex.baseUrl,wire_api:"responses",...p.codex.apiKey?{env_key:"OPENAI_API_KEY"}:null,...e?{http_headers:{"X-Elevo-LLM-Binding":e}}:null}},"features.default_mode_request_user_input":!1}:null}}}p.codex.apiKey&&!process.env.OPENAI_API_KEY&&(process.env.OPENAI_API_KEY=p.codex.apiKey);function M(s){const e=[];for(const t of s.items??[])e.push({type:"item.completed",item:t});return s.status==="completed"?e.push({type:"turn.completed"}):s.status==="failed"&&e.push({type:"turn.failed",error:{message:s.error?.message??"Codex turn failed"}}),e}function R(s,e,t,n){const r=e;if(!r)return;const o=r.threadId,a=r.turnId;if(!(typeof o=="string"&&o!==t)&&!(typeof a=="string"&&a!==n)){if(s==="item/started"&&S(r.item))return{type:"item.started",item:r.item};if(s==="item/completed"&&S(r.item))return{type:"item.completed",item:r.item};if(s==="item/agentMessage/delta")return b(r,"agentMessage");if(s==="item/reasoning/summaryTextDelta"||s==="item/reasoning/textDelta")return b(r,"reasoning");if(s==="item/plan/delta")return b(r,"plan");if(s==="item/tool/requestUserInput")return{type:"item.tool.requestUserInput",params:r};if(s==="turn/diff/updated"&&typeof r.diff=="string")return{type:"turn.diff.updated",diff:r.diff};if(s==="thread/tokenUsage/updated")return{type:"thread.tokenUsage.updated",usage:D(r)};if(s==="turn/completed"){const d=r.turn;return d?.status==="failed"?{type:"turn.failed",error:{message:d.error?.message??"Codex turn failed"}}:{type:"turn.completed"}}if(s==="error")return console.error("Received error notification from app-server:"
|
|
3
|
+
`)}class F{sessions=new Map;constructor(){for(const e of U())this.sessions.set(e.sessionId,{workdir:e.workdir,botName:e.botName??"main",llmProxyBinding:e.llmProxyBinding});I.info(`SessionManager: loaded ${this.sessions.size} persisted sessions`)}async getOrCreate(e,t,n,r,o,a,d){const i=this.resolveWorkdir(n,r);if(e){const c=this.sessions.get(e);if(c&&c.workdir===i){const f=c.llmProxyBinding??d,y=await l.request("thread/resume",this.threadResumeParams(e,o,i,a,f));if(!c.llmProxyBinding&&f){const m={...c,llmProxyBinding:f};this.sessions.set(e,m),_({sessionId:e,botName:m.botName,workdir:m.workdir,createdAt:Date.now(),llmProxyBinding:f})}return{thread:new x(y.thread.id,o,i),sessionId:e,isNewSession:!1}}}if(n&&!P(i))throw new T(i);k(i,{recursive:!0});const u=await l.request("thread/start",this.threadStartParams(o,i,a,d));return{thread:new x(u.thread.id,o,i),sessionId:void 0,isNewSession:!0}}async fork(e,t,n,r,o,a,d){const i=this.resolveWorkdir(n,r);if(n&&!P(i))throw new T(i);k(i,{recursive:!0});const c=(await l.request("thread/fork",this.threadForkParams(e.threadId,e.turnId,o,i,a,d))).thread.id;return this.persist(c,t,n,r,d),{thread:new x(c,o,i),sessionId:c,isNewSession:!1}}persist(e,t,n,r,o){const a=t??"main",d=this.resolveWorkdir(n,r);this.sessions.set(e,{botName:a,workdir:d,llmProxyBinding:o}),_({sessionId:e,botName:a,workdir:d,createdAt:Date.now(),llmProxyBinding:o})}bindingFor(e){return e?this.sessions.get(e)?.llmProxyBinding:void 0}workdirFor(e){return e?this.sessions.get(e)?.workdir:void 0}hasSession(e){return!!e&&this.sessions.has(e)}resolveWorkdir(e,t){return N(p,t,e)}threadStartParams(e,t,n,r){return{model:e,modelProvider:p.codex.baseUrl?"custom":null,cwd:t,approvalPolicy:"never",sandbox:n,config:this.codexConfig(r),developerInstructions:v,dynamicTools:E(p.schedule?.enabled===!0),experimentalRawEvents:!1,persistExtendedHistory:!1}}threadResumeParams(e,t,n,r,o){return{threadId:e,model:t,modelProvider:p.codex.baseUrl?"custom":null,cwd:n,approvalPolicy:"never",sandbox:r,config:this.codexConfig(o),developerInstructions:v,excludeTurns:!0,persistExtendedHistory:!1}}threadForkParams(e,t,n,r,o,a){return{threadId:e,lastTurnId:t,model:n,modelProvider:p.codex.baseUrl?"custom":null,cwd:r,approvalPolicy:"never",sandbox:o,config:this.codexConfig(a),developerInstructions:v,ephemeral:!1,excludeTurns:!0}}codexConfig(e){return{approval_policy:"never",...p.codex.baseUrl?{model_provider:"custom",model_reasoning_effort:"medium",disable_response_storage:!0,model_providers:{custom:{name:"custom",base_url:p.codex.baseUrl,wire_api:"responses",...p.codex.apiKey?{env_key:"OPENAI_API_KEY"}:null,...e?{http_headers:{"X-Elevo-LLM-Binding":e}}:null}},"features.default_mode_request_user_input":!1}:null}}}p.codex.apiKey&&!process.env.OPENAI_API_KEY&&(process.env.OPENAI_API_KEY=p.codex.apiKey);function M(s){const e=[];for(const t of s.items??[])e.push({type:"item.completed",item:t});return s.status==="completed"?e.push({type:"turn.completed"}):s.status==="failed"&&e.push({type:"turn.failed",error:{message:s.error?.message??"Codex turn failed"}}),e}function R(s,e,t,n){const r=e;if(!r)return;const o=r.threadId,a=r.turnId;if(!(typeof o=="string"&&o!==t)&&!(typeof a=="string"&&a!==n)){if(s==="item/started"&&S(r.item))return{type:"item.started",item:r.item};if(s==="item/completed"&&S(r.item))return{type:"item.completed",item:r.item};if(s==="item/agentMessage/delta")return b(r,"agentMessage");if(s==="item/reasoning/summaryTextDelta"||s==="item/reasoning/textDelta")return b(r,"reasoning");if(s==="item/plan/delta")return b(r,"plan");if(s==="item/tool/requestUserInput")return{type:"item.tool.requestUserInput",params:r};if(s==="turn/diff/updated"&&typeof r.diff=="string")return{type:"turn.diff.updated",diff:r.diff};if(s==="thread/tokenUsage/updated")return{type:"thread.tokenUsage.updated",usage:D(r)};if(s==="turn/completed"){const d=r.turn;return d?.status==="failed"?{type:"turn.failed",error:{message:d.error?.message??"Codex turn failed"}}:{type:"turn.completed"}}if(s==="error")return console.error("Received error notification from app-server:",r),{type:"error",message:q(r)}}}function q(s){const e=s.error,t=e&&typeof e=="object"&&"message"in e?e.message:void 0,n=s.additionalDetails,r=typeof t=="string"&&t.trim()?t:"Codex app-server error";return typeof n=="string"&&n.trim()?`${r}: ${n}`:r}function b(s,e){if(!(typeof s.itemId!="string"||typeof s.delta!="string"))return{type:"item.delta",item_id:s.itemId,item_type:e,delta:s.delta}}function D(s){const e=s.tokenUsage;if(!e||typeof e!="object")return null;const t=e,n=t.total,r=t.last;if(!n||typeof n!="object"||!r||typeof r!="object")return null;const o=d=>{const i=d;return{input_tokens:typeof i.inputTokens=="number"?i.inputTokens:void 0,output_tokens:typeof i.outputTokens=="number"?i.outputTokens:void 0,total_tokens:typeof i.totalTokens=="number"?i.totalTokens:void 0}},a=t.modelContextWindow;return{total:o(n),last:o(r),model_context_window:typeof a=="number"?a:void 0}}function S(s){return!!(s&&typeof s=="object"&&"type"in s&&"id"in s)}const H=new F;export{x as CodexThread,T as WorkdirNotFoundError,H as sessionManager};
|
package/dist/routes/chat.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Hono as Jt}from"hono";import{HTTPException as U}from"hono/http-exception";import{zValidator as Qt}from"@hono/zod-validator";import{streamSSE as Vt}from"hono/streaming";import{randomUUID as Gt}from"node:crypto";import{join as Yt}from"node:path";import{sessionManager as z,WorkdirNotFoundError as Zt}from"../codex.js";import{logger as H}from"@messenger-agent/shared/logger";import{ChatBodySchema as te,RequestUserInputToolPartSchema as ee}from"../schemas.js";import{appConfig as ot}from"../config.js";import{setManagedTaskToolContext as ae}from"../dynamic-tools.js";import{resolveAgentWorkspaceRoot as ne}from"@messenger-agent/shared/agent-workspaces";import{saveUploadedFilePart as re}from"@messenger-agent/shared/uploaded-files";import{WakeupIdempotencyStore as oe}from"@messenger-agent/shared/wakeup-idempotency";import{commandExecutionItem as Tt,addTokenUsage as ie,displayCommand as se,dynamicToolCallItem as St,dynamicToolCallOutput as de,fileChangeItem as kt,itemText as Et,mcpToolCallItem as Rt,mcpToolCallOutput as ue,toUsage as le,turnOptions as ce,webSearchItem as _t,webSearchPayload as Ot}from"./shared.js";const K=new Jt;let pe;function it(){return pe??=new oe(Yt(ot.dataDir,"wakeup-idempotency.sqlite"))}const m=new Map,J=new Set,st=new Map,_=new Map;function je(){const t=new Set([..._.values()].map(
|
|
2
|
-
`).trim();if(!r)throw new U(400,{message:"No supported message content found"});return r}function Te(t){if(!t.some(o=>o.type==="tool"))return;if(t.length!==1)throw new U(400,{message:"When a tool part is present, the message must contain exactly one part"});const r=ee.safeParse(t[0]);if(!r.success)throw new U(400,{message:`Invalid requestUserInput tool part: ${r.error.message}`});return{itemId:r.data.toolCallId,response:r.data.output,sourceTurnId:r.data.sourceTurnId}}function w(t,e,r,o){const i=[e,r,o].filter(Boolean).join(":"),d=t.get(i);if(d)return d;const I=Gt();return t.set(i,I),I}function Se(t,e){return t&&t.length>0?t:e}function ke(t){H.debug("Received chat stream event:",t)}function Ee(t){if(t instanceof Zt||t&&typeof t=="object"&&"path"in t&&typeof t.path=="string")return t.path;if(t instanceof Error&&t.message.startsWith("Workdir not found: "))return t.message.slice(19)}K.post("/v1/chat/:conversation_id/cancel",async t=>{const e=t.req.param("conversation_id");try{return await Mt(e)?t.json({success:!0}):t.json({error:fe(e)},404)}catch(r){const o=r instanceof Error?r.message:"Failed to interrupt Codex conversation";return H.error(`Failed to cancel Codex conversation ${e}:`,r),t.json({error:{type:"server_error",code:"cancel_failed",message:o}},500)}}),K.get("/admin/status",t=>t.json({activeTaskCount:J.size,waitingClientToolCount:_.size,clientToolWaits:[..._.values()].map(({conversationId:e,toolCallId:r,toolName:o,since:i})=>({conversationId:e,toolCallId:r,toolName:o,since:i}))}));async function Lt({body:t,headers:e}){const{conversationId:r,fork:o,model:i,effort:d,message:I}=t,O=h(e,"X-Matrix-Bot-Name"),M=h(e,"X-Matrix-Workdir"),S=h(e,"X-Matrix-Workspace-ID"),At=h(e,"X-Matrix-Agent-Mode"),G=h(e,"X-Elevo-LLM-Binding")?.trim()||void 0,Y=o?void 0:z.bindingFor(r);if(G&&Y&&G!==Y)throw new W(409,"LLM proxy binding does not match the existing conversation");const A=Y??G,$t=me(e),Ft=we(e),Z=Te(I.parts),tt=Z?void 0:await be(I.parts),dt=Z?void 0:Ce(r),c=Z,P=tt&&dt?dt:void 0;let $=!o&&r?Pt(r):void 0;const Xt=(()=>{try{return ne(ot,S)}catch(k){throw new W(400,k instanceof Error?k.message:"Invalid workspace")}})(),{thread:y,isNewSession:jt}=await(async()=>{try{if(o){if(!("threadId"in o))throw new W(400,"Codex fork requires threadId and turnId");return await z.fork(o,O,M,S,i,"danger-full-access",A)}return await z.getOrCreate(r,O,M,S,i,"danger-full-access",A)}catch(k){$&&he($);const q=Ee(k);throw q?new W(400,`Workdir not found: ${q}`):k}})();$??=Pt(y.id),ae(y.id,{workspaceRoot:Xt,threadRef:$t,matrixEvent:Ft,llmProxyBinding:A});const ut=ce(At,i,d);return{run:async(k,q)=>{const p=$,F=p.abortController;J.add(F);const et=()=>{L(p)&&Mt(p.state.conversationId).catch(n=>H.error("Failed to cancel disconnected Codex stream:",n))};q?.aborted&&et(),q?.addEventListener("abort",et,{once:!0});let b=!!c,lt=!!c,ct=!1,l,C=y.id,v,X=!1,N;const pt=new Set,ft=new Set,j=new Set,f=new Map,mt=new Set,Bt=new Set,wt=new Set;let yt,at,gt;const It=()=>{N=setTimeout(()=>{s({type:"heartbeat"}).catch(()=>{})},6e4)},ht=async n=>{N&&clearTimeout(N),n.data==="[DONE]"?await k.writeDone():await k.writeData(JSON.parse(String(n.data))),It()},s=async n=>{await ht({data:JSON.stringify(n)})},B=async()=>{await ht({data:"[DONE]"})},zt=async()=>{const n=Wt(l,y.id),a=st.get(n);a&&(st.delete(n),await s({type:"data",kind:"diff",data:{diff:a}}))},Ct=async(n,a)=>{if(n.type!=="contextCompaction")return!1;if(a==="end"&&!mt.has(n.id))return!0;const u=a==="start"?mt:Bt;return u.has(n.id)||(u.add(n.id),await s({type:"data",kind:"compact",data:a==="start"?{phase:a,activityId:n.id}:{phase:a,activityId:n.id,status:"completed"}})),!0},nt=async n=>{if(n.type!=="subAgentActivity")return!1;if(wt.has(n.id))return!0;wt.add(n.id);const a=w(f,l,n.id),u={activity:n.kind,agentThreadId:n.agentThreadId,...n.agentPath?{agentPath:n.agentPath}:null};return await s({type:"tool-input-available",toolCallId:a,toolName:`subagent_${n.kind}`,...n.agentPath?{title:n.agentPath}:null,input:u}),await s(n.kind==="interrupted"?{type:"tool-output-error",toolCallId:a,errorText:"Subagent interrupted"}:{type:"tool-output-available",toolCallId:a,output:u}),!0};It();const vt=n=>{l!==n&&(l=n,jt&&z.persist(n,O,M,S,A))},E=async(n,a)=>{b||(b=!0,vt(n),await s({type:"start",messageMetadata:{conversationId:n,threadId:n,...a?{turnId:a}:null}}),await s({type:"start-step"}),lt=!0)},rt=async n=>{ct||(ct=!0,lt&&(await zt(),n==="stop"&&at&&await s({type:"data",kind:"token_usage",data:at}),await s({type:"finish-step"})),await s({type:"finish",finishReason:n}),await B())},xt=async n=>{if(pt.has(n.id))return;pt.add(n.id);const a=n.type==="plan"?{type:"plan"}:void 0;await s({type:"text-start",id:n.id,...a?{textMetadata:a}:{}})},bt=async n=>{ft.has(n.id)||(ft.add(n.id),await s({type:"reasoning-start",id:n.id}))},Ht=async n=>{const a=Tt(n);if(a)return await s({type:"tool-input-available",toolCallId:w(f,l,n.id),toolName:"bash",input:se(a.command)}),!0;const u=kt(n);if(u){for(const[T,x]of u.changes.entries())await s({type:"tool-input-available",toolCallId:w(f,l,n.id,String(T)),toolName:x.kind.type==="update"?"Edit":x.kind.type==="add"?"Add":"Delete",title:x.path,input:x});return!0}const g=Rt(n);if(g)return await s({type:"tool-input-available",toolCallId:w(f,l,n.id),toolName:g.tool,input:g.arguments}),!0;const R=St(n);if(R)return await s({type:"tool-input-available",toolCallId:w(f,l,n.id),toolName:R.tool,input:R.arguments}),!0;const D=_t(n);return D?(await s({type:"tool-input-available",toolCallId:w(f,l,n.id),toolName:"web_search",title:D.query,input:Ot(D)}),!0):!1},Kt=async n=>{const a=Tt(n);if(a){const T=a.status==="failed"||a.status==="declined";return await s(T?{type:"tool-output-error",toolCallId:w(f,l,n.id),errorText:Se(a.aggregatedOutput,"Command execution failed")}:{type:"tool-output-available",toolCallId:w(f,l,n.id),output:a.aggregatedOutput??""}),!0}const u=kt(n);if(u){const T=u.status==="failed"||u.status==="declined";for(const[x]of u.changes.entries())await s(T?{type:"tool-output-error",toolCallId:w(f,l,n.id,String(x)),errorText:u.status}:{type:"tool-output-available",toolCallId:w(f,l,n.id,String(x)),output:u.status,preliminary:!1});return!0}const g=Rt(n);if(g){const T=g.status==="failed";return await s(T?{type:"tool-output-error",toolCallId:w(f,l,n.id),errorText:g.error?.message??"MCP tool call failed"}:{type:"tool-output-available",toolCallId:w(f,l,n.id),output:ue(g)}),!0}const R=St(n);if(R){const T=R.status==="failed"||R.success===!1,x=de(R);return await s(T?{type:"tool-output-error",toolCallId:w(f,l,n.id),errorText:x||"Dynamic tool call failed"}:{type:"tool-output-available",toolCallId:w(f,l,n.id),output:x}),!0}const D=_t(n);return D?(await s({type:"tool-output-available",toolCallId:w(f,l,n.id),output:Ot(D)}),!0):!1};try{if(c&&!p.ownsOutput){await qt(p,y,c.itemId,c.response),_.delete(V(r,c.itemId)),await B();return}const n=P?await Ie(p,y,P,tt,ut):c?await ge(p,y,()=>y.continueWithUserInput(c.itemId,c.response,{signal:F.signal,sourceTurnId:c.sourceTurnId})):await ye(p,y,tt,ut);if(c?_.delete(V(r,c.itemId)):P&&_.delete(V(r,P.toolCallId)),Dt(p,y,n.turnId),!p.ownsOutput){await B();return}c?(await s({type:"data",kind:"agent_session",data:{agentType:"codex",threadId:y.id,turnId:n.turnId}}),await s({type:"tool-output-available",toolCallId:c.itemId,toolName:"request_user_input",output:c.response})):P&&await s({type:"tool-output-error",toolCallId:P.toolCallId,toolName:"request_user_input",errorText:"Question interrupted by text response"});for await(const a of n.events)if(a.type!=="item.delta"&&ke(a),a.type==="thread.started")C=a.thread_id,vt(C);else if(a.type==="turn.started")v=a.turn_id,Dt(p,y,v),await E(C,v);else if(a.type==="item.started"){if(b||await E(C,v),await Ct(a.item,"start")||await nt(a.item)||await Ht(a.item))continue;a.item.type==="agentMessage"||a.item.type==="plan"?await xt(a.item):a.item.type==="reasoning"&&await bt(a.item)}else if(a.type==="item.delta")b||await E(C,v),a.item_type==="agentMessage"||a.item_type==="plan"?(j.add(a.item_id),await s({type:"text-delta",id:a.item_id,delta:a.delta})):a.item_type==="reasoning"&&(j.add(a.item_id),await s({type:"reasoning-delta",id:a.item_id,delta:a.delta}));else if(a.type==="item.updated"){if(await nt(a.item))continue}else if(a.type==="item.completed"){if(b||await E(C,v),await Ct(a.item,"end")||await nt(a.item)||await Kt(a.item))continue;if(a.item.type==="agentMessage"||a.item.type==="plan"){await xt(a.item);const u=Et(a.item)??"";u&&!j.has(a.item.id)&&await s({type:"text-delta",id:a.item.id,delta:u}),await s({type:"text-end",id:a.item.id})}else if(a.item.type==="reasoning"){await bt(a.item);const u=Et(a.item)??"";u&&!j.has(a.item.id)&&await s({type:"reasoning-delta",id:a.item.id,delta:u}),await s({type:"reasoning-end",id:a.item.id})}}else if(a.type==="item.tool.requestUserInput"){if(b||await E(C,v),a.params.isBlocking===!1){if(!await qt(p,y,a.params.itemId,{answers:{}}))throw new Error(`Non-blocking user input request is no longer pending: ${a.params.itemId}`);H.info(`Auto-resolved non-blocking request_user_input ${a.params.itemId} for turn ${a.params.turnId}`);continue}_.set(V(l,a.params.itemId),{conversationId:l,toolCallId:a.params.itemId,toolName:"request_user_input",turnId:a.params.turnId,since:new Date().toISOString()}),await s({type:"tool-input-available",toolCallId:a.params.itemId,toolName:"request_user_input",input:a.params}),await B();return}else if(a.type==="turn.diff.updated")b||await E(C,v),st.set(Wt(l,y.id),a.diff);else if(a.type==="thread.tokenUsage.updated"){const u=le(a.usage);if(u){const g=ie(gt,u.last);g&&(gt=g,at={...u,last:g})}}else if(a.type==="turn.failed"){yt!==a.error.message&&await s({type:"error",errorText:a.error.message}),await rt("error"),X=!0,Nt(p);return}else a.type==="error"&&(yt=a.message,await s({type:"error",errorText:a.message}));X=!F.signal.aborted,X&&Nt(p),b||await E(C,v),await rt("stop")}catch(n){const a=n instanceof Error?n.message:"Chat stream failed";b||await E(C,v),await s({type:"error",errorText:a}),await rt("error")}finally{N&&clearTimeout(N),q?.removeEventListener("abort",et),J.delete(F),X||Ut(p)}}}}async function Be({body:t,headers:e,signal:r},o){await(await Lt({body:t,headers:e})).run(o,r)}K.post("/v1/chat/stream",Qt("json",te),async t=>{const e=t.req.valid("json"),r=h(t.req.raw.headers,"X-Elevo-Wakeup-Key")?.trim()||void 0;let o;if(r){const d=it().claim(r);if(d.status!=="claimed")return t.header("X-Elevo-Wakeup-Duplicate","true"),t.json({error:"Scheduled wakeup was already accepted"},409);o=d.token}let i;try{i=await Lt({body:e,headers:t.req.raw.headers})}catch(d){throw r&&o&&it().release(r,o),d instanceof W?new U(d.status,{message:d.message}):d}return Vt(t,async d=>{let I=!1;const O=()=>{!r||!o||I||(I=!0,it().markStarted(r,o))},M=async S=>{await d.writeSSE(S)};await i.run({writeData:async S=>{O(),await M({data:JSON.stringify(S)})},writeDone:async()=>{O(),await M({data:"[DONE]"})}},t.req.raw.signal)})});var ze=K;export{W as ChatRequestError,Mt as cancelCodexConversation,je as codexActivitySnapshot,ze as default,Be as handleCodexChatStream,Lt as prepareCodexChatStream};
|
|
1
|
+
import{Hono as Jt}from"hono";import{HTTPException as U}from"hono/http-exception";import{zValidator as Qt}from"@hono/zod-validator";import{streamSSE as Vt}from"hono/streaming";import{randomUUID as Gt}from"node:crypto";import{join as Yt}from"node:path";import{sessionManager as z,WorkdirNotFoundError as Zt}from"../codex.js";import{logger as H}from"@messenger-agent/shared/logger";import{ChatBodySchema as te,RequestUserInputToolPartSchema as ee}from"../schemas.js";import{appConfig as ot}from"../config.js";import{setManagedTaskToolContext as ae}from"../dynamic-tools.js";import{resolveAgentWorkspaceRoot as ne}from"@messenger-agent/shared/agent-workspaces";import{saveUploadedFilePart as re}from"@messenger-agent/shared/uploaded-files";import{WakeupIdempotencyStore as oe}from"@messenger-agent/shared/wakeup-idempotency";import{commandExecutionItem as Tt,addTokenUsage as ie,displayCommand as se,dynamicToolCallItem as St,dynamicToolCallOutput as de,fileChangeItem as kt,itemText as Et,mcpToolCallItem as Rt,mcpToolCallOutput as ue,toUsage as le,turnOptions as ce,webSearchItem as _t,webSearchPayload as Ot}from"./shared.js";const K=new Jt;let pe;function it(){return pe??=new oe(Yt(ot.dataDir,"wakeup-idempotency.sqlite"))}const m=new Map,J=new Set,st=new Map,_=new Map;function je(){const t=new Set([..._.values()].map(a=>a.conversationId).filter(a=>!!a));return{active:J.size,waiting:t.size}}function fe(t){return{type:"not_found_error",code:"conversation_not_found",message:`Conversation not found or already completed: ${t}`,param:"conversation_id"}}function me(t){const a=h(t,"X-Matrix-Room-Id")?.trim(),r=h(t,"X-Matrix-Thread-ID")?.trim();return a&&r?`${a}/${r}`:void 0}function we(t){const a=h(t,"X-Matrix-Room-Id")?.trim(),r=h(t,"X-Matrix-Event-ID")?.trim(),o=h(t,"X-Matrix-Thread-ID")?.trim();if(!(!a||!r))return{roomId:a,eventId:r,...o?{threadRootEventId:o}:null}}class W extends Error{status;constructor(a,r){super(r),this.status=a,this.name="ChatRequestError"}}function h(t,a){return t?.get(a)??void 0}async function Mt(t){const a=m.get(t);if(!a)return!1;for(a.cancelRequested=!0,a.turnId||a.owner?.abortController.abort();;){const r=a.transition;if(await r,r===a.transition)break}return a.thread&&a.turnId&&(a.interrupt||(a.interrupt=a.thread.interrupt(a.turnId).finally(()=>{a.interrupt=void 0})),await a.interrupt),a.owner?.abortController.abort(),a.resolveOwnerReady?.(),m.get(t)===a&&m.delete(t),!0}function Pt(t){let a=m.get(t);a||(a={conversationId:t,generation:0,cancelRequested:!1,transition:Promise.resolve()},m.set(t,a));const r=++a.generation,o=new AbortController,i=!a.owner;return i&&(a.owner={generation:r,abortController:o},a.ownerReady=new Promise(d=>{a.resolveOwnerReady=d})),{state:a,generation:r,abortController:o,ownsOutput:i}}function L(t){return m.get(t.state.conversationId)===t.state&&t.state.owner?.generation===t.generation}async function Q(t,a){const r=t.transition;let o;const i=new Promise(d=>{o=d});t.transition=r.then(()=>i),await r;try{return await a()}finally{o()}}async function ye(t,a,r,o){!t.ownsOutput&&!t.state.turnId&&await t.state.ownerReady;try{return await Q(t.state,async()=>{const{state:i}=t;if(m.get(i.conversationId)!==i||i.cancelRequested)throw new DOMException("Chat stream was cancelled before the turn started","AbortError");i.thread=a;const d=i.turnId?await a.steerStreamed(r,i.turnId,{signal:t.abortController.signal}):await a.runStreamed(r,{...o,signal:t.abortController.signal});return i.turnId=d.turnId,d})}finally{t.ownsOutput&&t.state.resolveOwnerReady?.()}}async function ge(t,a,r){return Q(t.state,async()=>{const{state:o}=t;if(!L(t)||o.cancelRequested)throw new DOMException("Chat stream was cancelled before user input was submitted","AbortError");o.thread=a;const i=await r();return o.turnId=i.turnId,o.resolveOwnerReady?.(),i})}async function qt(t,a,r,o){return Q(t.state,async()=>{const{state:i}=t;if(m.get(i.conversationId)!==i||i.cancelRequested)throw new DOMException("Chat stream was cancelled before user input was submitted","AbortError");i.thread=a;const d=await a.submitPendingUserInput(r,o);return d&&(i.turnId=d),d})}async function Ie(t,a,r,o,i){return Q(t.state,async()=>{const{state:d}=t;if(m.get(d.conversationId)!==d||d.cancelRequested)throw new DOMException("Chat stream was cancelled before the turn started","AbortError");d.thread=a,await a.interruptPendingUserInput(r.turnId,r.toolCallId),d.turnId=void 0;const I=await a.runStreamed(o,{...i,signal:t.abortController.signal});return d.turnId=I.turnId,I})}function Dt(t,a,r){t.state.thread=a,t.state.turnId=r}function Nt(t){const{state:a}=t;L(t)&&(a.owner=void 0,a.resolveOwnerReady?.(),a.turnId=void 0,m.get(a.conversationId)===a&&m.delete(a.conversationId))}function Ut(t){L(t)&&(t.state.owner=void 0,t.state.resolveOwnerReady?.(),!t.state.turnId&&m.get(t.state.conversationId)===t.state&&m.delete(t.state.conversationId))}function he(t){L(t)&&(Ut(t),t.state.resolveOwnerReady?.(),!t.state.turnId&&m.get(t.state.conversationId)===t.state&&m.delete(t.state.conversationId))}function V(t,a){return`${t??"unknown"}:${a}`}function Ce(t){if(t)return[..._.values()].find(a=>a.conversationId===t)}function Wt(t,a){return t??a}function ve(t){return t.type==="file"&&"mediaType"in t&&typeof t.mediaType=="string"&&"url"in t&&typeof t.url=="string"&&(!("filename"in t)||t.filename===void 0||typeof t.filename=="string")}async function xe(t){if(t.type==="text"&&typeof t.text=="string")return t.text;if(ve(t))return re(t,ot.fileUploads.tempDir);throw new U(400,{message:`Unsupported message part type: ${t.type}`})}async function be(t){const r=(await Promise.all(t.map(xe))).filter(o=>!!(o&&o.trim().length>0)).join(`
|
|
2
|
+
`).trim();if(!r)throw new U(400,{message:"No supported message content found"});return r}function Te(t){if(!t.some(o=>o.type==="tool"))return;if(t.length!==1)throw new U(400,{message:"When a tool part is present, the message must contain exactly one part"});const r=ee.safeParse(t[0]);if(!r.success)throw new U(400,{message:`Invalid requestUserInput tool part: ${r.error.message}`});return{itemId:r.data.toolCallId,response:r.data.output,sourceTurnId:r.data.sourceTurnId}}function w(t,a,r,o){const i=[a,r,o].filter(Boolean).join(":"),d=t.get(i);if(d)return d;const I=Gt();return t.set(i,I),I}function Se(t,a){return t&&t.length>0?t:a}function ke(t){H.debug("Received chat stream event:",t)}function Ee(t){if(t instanceof Zt||t&&typeof t=="object"&&"path"in t&&typeof t.path=="string")return t.path;if(t instanceof Error&&t.message.startsWith("Workdir not found: "))return t.message.slice(19)}K.post("/v1/chat/:conversation_id/cancel",async t=>{const a=t.req.param("conversation_id");try{return await Mt(a)?t.json({success:!0}):t.json({error:fe(a)},404)}catch(r){const o=r instanceof Error?r.message:"Failed to interrupt Codex conversation";return H.error(`Failed to cancel Codex conversation ${a}:`,r),t.json({error:{type:"server_error",code:"cancel_failed",message:o}},500)}}),K.get("/admin/status",t=>t.json({activeTaskCount:J.size,waitingClientToolCount:_.size,clientToolWaits:[..._.values()].map(({conversationId:a,toolCallId:r,toolName:o,since:i})=>({conversationId:a,toolCallId:r,toolName:o,since:i}))}));async function Lt({body:t,headers:a}){const{conversationId:r,fork:o,model:i,effort:d,message:I}=t,O=h(a,"X-Matrix-Bot-Name"),M=h(a,"X-Matrix-Workdir"),S=h(a,"X-Matrix-Workspace-ID"),At=h(a,"X-Matrix-Agent-Mode"),G=h(a,"X-Elevo-LLM-Binding")?.trim()||void 0,Y=o?void 0:z.bindingFor(r);if(G&&Y&&G!==Y)throw new W(409,"LLM proxy binding does not match the existing conversation");const A=Y??G,$t=me(a),Ft=we(a),Z=Te(I.parts),tt=Z?void 0:await be(I.parts),dt=Z?void 0:Ce(r),c=Z,P=tt&&dt?dt:void 0;let $=!o&&r?Pt(r):void 0;const Xt=(()=>{try{return ne(ot,S)}catch(k){throw new W(400,k instanceof Error?k.message:"Invalid workspace")}})(),{thread:y,isNewSession:jt}=await(async()=>{try{if(o){if(!("threadId"in o))throw new W(400,"Codex fork requires threadId and turnId");return await z.fork(o,O,M,S,i,"danger-full-access",A)}return await z.getOrCreate(r,O,M,S,i,"danger-full-access",A)}catch(k){$&&he($);const q=Ee(k);throw q?new W(400,`Workdir not found: ${q}`):k}})();$??=Pt(y.id),ae(y.id,{workspaceRoot:Xt,threadRef:$t,matrixEvent:Ft,llmProxyBinding:A});const ut=ce(At,i,d);return{run:async(k,q)=>{const p=$,F=p.abortController;J.add(F);const et=()=>{L(p)&&Mt(p.state.conversationId).catch(n=>H.error("Failed to cancel disconnected Codex stream:",n))};q?.aborted&&et(),q?.addEventListener("abort",et,{once:!0});let b=!!c,lt=!!c,ct=!1,l,C=y.id,v,X=!1,N;const pt=new Set,ft=new Set,j=new Set,f=new Map,mt=new Set,Bt=new Set,wt=new Set;let yt,at,gt;const It=()=>{N=setTimeout(()=>{s({type:"heartbeat"}).catch(()=>{})},6e4)},ht=async n=>{N&&clearTimeout(N),n.data==="[DONE]"?await k.writeDone():await k.writeData(JSON.parse(String(n.data))),It()},s=async n=>{await ht({data:JSON.stringify(n)})},B=async()=>{await ht({data:"[DONE]"})},zt=async()=>{const n=Wt(l,y.id),e=st.get(n);e&&(st.delete(n),await s({type:"data",kind:"diff",data:{diff:e}}))},Ct=async(n,e)=>{if(n.type!=="contextCompaction")return!1;if(e==="end"&&!mt.has(n.id))return!0;const u=e==="start"?mt:Bt;return u.has(n.id)||(u.add(n.id),await s({type:"data",kind:"compact",data:e==="start"?{phase:e,activityId:n.id}:{phase:e,activityId:n.id,status:"completed"}})),!0},nt=async n=>{if(n.type!=="subAgentActivity")return!1;if(wt.has(n.id))return!0;wt.add(n.id);const e=w(f,l,n.id),u={activity:n.kind,agentThreadId:n.agentThreadId,...n.agentPath?{agentPath:n.agentPath}:null};return await s({type:"tool-input-available",toolCallId:e,toolName:`subagent_${n.kind}`,...n.agentPath?{title:n.agentPath}:null,input:u}),await s(n.kind==="interrupted"?{type:"tool-output-error",toolCallId:e,errorText:"Subagent interrupted"}:{type:"tool-output-available",toolCallId:e,output:u}),!0};It();const vt=n=>{l!==n&&(l=n,jt&&z.persist(n,O,M,S,A))},E=async(n,e)=>{b||(b=!0,vt(n),await s({type:"start",messageMetadata:{conversationId:n,threadId:n,...e?{turnId:e}:null}}),await s({type:"start-step"}),lt=!0)},rt=async n=>{ct||(ct=!0,lt&&(await zt(),n==="stop"&&at&&await s({type:"data",kind:"token_usage",data:at}),await s({type:"finish-step"})),await s({type:"finish",finishReason:n}),await B())},xt=async n=>{if(pt.has(n.id))return;pt.add(n.id);const e=n.type==="plan"?{type:"plan"}:void 0;await s({type:"text-start",id:n.id,...e?{textMetadata:e}:{}})},bt=async n=>{ft.has(n.id)||(ft.add(n.id),await s({type:"reasoning-start",id:n.id}))},Ht=async n=>{const e=Tt(n);if(e)return await s({type:"tool-input-available",toolCallId:w(f,l,n.id),toolName:"bash",input:se(e.command)}),!0;const u=kt(n);if(u){for(const[T,x]of u.changes.entries())await s({type:"tool-input-available",toolCallId:w(f,l,n.id,String(T)),toolName:x.kind.type==="update"?"Edit":x.kind.type==="add"?"Add":"Delete",title:x.path,input:x});return!0}const g=Rt(n);if(g)return await s({type:"tool-input-available",toolCallId:w(f,l,n.id),toolName:g.tool,input:g.arguments}),!0;const R=St(n);if(R)return await s({type:"tool-input-available",toolCallId:w(f,l,n.id),toolName:R.tool,input:R.arguments}),!0;const D=_t(n);return D?(await s({type:"tool-input-available",toolCallId:w(f,l,n.id),toolName:"web_search",title:D.query,input:Ot(D)}),!0):!1},Kt=async n=>{const e=Tt(n);if(e){const T=e.status==="failed"||e.status==="declined";return await s(T?{type:"tool-output-error",toolCallId:w(f,l,n.id),errorText:Se(e.aggregatedOutput,"Command execution failed")}:{type:"tool-output-available",toolCallId:w(f,l,n.id),output:e.aggregatedOutput??""}),!0}const u=kt(n);if(u){const T=u.status==="failed"||u.status==="declined";for(const[x]of u.changes.entries())await s(T?{type:"tool-output-error",toolCallId:w(f,l,n.id,String(x)),errorText:u.status}:{type:"tool-output-available",toolCallId:w(f,l,n.id,String(x)),output:u.status,preliminary:!1});return!0}const g=Rt(n);if(g){const T=g.status==="failed";return await s(T?{type:"tool-output-error",toolCallId:w(f,l,n.id),errorText:g.error?.message??"MCP tool call failed"}:{type:"tool-output-available",toolCallId:w(f,l,n.id),output:ue(g)}),!0}const R=St(n);if(R){const T=R.status==="failed"||R.success===!1,x=de(R);return await s(T?{type:"tool-output-error",toolCallId:w(f,l,n.id),errorText:x||"Dynamic tool call failed"}:{type:"tool-output-available",toolCallId:w(f,l,n.id),output:x}),!0}const D=_t(n);return D?(await s({type:"tool-output-available",toolCallId:w(f,l,n.id),output:Ot(D)}),!0):!1};try{if(c&&!p.ownsOutput){await qt(p,y,c.itemId,c.response),_.delete(V(r,c.itemId)),await B();return}const n=P?await Ie(p,y,P,tt,ut):c?await ge(p,y,()=>y.continueWithUserInput(c.itemId,c.response,{signal:F.signal,sourceTurnId:c.sourceTurnId})):await ye(p,y,tt,ut);if(c?_.delete(V(r,c.itemId)):P&&_.delete(V(r,P.toolCallId)),Dt(p,y,n.turnId),!p.ownsOutput){await B();return}c?(await s({type:"data",kind:"agent_session",data:{agentType:"codex",threadId:y.id,turnId:n.turnId}}),await s({type:"tool-output-available",toolCallId:c.itemId,toolName:"request_user_input",output:c.response})):P&&await s({type:"tool-output-error",toolCallId:P.toolCallId,toolName:"request_user_input",errorText:"Question interrupted by text response"});for await(const e of n.events)if(e.type!=="item.delta"&&ke(e),e.type==="thread.started")C=e.thread_id,vt(C);else if(e.type==="turn.started")v=e.turn_id,Dt(p,y,v),await E(C,v);else if(e.type==="item.started"){if(b||await E(C,v),await Ct(e.item,"start")||await nt(e.item)||await Ht(e.item))continue;e.item.type==="agentMessage"||e.item.type==="plan"?await xt(e.item):e.item.type==="reasoning"&&await bt(e.item)}else if(e.type==="item.delta")b||await E(C,v),e.item_type==="agentMessage"||e.item_type==="plan"?(j.add(e.item_id),await s({type:"text-delta",id:e.item_id,delta:e.delta})):e.item_type==="reasoning"&&(j.add(e.item_id),await s({type:"reasoning-delta",id:e.item_id,delta:e.delta}));else if(e.type==="item.updated"){if(await nt(e.item))continue}else if(e.type==="item.completed"){if(b||await E(C,v),await Ct(e.item,"end")||await nt(e.item)||await Kt(e.item))continue;if(e.item.type==="agentMessage"||e.item.type==="plan"){await xt(e.item);const u=Et(e.item)??"";u&&!j.has(e.item.id)&&await s({type:"text-delta",id:e.item.id,delta:u}),await s({type:"text-end",id:e.item.id,...e.item.type==="agentMessage"&&e.item.phase!=null?{phase:e.item.phase}:{}})}else if(e.item.type==="reasoning"){await bt(e.item);const u=Et(e.item)??"";u&&!j.has(e.item.id)&&await s({type:"reasoning-delta",id:e.item.id,delta:u}),await s({type:"reasoning-end",id:e.item.id})}}else if(e.type==="item.tool.requestUserInput"){if(b||await E(C,v),e.params.isBlocking===!1){if(!await qt(p,y,e.params.itemId,{answers:{}}))throw new Error(`Non-blocking user input request is no longer pending: ${e.params.itemId}`);H.info(`Auto-resolved non-blocking request_user_input ${e.params.itemId} for turn ${e.params.turnId}`);continue}_.set(V(l,e.params.itemId),{conversationId:l,toolCallId:e.params.itemId,toolName:"request_user_input",turnId:e.params.turnId,since:new Date().toISOString()}),await s({type:"tool-input-available",toolCallId:e.params.itemId,toolName:"request_user_input",input:e.params}),await B();return}else if(e.type==="turn.diff.updated")b||await E(C,v),st.set(Wt(l,y.id),e.diff);else if(e.type==="thread.tokenUsage.updated"){const u=le(e.usage);if(u){const g=ie(gt,u.last);g&&(gt=g,at={...u,last:g})}}else if(e.type==="turn.failed"){yt!==e.error.message&&await s({type:"error",errorText:e.error.message}),await rt("error"),X=!0,Nt(p);return}else e.type==="error"&&(yt=e.message,await s({type:"error",errorText:e.message}));X=!F.signal.aborted,X&&Nt(p),b||await E(C,v),await rt("stop")}catch(n){const e=n instanceof Error?n.message:"Chat stream failed";b||await E(C,v),await s({type:"error",errorText:e}),await rt("error")}finally{N&&clearTimeout(N),q?.removeEventListener("abort",et),J.delete(F),X||Ut(p)}}}}async function Be({body:t,headers:a,signal:r},o){await(await Lt({body:t,headers:a})).run(o,r)}K.post("/v1/chat/stream",Qt("json",te),async t=>{const a=t.req.valid("json"),r=h(t.req.raw.headers,"X-Elevo-Wakeup-Key")?.trim()||void 0;let o;if(r){const d=it().claim(r);if(d.status!=="claimed")return t.header("X-Elevo-Wakeup-Duplicate","true"),t.json({error:"Scheduled wakeup was already accepted"},409);o=d.token}let i;try{i=await Lt({body:a,headers:t.req.raw.headers})}catch(d){throw r&&o&&it().release(r,o),d instanceof W?new U(d.status,{message:d.message}):d}return Vt(t,async d=>{let I=!1;const O=()=>{!r||!o||I||(I=!0,it().markStarted(r,o))},M=async S=>{await d.writeSSE(S)};await i.run({writeData:async S=>{O(),await M({data:JSON.stringify(S)})},writeDone:async()=>{O(),await M({data:"[DONE]"})}},t.req.raw.signal)})});var ze=K;export{W as ChatRequestError,Mt as cancelCodexConversation,je as codexActivitySnapshot,ze as default,Be as handleCodexChatStream,Lt as prepareCodexChatStream};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@messenger-agent/codex-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0-alpha.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"ws": "^8.21.0",
|
|
23
23
|
"yaml": "^2.9.0",
|
|
24
24
|
"zod": "^4.4.3",
|
|
25
|
-
"@messenger-agent/shared": "0.
|
|
25
|
+
"@messenger-agent/shared": "0.38.0-alpha.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/ws": "^8.18.1"
|