@messenger-agent/codex-agent 0.43.1-alpha.0 → 0.43.1-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/routes/chat.js +2 -2
- package/dist/schemas.d.ts +2 -0
- package/dist/schemas.js +1 -1
- package/package.json +2 -2
package/dist/routes/chat.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Hono as
|
|
2
|
-
`).trim();if(!o)throw new U(400,{message:"No supported message content found"});return o}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 h=Gt();return t.set(i,h),h}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 $t({body:t,headers:a}){const{conversationId:r,fork:o,model:i,effort:d,message:h}=t,q=x(a,"X-Matrix-Bot-Name"),M=x(a,"X-Matrix-Workdir"),k=x(a,"X-Matrix-Workspace-ID"),At=x(a,"X-Matrix-Agent-Mode"),G=x(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 $=Y??G,Ft=me(a),Xt=we(a),Z=Te(h.parts),dt=(()=>{try{return ne(ot,k)}catch(E){throw new W(400,E instanceof Error?E.message:"Invalid workspace")}})(),tt=Z?void 0:await be(h.parts,dt),ut=Z?void 0:ve(r),p=Z,P=tt&&ut?ut:void 0;let A=!o&&r?Pt(r):void 0;const{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,q,M,k,i,"danger-full-access",$)}return await z.getOrCreate(r,q,M,k,i,"danger-full-access",$)}catch(E){A&&he(A);const D=Ee(E);throw D?new W(400,`Workdir not found: ${D}`):E}})();A??=Pt(y.id),ae(y.id,{workspaceRoot:dt,threadRef:Ft,matrixEvent:Xt,llmProxyBinding:$});const lt=ce(At,i,d);return{run:async(E,D)=>{const c=A,F=c.abortController;J.add(F);const et=()=>{L(c)&&Mt(c.state.conversationId).catch(n=>H.error("Failed to cancel disconnected Codex stream:",n))};D?.aborted&&et(),D?.addEventListener("abort",et,{once:!0});let T=!!p,ct=!!p,pt=!1,l,I=y.id,v,X=!1,N;const ft=new Set,mt=new Set,j=new Set,f=new Map,wt=new Set,Bt=new Set,yt=new Set;let gt,at,It;const ht=()=>{N=setTimeout(()=>{s({type:"heartbeat"}).catch(()=>{})},6e4)},vt=async n=>{N&&clearTimeout(N),n.data==="[DONE]"?await E.writeDone():await E.writeData(JSON.parse(String(n.data))),ht()},s=async n=>{await vt({data:JSON.stringify(n)})},B=async()=>{await vt({data:"[DONE]"})},zt=async()=>{const n=Lt(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"&&!wt.has(n.id))return!0;const u=e==="start"?wt: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(yt.has(n.id))return!0;yt.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};ht();const xt=n=>{l!==n&&(l=n,jt&&z.persist(n,q,M,k,$))},R=async(n,e)=>{T||(T=!0,xt(n),await s({type:"start",messageMetadata:{conversationId:n,threadId:n,...e?{turnId:e}:null}}),await s({type:"start-step"}),ct=!0)},rt=async n=>{pt||(pt=!0,ct&&(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())},bt=async n=>{if(ft.has(n.id))return;ft.add(n.id);const e=n.type==="plan"?{type:"plan"}:void 0;await s({type:"text-start",id:n.id,...e?{textMetadata:e}:{}})},Tt=async n=>{mt.has(n.id)||(mt.add(n.id),await s({type:"reasoning-start",id:n.id}))},Ht=async n=>{const e=St(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=Et(n);if(u){for(const[S,b]of u.changes.entries())await s({type:"tool-input-available",toolCallId:w(f,l,n.id,String(S)),toolName:b.kind.type==="update"?"Edit":b.kind.type==="add"?"Add":"Delete",title:b.path,input:b});return!0}const g=Ot(n);if(g)return await s({type:"tool-input-available",toolCallId:w(f,l,n.id),toolName:g.tool,input:g.arguments}),!0;const C=kt(n);if(C)return await s({type:"tool-input-available",toolCallId:w(f,l,n.id),toolName:C.tool,input:C.arguments}),!0;const O=_t(n);return O?(await s({type:"tool-input-available",toolCallId:w(f,l,n.id),toolName:"web_search",title:O.query,input:qt(O)}),!0):!1},Kt=async n=>{const e=St(n);if(e){const S=e.status==="failed"||e.status==="declined";return await s(S?{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=Et(n);if(u){const S=u.status==="failed"||u.status==="declined";for(const[b]of u.changes.entries())await s(S?{type:"tool-output-error",toolCallId:w(f,l,n.id,String(b)),errorText:u.status}:{type:"tool-output-available",toolCallId:w(f,l,n.id,String(b)),output:u.status,preliminary:!1});return!0}const g=Ot(n);if(g){const S=g.status==="failed";return await s(S?{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 C=kt(n);if(C){const S=C.status==="failed"||C.success===!1,b=de(C);return await s(S?{type:"tool-output-error",toolCallId:w(f,l,n.id),errorText:b||"Dynamic tool call failed"}:{type:"tool-output-available",toolCallId:w(f,l,n.id),output:b}),!0}const O=_t(n);return O?(await s({type:"tool-output-available",toolCallId:w(f,l,n.id),output:qt(O)}),!0):!1};try{if(p&&!c.ownsOutput){await Dt(c,y,p.itemId,p.response),_.delete(V(r,p.itemId)),await B();return}const n=P?await Ie(c,y,P,tt,lt):p?await ge(c,y,()=>y.continueWithUserInput(p.itemId,p.response,{signal:F.signal,sourceTurnId:p.sourceTurnId})):await ye(c,y,tt,lt);if(p?_.delete(V(r,p.itemId)):P&&_.delete(V(r,P.toolCallId)),Nt(c,y,n.turnId),!c.ownsOutput){await B();return}p?(await s({type:"data",kind:"agent_session",data:{agentType:"codex",threadId:y.id,turnId:n.turnId}}),await s({type:"tool-output-available",toolCallId:p.itemId,toolName:"request_user_input",output:p.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")I=e.thread_id,xt(I);else if(e.type==="turn.started")v=e.turn_id,Nt(c,y,v),await R(I,v);else if(e.type==="item.started"){if(T||await R(I,v),await Ct(e.item,"start")||await nt(e.item)||await Ht(e.item))continue;e.item.type==="agentMessage"||e.item.type==="plan"?await bt(e.item):e.item.type==="reasoning"&&await Tt(e.item)}else if(e.type==="item.delta")T||await R(I,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(T||await R(I,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 bt(e.item);const u=Rt(e.item)??"";if(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}:{}}),e.item.type==="agentMessage"&&e.item.delivery==="async"&&e.item.questions?.length){const g=`${l??I}:${e.item.id}`;await s({type:"async-question",request:{version:1,requestId:g,conversationId:l??I,threadId:I,turnId:v??c.state.turnId??"",toolCallId:e.item.id,questions:e.item.questions.map((C,O)=>({id:`${e.item.id}:${O}`,title:C.title,options:C.options,allowOther:!0}))}})}}else if(e.item.type==="reasoning"){await Tt(e.item);const u=Rt(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(T||await R(I,v),e.params.isBlocking===!1){if(!await Dt(c,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")T||await R(I,v),st.set(Lt(l,y.id),e.diff);else if(e.type==="thread.tokenUsage.updated"){const u=le(e.usage);if(u){const g=ie(It,u.last);g&&(It=g,at={...u,last:g})}}else if(e.type==="turn.failed"){gt!==e.error.message&&await s({type:"error",errorText:e.error.message}),await rt("error"),X=!0,Ut(c);return}else e.type==="error"&&(gt=e.message,await s({type:"error",errorText:e.message}));X=!F.signal.aborted,X&&Ut(c),T||await R(I,v),await rt("stop")}catch(n){const e=n instanceof Error?n.message:"Chat stream failed";T||await R(I,v),await s({type:"error",errorText:e}),await rt("error")}finally{N&&clearTimeout(N),D?.removeEventListener("abort",et),J.delete(F),X||Wt(c)}}}}async function Be({body:t,headers:a,signal:r},o){await(await $t({body:t,headers:a})).run(o,r)}K.post("/v1/chat/stream",Qt("json",te),async t=>{const a=t.req.valid("json"),r=x(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 $t({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 h=!1;const q=()=>{!r||!o||h||(h=!0,it().markStarted(r,o))},M=async k=>{await d.writeSSE(k)};await i.run({writeData:async k=>{q(),await M({data:JSON.stringify(k)})},writeDone:async()=>{q(),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,$t as prepareCodexChatStream};
|
|
1
|
+
import{Hono as Qt}from"hono";import{HTTPException as U}from"hono/http-exception";import{zValidator as Vt}from"@hono/zod-validator";import{streamSSE as Gt}from"hono/streaming";import{randomUUID as Yt}from"node:crypto";import{join as Zt}from"node:path";import{sessionManager as H,WorkdirNotFoundError as te}from"../codex.js";import{logger as K}from"@messenger-agent/shared/logger";import{ChatBodySchema as ee,RequestUserInputToolPartSchema as ne}from"../schemas.js";import{appConfig as it}from"../config.js";import{setManagedTaskToolContext as ae}from"../dynamic-tools.js";import{resolveAgentWorkspaceRoot as re,workspaceIdFromHeader as oe}from"@messenger-agent/shared/agent-workspaces";import{saveUploadedFilePart as ie}from"@messenger-agent/shared/uploaded-files";import{WakeupIdempotencyStore as se}from"@messenger-agent/shared/wakeup-idempotency";import{commandExecutionItem as kt,addTokenUsage as de,displayCommand as ue,dynamicToolCallItem as St,dynamicToolCallOutput as le,fileChangeItem as Et,itemText as Rt,mcpToolCallItem as qt,mcpToolCallOutput as ce,toUsage as pe,turnOptions as fe,webSearchItem as Mt,webSearchPayload as Ot}from"./shared.js";const J=new Qt;let me;function st(){return me??=new se(Zt(it.dataDir,"wakeup-idempotency.sqlite"))}const m=new Map,Q=new Set,dt=new Map,q=new Map;function ze(){const t=new Set([...q.values()].map(n=>n.conversationId).filter(n=>!!n));return{active:Q.size,waiting:t.size}}function we(t){return{type:"not_found_error",code:"conversation_not_found",message:`Conversation not found or already completed: ${t}`,param:"conversation_id"}}function ye(t){const n=b(t,"X-Matrix-Room-Id")?.trim(),r=b(t,"X-Matrix-Thread-ID")?.trim();return n&&r?`${n}/${r}`:void 0}function ge(t){const n=b(t,"X-Matrix-Room-Id")?.trim(),r=b(t,"X-Matrix-Event-ID")?.trim(),o=b(t,"X-Matrix-Thread-ID")?.trim();if(!(!n||!r))return{roomId:n,eventId:r,...o?{threadRootEventId:o}:null}}class W extends Error{status;constructor(n,r){super(r),this.status=n,this.name="ChatRequestError"}}function b(t,n){return t?.get(n)??void 0}async function _t(t){const n=m.get(t);if(!n)return!1;for(n.cancelRequested=!0,n.turnId||n.owner?.abortController.abort();;){const r=n.transition;if(await r,r===n.transition)break}return n.thread&&n.turnId&&(n.interrupt||(n.interrupt=n.thread.interrupt(n.turnId).finally(()=>{n.interrupt=void 0})),await n.interrupt),n.owner?.abortController.abort(),n.resolveOwnerReady?.(),m.get(t)===n&&m.delete(t),!0}function Pt(t){let n=m.get(t);n||(n={conversationId:t,generation:0,cancelRequested:!1,transition:Promise.resolve()},m.set(t,n));const r=++n.generation,o=new AbortController,i=!n.owner;return i&&(n.owner={generation:r,abortController:o},n.ownerReady=new Promise(d=>{n.resolveOwnerReady=d})),{state:n,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 V(t,n){const r=t.transition;let o;const i=new Promise(d=>{o=d});t.transition=r.then(()=>i),await r;try{return await n()}finally{o()}}async function Ie(t,n,r,o){!t.ownsOutput&&!t.state.turnId&&await t.state.ownerReady;try{return await V(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=n;const d=i.turnId?await n.steerStreamed(r,i.turnId,{signal:t.abortController.signal}):await n.runStreamed(r,{...o,signal:t.abortController.signal});return i.turnId=d.turnId,d})}finally{t.ownsOutput&&t.state.resolveOwnerReady?.()}}async function he(t,n,r){return V(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=n;const i=await r();return o.turnId=i.turnId,o.resolveOwnerReady?.(),i})}async function Dt(t,n,r,o){return V(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=n;const d=await n.submitPendingUserInput(r,o);return d&&(i.turnId=d),d})}async function ve(t,n,r,o,i){return V(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=n,await n.interruptPendingUserInput(r.turnId,r.toolCallId),d.turnId=void 0;const v=await n.runStreamed(o,{...i,signal:t.abortController.signal});return d.turnId=v.turnId,v})}function Nt(t,n,r){t.state.thread=n,t.state.turnId=r}function Ut(t){const{state:n}=t;L(t)&&(n.owner=void 0,n.resolveOwnerReady?.(),n.turnId=void 0,m.get(n.conversationId)===n&&m.delete(n.conversationId))}function Wt(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 Ce(t){L(t)&&(Wt(t),t.state.resolveOwnerReady?.(),!t.state.turnId&&m.get(t.state.conversationId)===t.state&&m.delete(t.state.conversationId))}function G(t,n){return`${t??"unknown"}:${n}`}function xe(t){if(t)return[...q.values()].find(n=>n.conversationId===t)}function Lt(t,n){return t??n}function be(t){return t.type==="file"&&"mediaType"in t&&typeof t.mediaType=="string"&&"url"in t&&typeof t.url=="string"&&(!("attachmentId"in t)||t.attachmentId===void 0||typeof t.attachmentId=="string")&&(!("originalFilename"in t)||t.originalFilename===void 0||typeof t.originalFilename=="string")&&(!("filename"in t)||t.filename===void 0||typeof t.filename=="string")}async function Te(t,n){if(t.type==="text"&&typeof t.text=="string")return t.text;if(be(t))return ie(t,it.fileUploads.tempDir,n);throw new U(400,{message:`Unsupported message part type: ${t.type}`})}async function ke(t,n){const o=(await Promise.all(t.map(i=>Te(i,n)))).filter(i=>!!(i&&i.trim().length>0)).join(`
|
|
2
|
+
`).trim();if(!o)throw new U(400,{message:"No supported message content found"});return o}function Se(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=ne.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,n,r,o){const i=[n,r,o].filter(Boolean).join(":"),d=t.get(i);if(d)return d;const v=Yt();return t.set(i,v),v}function Ee(t,n){return t&&t.length>0?t:n}function Re(t){K.debug("Received chat stream event:",t)}function qe(t){if(t instanceof te||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)}J.post("/v1/chat/:conversation_id/cancel",async t=>{const n=t.req.param("conversation_id");try{return await _t(n)?t.json({success:!0}):t.json({error:we(n)},404)}catch(r){const o=r instanceof Error?r.message:"Failed to interrupt Codex conversation";return K.error(`Failed to cancel Codex conversation ${n}:`,r),t.json({error:{type:"server_error",code:"cancel_failed",message:o}},500)}}),J.get("/admin/status",t=>t.json({activeTaskCount:Q.size,waitingClientToolCount:q.size,clientToolWaits:[...q.values()].map(({conversationId:n,toolCallId:r,toolName:o,since:i})=>({conversationId:n,toolCallId:r,toolName:o,since:i}))}));async function $t({body:t,headers:n}){const{conversationId:r,fork:o,model:i,effort:d,message:v}=t,M=b(n,"X-Matrix-Bot-Name"),O=b(n,"X-Matrix-Workdir"),D=b(n,"X-Matrix-Workspace-ID"),At=b(n,"X-Matrix-Agent-Mode"),Y=b(n,"X-Elevo-LLM-Binding")?.trim()||void 0,Z=o?void 0:H.bindingFor(r);if(Y&&Z&&Y!==Z)throw new W(409,"LLM proxy binding does not match the existing conversation");const $=Z??Y,Ft=ye(n),Xt=ge(n),tt=Se(v.parts),{workspaceId:A,workspaceRoot:jt}=(()=>{try{const h=oe(D);if(!h)throw new Error("Missing workspace id");return{workspaceId:h,workspaceRoot:re(it,h)}}catch(h){throw new W(400,h instanceof Error?h.message:"Invalid workspace")}})(),et=tt?void 0:await ke(v.parts,A),ut=tt?void 0:xe(r),p=tt,_=et&&ut?ut:void 0;let F=!o&&r?Pt(r):void 0;const{thread:y,isNewSession:Bt}=await(async()=>{try{if(o){if(!("threadId"in o))throw new W(400,"Codex fork requires threadId and turnId");return await H.fork(o,M,O,A,i,"danger-full-access",$)}return await H.getOrCreate(r,M,O,A,i,"danger-full-access",$)}catch(h){F&&Ce(F);const P=qe(h);throw P?new W(400,`Workdir not found: ${P}`):h}})();F??=Pt(y.id),ae(y.id,{workspaceRoot:jt,threadRef:Ft,matrixEvent:Xt,llmProxyBinding:$});const lt=fe(At,i,d);return{run:async(h,P)=>{const c=F,X=c.abortController;Q.add(X);const nt=()=>{L(c)&&_t(c.state.conversationId).catch(a=>K.error("Failed to cancel disconnected Codex stream:",a))};P?.aborted&&nt(),P?.addEventListener("abort",nt,{once:!0});let k=!!p,ct=!!p,pt=!1,l,I=y.id,C,j=!1,N;const ft=new Set,mt=new Set,B=new Set,f=new Map,wt=new Set,zt=new Set,yt=new Set;let gt,at,It;const ht=()=>{N=setTimeout(()=>{s({type:"heartbeat"}).catch(()=>{})},6e4)},vt=async a=>{N&&clearTimeout(N),a.data==="[DONE]"?await h.writeDone():await h.writeData(JSON.parse(String(a.data))),ht()},s=async a=>{await vt({data:JSON.stringify(a)})},z=async()=>{await vt({data:"[DONE]"})},Ht=async()=>{const a=Lt(l,y.id),e=dt.get(a);e&&(dt.delete(a),await s({type:"data",kind:"diff",data:{diff:e}}))},Ct=async(a,e)=>{if(a.type!=="contextCompaction")return!1;if(e==="end"&&!wt.has(a.id))return!0;const u=e==="start"?wt:zt;return u.has(a.id)||(u.add(a.id),await s({type:"data",kind:"compact",data:e==="start"?{phase:e,activityId:a.id}:{phase:e,activityId:a.id,status:"completed"}})),!0},rt=async a=>{if(a.type!=="subAgentActivity")return!1;if(yt.has(a.id))return!0;yt.add(a.id);const e=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:e,toolName:`subagent_${a.kind}`,...a.agentPath?{title:a.agentPath}:null,input:u}),await s(a.kind==="interrupted"?{type:"tool-output-error",toolCallId:e,errorText:"Subagent interrupted"}:{type:"tool-output-available",toolCallId:e,output:u}),!0};ht();const xt=a=>{l!==a&&(l=a,Bt&&H.persist(a,M,O,A,$))},E=async(a,e)=>{k||(k=!0,xt(a),await s({type:"start",messageMetadata:{conversationId:a,threadId:a,...e?{turnId:e}:null}}),await s({type:"start-step"}),ct=!0)},ot=async a=>{pt||(pt=!0,ct&&(await Ht(),a==="stop"&&at&&await s({type:"data",kind:"token_usage",data:at}),await s({type:"finish-step"})),await s({type:"finish",finishReason:a}),await z())},bt=async a=>{if(ft.has(a.id))return;ft.add(a.id);const e=a.type==="plan"?{type:"plan"}:void 0;await s({type:"text-start",id:a.id,...e?{textMetadata:e}:{}})},Tt=async a=>{mt.has(a.id)||(mt.add(a.id),await s({type:"reasoning-start",id:a.id}))},Kt=async a=>{const e=kt(a);if(e)return await s({type:"tool-input-available",toolCallId:w(f,l,a.id),toolName:"bash",input:ue(e.command)}),!0;const u=Et(a);if(u){for(const[S,T]of u.changes.entries())await s({type:"tool-input-available",toolCallId:w(f,l,a.id,String(S)),toolName:T.kind.type==="update"?"Edit":T.kind.type==="add"?"Add":"Delete",title:T.path,input:T});return!0}const g=qt(a);if(g)return await s({type:"tool-input-available",toolCallId:w(f,l,a.id),toolName:g.tool,input:g.arguments}),!0;const x=St(a);if(x)return await s({type:"tool-input-available",toolCallId:w(f,l,a.id),toolName:x.tool,input:x.arguments}),!0;const R=Mt(a);return R?(await s({type:"tool-input-available",toolCallId:w(f,l,a.id),toolName:"web_search",title:R.query,input:Ot(R)}),!0):!1},Jt=async a=>{const e=kt(a);if(e){const S=e.status==="failed"||e.status==="declined";return await s(S?{type:"tool-output-error",toolCallId:w(f,l,a.id),errorText:Ee(e.aggregatedOutput,"Command execution failed")}:{type:"tool-output-available",toolCallId:w(f,l,a.id),output:e.aggregatedOutput??""}),!0}const u=Et(a);if(u){const S=u.status==="failed"||u.status==="declined";for(const[T]of u.changes.entries())await s(S?{type:"tool-output-error",toolCallId:w(f,l,a.id,String(T)),errorText:u.status}:{type:"tool-output-available",toolCallId:w(f,l,a.id,String(T)),output:u.status,preliminary:!1});return!0}const g=qt(a);if(g){const S=g.status==="failed";return await s(S?{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:ce(g)}),!0}const x=St(a);if(x){const S=x.status==="failed"||x.success===!1,T=le(x);return await s(S?{type:"tool-output-error",toolCallId:w(f,l,a.id),errorText:T||"Dynamic tool call failed"}:{type:"tool-output-available",toolCallId:w(f,l,a.id),output:T}),!0}const R=Mt(a);return R?(await s({type:"tool-output-available",toolCallId:w(f,l,a.id),output:Ot(R)}),!0):!1};try{if(p&&!c.ownsOutput){await Dt(c,y,p.itemId,p.response),q.delete(G(r,p.itemId)),await z();return}const a=_?await ve(c,y,_,et,lt):p?await he(c,y,()=>y.continueWithUserInput(p.itemId,p.response,{signal:X.signal,sourceTurnId:p.sourceTurnId})):await Ie(c,y,et,lt);if(p?q.delete(G(r,p.itemId)):_&&q.delete(G(r,_.toolCallId)),Nt(c,y,a.turnId),!c.ownsOutput){await z();return}p?(await s({type:"data",kind:"agent_session",data:{agentType:"codex",threadId:y.id,turnId:a.turnId}}),await s({type:"tool-output-available",toolCallId:p.itemId,toolName:"request_user_input",output:p.response})):_&&await s({type:"tool-output-error",toolCallId:_.toolCallId,toolName:"request_user_input",errorText:"Question interrupted by text response"});for await(const e of a.events)if(e.type!=="item.delta"&&Re(e),e.type==="thread.started")I=e.thread_id,xt(I);else if(e.type==="turn.started")C=e.turn_id,Nt(c,y,C),await E(I,C);else if(e.type==="item.started"){if(k||await E(I,C),await Ct(e.item,"start")||await rt(e.item)||await Kt(e.item))continue;e.item.type==="agentMessage"||e.item.type==="plan"?await bt(e.item):e.item.type==="reasoning"&&await Tt(e.item)}else if(e.type==="item.delta")k||await E(I,C),e.item_type==="agentMessage"||e.item_type==="plan"?(B.add(e.item_id),await s({type:"text-delta",id:e.item_id,delta:e.delta})):e.item_type==="reasoning"&&(B.add(e.item_id),await s({type:"reasoning-delta",id:e.item_id,delta:e.delta}));else if(e.type==="item.updated"){if(await rt(e.item))continue}else if(e.type==="item.completed"){if(k||await E(I,C),await Ct(e.item,"end")||await rt(e.item)||await Jt(e.item))continue;if(e.item.type==="agentMessage"||e.item.type==="plan"){await bt(e.item);const u=Rt(e.item)??"";if(u&&!B.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}:{}}),e.item.type==="agentMessage"&&e.item.delivery==="async"&&e.item.questions?.length){const g=`${l??I}:${e.item.id}`;await s({type:"async-question",request:{version:1,requestId:g,conversationId:l??I,threadId:I,turnId:C??c.state.turnId??"",toolCallId:e.item.id,questions:e.item.questions.map((x,R)=>({id:`${e.item.id}:${R}`,title:x.title,options:x.options,allowOther:!0}))}})}}else if(e.item.type==="reasoning"){await Tt(e.item);const u=Rt(e.item)??"";u&&!B.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(k||await E(I,C),e.params.isBlocking===!1){if(!await Dt(c,y,e.params.itemId,{answers:{}}))throw new Error(`Non-blocking user input request is no longer pending: ${e.params.itemId}`);K.info(`Auto-resolved non-blocking request_user_input ${e.params.itemId} for turn ${e.params.turnId}`);continue}q.set(G(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 z();return}else if(e.type==="turn.diff.updated")k||await E(I,C),dt.set(Lt(l,y.id),e.diff);else if(e.type==="thread.tokenUsage.updated"){const u=pe(e.usage);if(u){const g=de(It,u.last);g&&(It=g,at={...u,last:g})}}else if(e.type==="turn.failed"){gt!==e.error.message&&await s({type:"error",errorText:e.error.message}),await ot("error"),j=!0,Ut(c);return}else e.type==="error"&&(gt=e.message,await s({type:"error",errorText:e.message}));j=!X.signal.aborted,j&&Ut(c),k||await E(I,C),await ot("stop")}catch(a){const e=a instanceof Error?a.message:"Chat stream failed";k||await E(I,C),await s({type:"error",errorText:e}),await ot("error")}finally{N&&clearTimeout(N),P?.removeEventListener("abort",nt),Q.delete(X),j||Wt(c)}}}}async function He({body:t,headers:n,signal:r},o){await(await $t({body:t,headers:n})).run(o,r)}J.post("/v1/chat/stream",Vt("json",ee),async t=>{const n=t.req.valid("json"),r=b(t.req.raw.headers,"X-Elevo-Wakeup-Key")?.trim()||void 0;let o;if(r){const d=st().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 $t({body:n,headers:t.req.raw.headers})}catch(d){throw r&&o&&st().release(r,o),d instanceof W?new U(d.status,{message:d.message}):d}return Gt(t,async d=>{let v=!1;const M=()=>{!r||!o||v||(v=!0,st().markStarted(r,o))},O=async D=>{await d.writeSSE(D)};await i.run({writeData:async D=>{M(),await O({data:JSON.stringify(D)})},writeDone:async()=>{M(),await O({data:"[DONE]"})}},t.req.raw.signal)})});var Ke=J;export{W as ChatRequestError,_t as cancelCodexConversation,ze as codexActivitySnapshot,Ke as default,He as handleCodexChatStream,$t as prepareCodexChatStream};
|
package/dist/schemas.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare const ChatFilePartSchema: z.ZodObject<{
|
|
|
7
7
|
type: z.ZodLiteral<"file">;
|
|
8
8
|
mediaType: z.ZodString;
|
|
9
9
|
filename: z.ZodOptional<z.ZodString>;
|
|
10
|
+
originalFilename: z.ZodOptional<z.ZodString>;
|
|
10
11
|
attachmentId: z.ZodOptional<z.ZodString>;
|
|
11
12
|
url: z.ZodString;
|
|
12
13
|
}, z.core.$strip>;
|
|
@@ -83,6 +84,7 @@ export declare const ChatBodySchema: z.ZodObject<{
|
|
|
83
84
|
type: z.ZodLiteral<"file">;
|
|
84
85
|
mediaType: z.ZodString;
|
|
85
86
|
filename: z.ZodOptional<z.ZodString>;
|
|
87
|
+
originalFilename: z.ZodOptional<z.ZodString>;
|
|
86
88
|
attachmentId: z.ZodOptional<z.ZodString>;
|
|
87
89
|
url: z.ZodString;
|
|
88
90
|
}, z.core.$strip>, z.ZodObject<{
|
package/dist/schemas.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{z as t}from"zod";const o=t.object({type:t.literal("text"),text:t.string()}),e=t.object({type:t.literal("file"),mediaType:t.string(),filename:t.string().optional(),attachmentId:t.string().min(1).optional(),url:t.string()}),n=t.object({type:t.literal("step-start")}),r=t.object({type:t.literal("tool"),toolCallId:t.string(),toolName:t.string(),title:t.string().optional(),state:t.string(),input:t.unknown().optional(),output:t.unknown().optional(),approval:t.object({id:t.string(),approved:t.boolean().optional(),reason:t.string().optional()}).optional()}),a=t.looseObject({type:t.string()}),
|
|
1
|
+
import{z as t}from"zod";const o=t.object({type:t.literal("text"),text:t.string()}),e=t.object({type:t.literal("file"),mediaType:t.string(),filename:t.string().optional(),originalFilename:t.string().optional(),attachmentId:t.string().min(1).optional(),url:t.string()}),n=t.object({type:t.literal("step-start")}),r=t.object({type:t.literal("tool"),toolCallId:t.string(),toolName:t.string(),title:t.string().optional(),state:t.string(),input:t.unknown().optional(),output:t.unknown().optional(),approval:t.object({id:t.string(),approved:t.boolean().optional(),reason:t.string().optional()}).optional()}),a=t.looseObject({type:t.string()}),i=t.strictObject({threadId:t.string().min(1),turnId:t.string().min(1)}),s=t.strictObject({sessionId:t.string().min(1),messageId:t.string().min(1)}),l=t.union([i,s]),p=t.enum(["low","medium","high","xhigh","max"]),g=t.object({conversationId:t.string().optional(),fork:l.optional(),model:t.string(),effort:p.default("medium"),message:t.object({id:t.string().optional(),role:t.enum(["user","assistant"]).optional(),parts:t.array(t.union([o,e,n,r,a]))})}),c=t.object({answers:t.array(t.string())}),m=t.object({answers:t.record(t.string(),c)}),h=t.object({type:t.literal("tool"),toolCallId:t.string(),toolName:t.literal("request_user_input"),state:t.literal("output-available"),sourceTurnId:t.string().min(1).optional(),output:m});export{p as AgentEffortSchema,g as ChatBodySchema,a as ChatCustomPartSchema,e as ChatFilePartSchema,l as ChatForkSchema,n as ChatStepStartPartSchema,o as ChatTextPartSchema,r as ChatToolPartSchema,s as ClaudeChatForkSchema,i as CodexChatForkSchema,h as RequestUserInputToolPartSchema,c as ToolRequestUserInputAnswerSchema,m as ToolRequestUserInputResponseSchema};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@messenger-agent/codex-agent",
|
|
3
|
-
"version": "0.43.1-alpha.
|
|
3
|
+
"version": "0.43.1-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.43.1-alpha.
|
|
25
|
+
"@messenger-agent/shared": "0.43.1-alpha.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/ws": "^8.18.1"
|