@messenger-agent/codex-agent 0.27.0-alpha.0 → 0.27.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.
|
@@ -290,10 +290,6 @@ export type ToolRequestUserInputParams = {
|
|
|
290
290
|
turnId: string;
|
|
291
291
|
itemId: string;
|
|
292
292
|
questions: Array<ToolRequestUserInputQuestion>;
|
|
293
|
-
/** Missing on Codex versions before 0.147.0; legacy requests are treated as blocking. */
|
|
294
|
-
isBlocking?: boolean;
|
|
295
|
-
/** @deprecated Use isBlocking to decide whether the request should block. */
|
|
296
|
-
autoResolutionMs?: number | null;
|
|
297
293
|
};
|
|
298
294
|
/**
|
|
299
295
|
* EXPERIMENTAL. Params sent with a request_user_input event.
|
package/dist/routes/chat.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Hono as
|
|
2
|
-
`).trim();if(!r)throw new q(400,{message:"No supported message content found"});return r}function Ce(t){if(!t.some(o=>o.type==="tool"))return;if(t.length!==1)throw new q(400,{message:"When a tool part is present, the message must contain exactly one part"});const r=Zt.safeParse(t[0]);if(!r.success)throw new q(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 T=Vt();return t.set(i,T),T}function ve(t,e){return t&&t.length>0?t:e}function xe(t){B.debug("Received chat stream event:",t)}function be(t){if(t instanceof Gt||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)}z.post("/v1/chat/:conversation_id/cancel",async t=>{const e=t.req.param("conversation_id");try{return await Ot(e)?t.json({success:!0}):t.json({error:ue(e)},404)}catch(r){const o=r instanceof Error?r.message:"Failed to interrupt Codex conversation";return B.error(`Failed to cancel Codex conversation ${e}:`,r),t.json({error:{type:"server_error",code:"cancel_failed",message:o}},500)}}),z.get("/admin/status",t=>t.json({activeTaskCount:H.size,waitingClientToolCount:R.size,clientToolWaits:[...R.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:T}=t,Q=v(e,"X-Matrix-Bot-Name"),V=v(e,"X-Matrix-Workdir"),U=v(e,"X-Matrix-Workspace-ID"),At=v(e,"X-Matrix-Agent-Mode"),G=v(e,"X-Elevo-LLM-Binding")?.trim()||void 0,Y=o?void 0:j.bindingFor(r);if(G&&Y&&G!==Y)throw new D(409,"LLM proxy binding does not match the existing conversation");const L=Y??G,Wt=le(e),$t=ce(e),Z=Ce(T.parts),tt=Z?void 0:await he(T.parts),it=Z?void 0:ye(r),c=Z,_=tt&&it?it:void 0;let A=!o&&r?Mt(r):void 0;const Ft=(()=>{try{return ee(xt,U)}catch(S){throw new D(400,S instanceof Error?S.message:"Invalid workspace")}})(),{thread:y,isNewSession:Xt}=await(async()=>{try{if(o){if(!("threadId"in o))throw new D(400,"Codex fork requires threadId and turnId");return await j.fork(o,Q,V,U,i,"danger-full-access",L)}return await j.getOrCreate(r,Q,V,U,i,"danger-full-access",L)}catch(S){A&&we(A);const O=be(S);throw O?new D(400,`Workdir not found: ${O}`):S}})();A??=Mt(y.id),te(y.id,{workspaceRoot:Ft,threadRef:Wt,matrixEvent:$t,llmProxyBinding:L});const st=de(At,i,d);return{run:async(S,O)=>{const p=A,W=p.abortController;H.add(W);const et=()=>{N(p)&&Ot(p.state.conversationId).catch(a=>B.error("Failed to cancel disconnected Codex stream:",a))};O?.aborted&&et(),O?.addEventListener("abort",et,{once:!0});let x=!!c,dt=!!c,ut=!1,l,I=y.id,h,$=!1,P;const lt=new Set,ct=new Set,F=new Set,f=new Map,pt=new Set,jt=new Set,ft=new Set;let mt,nt,wt;const yt=()=>{P=setTimeout(()=>{s({type:"heartbeat"}).catch(()=>{})},6e4)},gt=async a=>{P&&clearTimeout(P),a.data==="[DONE]"?await S.writeDone():await S.writeData(JSON.parse(String(a.data))),yt()},s=async a=>{await gt({data:JSON.stringify(a)})},X=async()=>{await gt({data:"[DONE]"})},Bt=async()=>{const a=Ut(l,y.id),n=ot.get(a);n&&(ot.delete(a),await s({type:"data",kind:"diff",data:{diff:n}}))},It=async(a,n)=>{if(a.type!=="contextCompaction")return!1;if(n==="end"&&!pt.has(a.id))return!0;const u=n==="start"?pt:jt;return u.has(a.id)||(u.add(a.id),await s({type:"data",kind:"compact",data:n==="start"?{phase:n,activityId:a.id}:{phase:n,activityId:a.id,status:"completed"}})),!0},at=async a=>{if(a.type!=="subAgentActivity")return!1;if(ft.has(a.id))return!0;ft.add(a.id);const n=w(f,l,a.id),u={activity:a.kind,agentThreadId:a.agentThreadId,...a.agentPath?{agentPath:a.agentPath}:null};return await s({type:"tool-input-available",toolCallId:n,toolName:`subagent_${a.kind}`,...a.agentPath?{title:a.agentPath}:null,input:u}),await s(a.kind==="interrupted"?{type:"tool-output-error",toolCallId:n,errorText:"Subagent interrupted"}:{type:"tool-output-available",toolCallId:n,output:u}),!0};yt();const ht=a=>{l!==a&&(l=a,Xt&&j.persist(a,Q,V,U,L))},E=async(a,n)=>{x||(x=!0,ht(a),await s({type:"start",messageMetadata:{conversationId:a,threadId:a,...n?{turnId:n}:null}}),await s({type:"start-step"}),dt=!0)},rt=async a=>{ut||(ut=!0,dt&&(await Bt(),a==="stop"&&nt&&await s({type:"data",kind:"token_usage",data:nt}),await s({type:"finish-step"})),await s({type:"finish",finishReason:a}),await X())},Ct=async a=>{if(lt.has(a.id))return;lt.add(a.id);const n=a.type==="plan"?{type:"plan"}:void 0;await s({type:"text-start",id:a.id,...n?{textMetadata:n}:{}})},vt=async a=>{ct.has(a.id)||(ct.add(a.id),await s({type:"reasoning-start",id:a.id}))},zt=async a=>{const n=bt(a);if(n)return await s({type:"tool-input-available",toolCallId:w(f,l,a.id),toolName:"bash",input:re(n.command)}),!0;const u=St(a);if(u){for(const[b,C]of u.changes.entries())await s({type:"tool-input-available",toolCallId:w(f,l,a.id,String(b)),toolName:C.kind.type==="update"?"Edit":C.kind.type==="add"?"Add":"Delete",title:C.path,input:C});return!0}const g=kt(a);if(g)return await s({type:"tool-input-available",toolCallId:w(f,l,a.id),toolName:g.tool,input:g.arguments}),!0;const k=Tt(a);if(k)return await s({type:"tool-input-available",toolCallId:w(f,l,a.id),toolName:k.tool,input:k.arguments}),!0;const M=Rt(a);return M?(await s({type:"tool-input-available",toolCallId:w(f,l,a.id),toolName:"web_search",title:M.query,input:_t(M)}),!0):!1},Ht=async a=>{const n=bt(a);if(n){const b=n.status==="failed"||n.status==="declined";return await s(b?{type:"tool-output-error",toolCallId:w(f,l,a.id),errorText:ve(n.aggregatedOutput,"Command execution failed")}:{type:"tool-output-available",toolCallId:w(f,l,a.id),output:n.aggregatedOutput??""}),!0}const u=St(a);if(u){const b=u.status==="failed"||u.status==="declined";for(const[C]of u.changes.entries())await s(b?{type:"tool-output-error",toolCallId:w(f,l,a.id,String(C)),errorText:u.status}:{type:"tool-output-available",toolCallId:w(f,l,a.id,String(C)),output:u.status,preliminary:!1});return!0}const g=kt(a);if(g){const b=g.status==="failed";return await s(b?{type:"tool-output-error",toolCallId:w(f,l,a.id),errorText:g.error?.message??"MCP tool call failed"}:{type:"tool-output-available",toolCallId:w(f,l,a.id),output:ie(g)}),!0}const k=Tt(a);if(k){const b=k.status==="failed"||k.success===!1,C=oe(k);return await s(b?{type:"tool-output-error",toolCallId:w(f,l,a.id),errorText:C||"Dynamic tool call failed"}:{type:"tool-output-available",toolCallId:w(f,l,a.id),output:C}),!0}const M=Rt(a);return M?(await s({type:"tool-output-available",toolCallId:w(f,l,a.id),output:_t(M)}),!0):!1};try{if(c&&!p.ownsOutput){await Pt(p,y,c.itemId,c.response),R.delete(K(r,c.itemId)),await X();return}const a=_?await me(p,y,_,tt,st):c?await fe(p,y,()=>y.continueWithUserInput(c.itemId,c.response,{signal:W.signal,sourceTurnId:c.sourceTurnId})):await pe(p,y,tt,st);if(c?R.delete(K(r,c.itemId)):_&&R.delete(K(r,_.toolCallId)),qt(p,y,a.turnId),!p.ownsOutput){await X();return}c?(await s({type:"data",kind:"agent_session",data:{agentType:"codex",threadId:y.id,turnId:a.turnId}}),await s({type:"tool-output-available",toolCallId:c.itemId,toolName:"request_user_input",output:c.response})):_&&await s({type:"tool-output-error",toolCallId:_.toolCallId,toolName:"request_user_input",errorText:"Question interrupted by text response"});for await(const n of a.events)if(n.type!=="item.delta"&&xe(n),n.type==="thread.started")I=n.thread_id,ht(I);else if(n.type==="turn.started")h=n.turn_id,qt(p,y,h),await E(I,h);else if(n.type==="item.started"){if(x||await E(I,h),await It(n.item,"start")||await at(n.item)||await zt(n.item))continue;n.item.type==="agentMessage"||n.item.type==="plan"?await Ct(n.item):n.item.type==="reasoning"&&await vt(n.item)}else if(n.type==="item.delta")x||await E(I,h),n.item_type==="agentMessage"||n.item_type==="plan"?(F.add(n.item_id),await s({type:"text-delta",id:n.item_id,delta:n.delta})):n.item_type==="reasoning"&&(F.add(n.item_id),await s({type:"reasoning-delta",id:n.item_id,delta:n.delta}));else if(n.type==="item.updated"){if(await at(n.item))continue}else if(n.type==="item.completed"){if(x||await E(I,h),await It(n.item,"end")||await at(n.item)||await Ht(n.item))continue;if(n.item.type==="agentMessage"||n.item.type==="plan"){await Ct(n.item);const u=Et(n.item)??"";u&&!F.has(n.item.id)&&await s({type:"text-delta",id:n.item.id,delta:u}),await s({type:"text-end",id:n.item.id})}else if(n.item.type==="reasoning"){await vt(n.item);const u=Et(n.item)??"";u&&!F.has(n.item.id)&&await s({type:"reasoning-delta",id:n.item.id,delta:u}),await s({type:"reasoning-end",id:n.item.id})}}else if(n.type==="item.tool.requestUserInput"){if(x||await E(I,h),n.params.isBlocking===!1){if(!await Pt(p,y,n.params.itemId,{answers:{}}))throw new Error(`Non-blocking user input request is no longer pending: ${n.params.itemId}`);B.info(`Auto-resolved non-blocking request_user_input ${n.params.itemId} for turn ${n.params.turnId}`);continue}R.set(K(l,n.params.itemId),{conversationId:l,toolCallId:n.params.itemId,toolName:"request_user_input",turnId:n.params.turnId,since:new Date().toISOString()}),await s({type:"tool-input-available",toolCallId:n.params.itemId,toolName:"request_user_input",input:n.params}),await X();return}else if(n.type==="turn.diff.updated")x||await E(I,h),ot.set(Ut(l,y.id),n.diff);else if(n.type==="thread.tokenUsage.updated"){const u=se(n.usage);if(u){const g=ae(wt,u.last);g&&(wt=g,nt={...u,last:g})}}else if(n.type==="turn.failed"){mt!==n.error.message&&await s({type:"error",errorText:n.error.message}),await rt("error"),$=!0,Dt(p);return}else n.type==="error"&&(mt=n.message,await s({type:"error",errorText:n.message}));$=!W.signal.aborted,$&&Dt(p),x||await E(I,h),await rt("stop")}catch(a){const n=a instanceof Error?a.message:"Chat stream failed";x||await E(I,h),await s({type:"error",errorText:n}),await rt("error")}finally{P&&clearTimeout(P),O?.removeEventListener("abort",et),H.delete(W),$||Nt(p)}}}}async function Ae({body:t,headers:e,signal:r},o){await(await Lt({body:t,headers:e})).run(o,r)}z.post("/v1/chat/stream",Kt("json",Yt),async t=>{const e=t.req.valid("json"),r=await(async()=>{try{return await Lt({body:e,headers:t.req.raw.headers})}catch(o){throw o instanceof D?new q(o.status,{message:o.message}):o}})();return Qt(t,async o=>{const i=async d=>{await o.writeSSE(d)};await r.run({writeData:async d=>{await i({data:JSON.stringify(d)})},writeDone:async()=>{await i({data:"[DONE]"})}},t.req.raw.signal)})});var We=z;export{D as ChatRequestError,Ot as cancelCodexConversation,Le as codexActivitySnapshot,We as default,Ae as handleCodexChatStream,Lt as prepareCodexChatStream};
|
|
1
|
+
import{Hono as Ht}from"hono";import{HTTPException as D}from"hono/http-exception";import{zValidator as Jt}from"@hono/zod-validator";import{streamSSE as Kt}from"hono/streaming";import{randomUUID as Qt}from"node:crypto";import{sessionManager as j,WorkdirNotFoundError as Vt}from"../codex.js";import{logger as rt}from"@messenger-agent/shared/logger";import{ChatBodySchema as Gt,RequestUserInputToolPartSchema as Yt}from"../schemas.js";import{appConfig as xt}from"../config.js";import{setManagedTaskToolContext as Zt}from"../dynamic-tools.js";import{resolveAgentWorkspaceRoot as te}from"@messenger-agent/shared/agent-workspaces";import{saveUploadedFilePart as ee}from"@messenger-agent/shared/uploaded-files";import{commandExecutionItem as bt,addTokenUsage as ne,displayCommand as ae,dynamicToolCallItem as Tt,dynamicToolCallOutput as re,fileChangeItem as St,itemText as Et,mcpToolCallItem as Rt,mcpToolCallOutput as oe,toUsage as ie,turnOptions as se,webSearchItem as kt,webSearchPayload as Ot}from"./shared.js";const B=new Ht,f=new Map,z=new Set,ot=new Map,k=new Map;function Le(){const t=new Set([...k.values()].map(e=>e.conversationId).filter(e=>!!e));return{active:z.size,waiting:t.size}}function de(t){return{type:"not_found_error",code:"conversation_not_found",message:`Conversation not found or already completed: ${t}`,param:"conversation_id"}}function ue(t){const e=v(t,"X-Matrix-Room-Id")?.trim(),r=v(t,"X-Matrix-Thread-ID")?.trim();return e&&r?`${e}/${r}`:void 0}function le(t){const e=v(t,"X-Matrix-Room-Id")?.trim(),r=v(t,"X-Matrix-Event-ID")?.trim(),o=v(t,"X-Matrix-Thread-ID")?.trim();if(!(!e||!r))return{roomId:e,eventId:r,...o?{threadRootEventId:o}:null}}class q extends Error{status;constructor(e,r){super(r),this.status=e,this.name="ChatRequestError"}}function v(t,e){return t?.get(e)??void 0}async function _t(t){const e=f.get(t);if(!e)return!1;for(e.cancelRequested=!0,e.turnId||e.owner?.abortController.abort();;){const r=e.transition;if(await r,r===e.transition)break}return e.thread&&e.turnId&&(e.interrupt||(e.interrupt=e.thread.interrupt(e.turnId).finally(()=>{e.interrupt=void 0})),await e.interrupt),e.owner?.abortController.abort(),e.resolveOwnerReady?.(),f.get(t)===e&&f.delete(t),!0}function Mt(t){let e=f.get(t);e||(e={conversationId:t,generation:0,cancelRequested:!1,transition:Promise.resolve()},f.set(t,e));const r=++e.generation,o=new AbortController,i=!e.owner;return i&&(e.owner={generation:r,abortController:o},e.ownerReady=new Promise(d=>{e.resolveOwnerReady=d})),{state:e,generation:r,abortController:o,ownsOutput:i}}function N(t){return f.get(t.state.conversationId)===t.state&&t.state.owner?.generation===t.generation}async function H(t,e){const r=t.transition;let o;const i=new Promise(d=>{o=d});t.transition=r.then(()=>i),await r;try{return await e()}finally{o()}}async function ce(t,e,r,o){!t.ownsOutput&&!t.state.turnId&&await t.state.ownerReady;try{return await H(t.state,async()=>{const{state:i}=t;if(f.get(i.conversationId)!==i||i.cancelRequested)throw new DOMException("Chat stream was cancelled before the turn started","AbortError");i.thread=e;const d=i.turnId?await e.steerStreamed(r,i.turnId,{signal:t.abortController.signal}):await e.runStreamed(r,{...o,signal:t.abortController.signal});return i.turnId=d.turnId,d})}finally{t.ownsOutput&&t.state.resolveOwnerReady?.()}}async function pe(t,e,r){return H(t.state,async()=>{const{state:o}=t;if(!N(t)||o.cancelRequested)throw new DOMException("Chat stream was cancelled before user input was submitted","AbortError");o.thread=e;const i=await r();return o.turnId=i.turnId,o.resolveOwnerReady?.(),i})}async function fe(t,e,r,o){return H(t.state,async()=>{const{state:i}=t;if(f.get(i.conversationId)!==i||i.cancelRequested)throw new DOMException("Chat stream was cancelled before user input was submitted","AbortError");i.thread=e;const d=await e.submitPendingUserInput(r,o);return d&&(i.turnId=d),d})}async function me(t,e,r,o,i){return H(t.state,async()=>{const{state:d}=t;if(f.get(d.conversationId)!==d||d.cancelRequested)throw new DOMException("Chat stream was cancelled before the turn started","AbortError");d.thread=e,await e.interruptPendingUserInput(r.turnId,r.toolCallId),d.turnId=void 0;const T=await e.runStreamed(o,{...i,signal:t.abortController.signal});return d.turnId=T.turnId,T})}function Pt(t,e,r){t.state.thread=e,t.state.turnId=r}function Dt(t){const{state:e}=t;N(t)&&(e.owner=void 0,e.resolveOwnerReady?.(),e.turnId=void 0,f.get(e.conversationId)===e&&f.delete(e.conversationId))}function qt(t){N(t)&&(t.state.owner=void 0,t.state.resolveOwnerReady?.(),!t.state.turnId&&f.get(t.state.conversationId)===t.state&&f.delete(t.state.conversationId))}function we(t){N(t)&&(qt(t),t.state.resolveOwnerReady?.(),!t.state.turnId&&f.get(t.state.conversationId)===t.state&&f.delete(t.state.conversationId))}function J(t,e){return`${t??"unknown"}:${e}`}function ye(t){if(t)return[...k.values()].find(e=>e.conversationId===t)}function Nt(t,e){return t??e}function ge(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 Ie(t){if(t.type==="text"&&typeof t.text=="string")return t.text;if(ge(t))return ee(t,xt.fileUploads.tempDir);throw new D(400,{message:`Unsupported message part type: ${t.type}`})}async function he(t){const r=(await Promise.all(t.map(Ie))).filter(o=>!!(o&&o.trim().length>0)).join(`
|
|
2
|
+
`).trim();if(!r)throw new D(400,{message:"No supported message content found"});return r}function Ce(t){if(!t.some(o=>o.type==="tool"))return;if(t.length!==1)throw new D(400,{message:"When a tool part is present, the message must contain exactly one part"});const r=Yt.safeParse(t[0]);if(!r.success)throw new D(400,{message:`Invalid requestUserInput tool part: ${r.error.message}`});return{itemId:r.data.toolCallId,response:r.data.output,sourceTurnId:r.data.sourceTurnId}}function m(t,e,r,o){const i=[e,r,o].filter(Boolean).join(":"),d=t.get(i);if(d)return d;const T=Qt();return t.set(i,T),T}function ve(t,e){return t&&t.length>0?t:e}function xe(t){rt.debug("Received chat stream event:",t)}function be(t){if(t instanceof Vt||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)}B.post("/v1/chat/:conversation_id/cancel",async t=>{const e=t.req.param("conversation_id");try{return await _t(e)?t.json({success:!0}):t.json({error:de(e)},404)}catch(r){const o=r instanceof Error?r.message:"Failed to interrupt Codex conversation";return rt.error(`Failed to cancel Codex conversation ${e}:`,r),t.json({error:{type:"server_error",code:"cancel_failed",message:o}},500)}}),B.get("/admin/status",t=>t.json({activeTaskCount:z.size,waitingClientToolCount:k.size,clientToolWaits:[...k.values()].map(({conversationId:e,toolCallId:r,toolName:o,since:i})=>({conversationId:e,toolCallId:r,toolName:o,since:i}))}));async function Ut({body:t,headers:e}){const{conversationId:r,fork:o,model:i,effort:d,message:T}=t,K=v(e,"X-Matrix-Bot-Name"),Q=v(e,"X-Matrix-Workdir"),U=v(e,"X-Matrix-Workspace-ID"),Lt=v(e,"X-Matrix-Agent-Mode"),V=v(e,"X-Elevo-LLM-Binding")?.trim()||void 0,G=o?void 0:j.bindingFor(r);if(V&&G&&V!==G)throw new q(409,"LLM proxy binding does not match the existing conversation");const L=G??V,At=ue(e),Wt=le(e),Y=Ce(T.parts),Z=Y?void 0:await he(T.parts),it=Y?void 0:ye(r),c=Y,O=Z&&it?it:void 0;let A=!o&&r?Mt(r):void 0;const Ft=(()=>{try{return te(xt,U)}catch(S){throw new q(400,S instanceof Error?S.message:"Invalid workspace")}})(),{thread:y,isNewSession:Xt}=await(async()=>{try{if(o){if(!("threadId"in o))throw new q(400,"Codex fork requires threadId and turnId");return await j.fork(o,K,Q,U,i,"danger-full-access",L)}return await j.getOrCreate(r,K,Q,U,i,"danger-full-access",L)}catch(S){A&&we(A);const _=be(S);throw _?new q(400,`Workdir not found: ${_}`):S}})();A??=Mt(y.id),Zt(y.id,{workspaceRoot:Ft,threadRef:At,matrixEvent:Wt,llmProxyBinding:L});const st=se(Lt,i,d);return{run:async(S,_)=>{const w=A,W=w.abortController;z.add(W);const tt=()=>{N(w)&&_t(w.state.conversationId).catch(a=>rt.error("Failed to cancel disconnected Codex stream:",a))};_?.aborted&&tt(),_?.addEventListener("abort",tt,{once:!0});let x=!!c,dt=!!c,ut=!1,l,I=y.id,h,F=!1,P;const lt=new Set,ct=new Set,X=new Set,p=new Map,pt=new Set,$t=new Set,ft=new Set;let mt,et,wt;const yt=()=>{P=setTimeout(()=>{s({type:"heartbeat"}).catch(()=>{})},6e4)},gt=async a=>{P&&clearTimeout(P),a.data==="[DONE]"?await S.writeDone():await S.writeData(JSON.parse(String(a.data))),yt()},s=async a=>{await gt({data:JSON.stringify(a)})},$=async()=>{await gt({data:"[DONE]"})},jt=async()=>{const a=Nt(l,y.id),n=ot.get(a);n&&(ot.delete(a),await s({type:"data",kind:"diff",data:{diff:n}}))},It=async(a,n)=>{if(a.type!=="contextCompaction")return!1;if(n==="end"&&!pt.has(a.id))return!0;const u=n==="start"?pt:$t;return u.has(a.id)||(u.add(a.id),await s({type:"data",kind:"compact",data:n==="start"?{phase:n,activityId:a.id}:{phase:n,activityId:a.id,status:"completed"}})),!0},nt=async a=>{if(a.type!=="subAgentActivity")return!1;if(ft.has(a.id))return!0;ft.add(a.id);const n=m(p,l,a.id),u={activity:a.kind,agentThreadId:a.agentThreadId,...a.agentPath?{agentPath:a.agentPath}:null};return await s({type:"tool-input-available",toolCallId:n,toolName:`subagent_${a.kind}`,...a.agentPath?{title:a.agentPath}:null,input:u}),await s(a.kind==="interrupted"?{type:"tool-output-error",toolCallId:n,errorText:"Subagent interrupted"}:{type:"tool-output-available",toolCallId:n,output:u}),!0};yt();const ht=a=>{l!==a&&(l=a,Xt&&j.persist(a,K,Q,U,L))},E=async(a,n)=>{x||(x=!0,ht(a),await s({type:"start",messageMetadata:{conversationId:a,threadId:a,...n?{turnId:n}:null}}),await s({type:"start-step"}),dt=!0)},at=async a=>{ut||(ut=!0,dt&&(await jt(),a==="stop"&&et&&await s({type:"data",kind:"token_usage",data:et}),await s({type:"finish-step"})),await s({type:"finish",finishReason:a}),await $())},Ct=async a=>{if(lt.has(a.id))return;lt.add(a.id);const n=a.type==="plan"?{type:"plan"}:void 0;await s({type:"text-start",id:a.id,...n?{textMetadata:n}:{}})},vt=async a=>{ct.has(a.id)||(ct.add(a.id),await s({type:"reasoning-start",id:a.id}))},Bt=async a=>{const n=bt(a);if(n)return await s({type:"tool-input-available",toolCallId:m(p,l,a.id),toolName:"bash",input:ae(n.command)}),!0;const u=St(a);if(u){for(const[b,C]of u.changes.entries())await s({type:"tool-input-available",toolCallId:m(p,l,a.id,String(b)),toolName:C.kind.type==="update"?"Edit":C.kind.type==="add"?"Add":"Delete",title:C.path,input:C});return!0}const g=Rt(a);if(g)return await s({type:"tool-input-available",toolCallId:m(p,l,a.id),toolName:g.tool,input:g.arguments}),!0;const R=Tt(a);if(R)return await s({type:"tool-input-available",toolCallId:m(p,l,a.id),toolName:R.tool,input:R.arguments}),!0;const M=kt(a);return M?(await s({type:"tool-input-available",toolCallId:m(p,l,a.id),toolName:"web_search",title:M.query,input:Ot(M)}),!0):!1},zt=async a=>{const n=bt(a);if(n){const b=n.status==="failed"||n.status==="declined";return await s(b?{type:"tool-output-error",toolCallId:m(p,l,a.id),errorText:ve(n.aggregatedOutput,"Command execution failed")}:{type:"tool-output-available",toolCallId:m(p,l,a.id),output:n.aggregatedOutput??""}),!0}const u=St(a);if(u){const b=u.status==="failed"||u.status==="declined";for(const[C]of u.changes.entries())await s(b?{type:"tool-output-error",toolCallId:m(p,l,a.id,String(C)),errorText:u.status}:{type:"tool-output-available",toolCallId:m(p,l,a.id,String(C)),output:u.status,preliminary:!1});return!0}const g=Rt(a);if(g){const b=g.status==="failed";return await s(b?{type:"tool-output-error",toolCallId:m(p,l,a.id),errorText:g.error?.message??"MCP tool call failed"}:{type:"tool-output-available",toolCallId:m(p,l,a.id),output:oe(g)}),!0}const R=Tt(a);if(R){const b=R.status==="failed"||R.success===!1,C=re(R);return await s(b?{type:"tool-output-error",toolCallId:m(p,l,a.id),errorText:C||"Dynamic tool call failed"}:{type:"tool-output-available",toolCallId:m(p,l,a.id),output:C}),!0}const M=kt(a);return M?(await s({type:"tool-output-available",toolCallId:m(p,l,a.id),output:Ot(M)}),!0):!1};try{if(c&&!w.ownsOutput){await fe(w,y,c.itemId,c.response),k.delete(J(r,c.itemId)),await $();return}const a=O?await me(w,y,O,Z,st):c?await pe(w,y,()=>y.continueWithUserInput(c.itemId,c.response,{signal:W.signal,sourceTurnId:c.sourceTurnId})):await ce(w,y,Z,st);if(c?k.delete(J(r,c.itemId)):O&&k.delete(J(r,O.toolCallId)),Pt(w,y,a.turnId),!w.ownsOutput){await $();return}c?(await s({type:"data",kind:"agent_session",data:{agentType:"codex",threadId:y.id,turnId:a.turnId}}),await s({type:"tool-output-available",toolCallId:c.itemId,toolName:"request_user_input",output:c.response})):O&&await s({type:"tool-output-error",toolCallId:O.toolCallId,toolName:"request_user_input",errorText:"Question interrupted by text response"});for await(const n of a.events)if(n.type!=="item.delta"&&xe(n),n.type==="thread.started")I=n.thread_id,ht(I);else if(n.type==="turn.started")h=n.turn_id,Pt(w,y,h),await E(I,h);else if(n.type==="item.started"){if(x||await E(I,h),await It(n.item,"start")||await nt(n.item)||await Bt(n.item))continue;n.item.type==="agentMessage"||n.item.type==="plan"?await Ct(n.item):n.item.type==="reasoning"&&await vt(n.item)}else if(n.type==="item.delta")x||await E(I,h),n.item_type==="agentMessage"||n.item_type==="plan"?(X.add(n.item_id),await s({type:"text-delta",id:n.item_id,delta:n.delta})):n.item_type==="reasoning"&&(X.add(n.item_id),await s({type:"reasoning-delta",id:n.item_id,delta:n.delta}));else if(n.type==="item.updated"){if(await nt(n.item))continue}else if(n.type==="item.completed"){if(x||await E(I,h),await It(n.item,"end")||await nt(n.item)||await zt(n.item))continue;if(n.item.type==="agentMessage"||n.item.type==="plan"){await Ct(n.item);const u=Et(n.item)??"";u&&!X.has(n.item.id)&&await s({type:"text-delta",id:n.item.id,delta:u}),await s({type:"text-end",id:n.item.id})}else if(n.item.type==="reasoning"){await vt(n.item);const u=Et(n.item)??"";u&&!X.has(n.item.id)&&await s({type:"reasoning-delta",id:n.item.id,delta:u}),await s({type:"reasoning-end",id:n.item.id})}}else if(n.type==="item.tool.requestUserInput"){x||await E(I,h),k.set(J(l,n.params.itemId),{conversationId:l,toolCallId:n.params.itemId,toolName:"request_user_input",turnId:n.params.turnId,since:new Date().toISOString()}),await s({type:"tool-input-available",toolCallId:n.params.itemId,toolName:"request_user_input",input:n.params}),await $();return}else if(n.type==="turn.diff.updated")x||await E(I,h),ot.set(Nt(l,y.id),n.diff);else if(n.type==="thread.tokenUsage.updated"){const u=ie(n.usage);if(u){const g=ne(wt,u.last);g&&(wt=g,et={...u,last:g})}}else if(n.type==="turn.failed"){mt!==n.error.message&&await s({type:"error",errorText:n.error.message}),await at("error"),F=!0,Dt(w);return}else n.type==="error"&&(mt=n.message,await s({type:"error",errorText:n.message}));F=!W.signal.aborted,F&&Dt(w),x||await E(I,h),await at("stop")}catch(a){const n=a instanceof Error?a.message:"Chat stream failed";x||await E(I,h),await s({type:"error",errorText:n}),await at("error")}finally{P&&clearTimeout(P),_?.removeEventListener("abort",tt),z.delete(W),F||qt(w)}}}}async function Ae({body:t,headers:e,signal:r},o){await(await Ut({body:t,headers:e})).run(o,r)}B.post("/v1/chat/stream",Jt("json",Gt),async t=>{const e=t.req.valid("json"),r=await(async()=>{try{return await Ut({body:e,headers:t.req.raw.headers})}catch(o){throw o instanceof q?new D(o.status,{message:o.message}):o}})();return Kt(t,async o=>{const i=async d=>{await o.writeSSE(d)};await r.run({writeData:async d=>{await i({data:JSON.stringify(d)})},writeDone:async()=>{await i({data:"[DONE]"})}},t.req.raw.signal)})});var We=B;export{q as ChatRequestError,_t as cancelCodexConversation,Le as codexActivitySnapshot,We as default,Ae as handleCodexChatStream,Ut as prepareCodexChatStream};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@messenger-agent/codex-agent",
|
|
3
|
-
"version": "0.27.0-alpha.
|
|
3
|
+
"version": "0.27.0-alpha.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
"@ai-sdk/openai": "^3.0.63",
|
|
16
16
|
"@hono/node-server": "^2.0.2",
|
|
17
17
|
"@hono/zod-validator": "^0.7.6",
|
|
18
|
-
"@openai/codex": "^0.
|
|
18
|
+
"@openai/codex": "^0.145.0",
|
|
19
19
|
"ai": "^6.0.182",
|
|
20
20
|
"hono": "^4.12.18",
|
|
21
21
|
"undici": "^8.4.1",
|
|
22
22
|
"ws": "^8.21.0",
|
|
23
23
|
"yaml": "^2.9.0",
|
|
24
24
|
"zod": "^4.4.3",
|
|
25
|
-
"@messenger-agent/shared": "0.27.0-alpha.
|
|
25
|
+
"@messenger-agent/shared": "0.27.0-alpha.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/ws": "^8.18.1"
|