@messenger-agent/codex-agent 0.26.0-alpha.3 → 0.26.0-alpha.5
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/assets/workspace-templates/basic/AGENTS.md +14 -0
- package/dist/assets/workspace-templates/developer/AGENTS.md +14 -0
- package/dist/routes/chat.js +2 -2
- package/dist/routes/shared.d.ts +2 -1
- package/dist/routes/shared.js +5 -5
- package/dist/schemas.d.ts +15 -0
- package/dist/schemas.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Working In This Workspace
|
|
2
|
+
|
|
3
|
+
Before making changes:
|
|
4
|
+
|
|
5
|
+
- Inspect the workspace structure and read the relevant README, project documentation, and any more specific `AGENTS.md` files.
|
|
6
|
+
- Follow the project's existing conventions, package manager, lockfiles, scripts, and test commands.
|
|
7
|
+
- Keep changes focused. Preserve unrelated user changes and do not overwrite files you have not understood.
|
|
8
|
+
- Run the most relevant available checks after editing and clearly report anything that could not be verified.
|
|
9
|
+
|
|
10
|
+
## Environment
|
|
11
|
+
|
|
12
|
+
This basic workspace is intended for source inspection, scripting, and lightweight development. Common tools include Bash, Git, Python 3, Node.js, ripgrep, curl, and GitHub/GitLab CLI tools.
|
|
13
|
+
|
|
14
|
+
System package installation and `sudo` access are not available. If the task requires a compiler, Rust, native system libraries, or other missing system dependencies, explain the limitation and recommend using a developer workspace.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Working In This Workspace
|
|
2
|
+
|
|
3
|
+
Before making changes:
|
|
4
|
+
|
|
5
|
+
- Inspect the workspace structure and read the relevant README, project documentation, and any more specific `AGENTS.md` files.
|
|
6
|
+
- Follow the project's existing conventions, package manager, lockfiles, scripts, and test commands.
|
|
7
|
+
- Keep changes focused. Preserve unrelated user changes and do not overwrite files you have not understood.
|
|
8
|
+
- Run the relevant tests, lint checks, or builds after editing and clearly report anything that could not be verified.
|
|
9
|
+
|
|
10
|
+
## Environment
|
|
11
|
+
|
|
12
|
+
This developer workspace includes common development tools, C/C++ build tools, Rust, ffmpeg, and `sudo`-based apt package installation.
|
|
13
|
+
|
|
14
|
+
Prefer the project's existing toolchain and package manager. Install additional system packages only when the task genuinely requires them, and avoid unrelated environment changes.
|
package/dist/routes/chat.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Hono as
|
|
2
|
-
`).trim();if(!r)throw new D(400,{message:"No supported message content found"});return r}function he(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=Gt.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 x=Kt();return t.set(i,x),x}function Ce(t,e){return t&&t.length>0?t:e}function ve(t){at.debug("Received chat stream event:",t)}function xe(t){if(t instanceof Qt||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 Ot(e)?t.json({success:!0}):t.json({error:se(e)},404)}catch(r){const o=r instanceof Error?r.message:"Failed to interrupt Codex conversation";return at.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 Nt({body:t,headers:e}){const{conversationId:r,fork:o,model:i,message:d}=t,x=v(e,"X-Matrix-Bot-Name"),K=v(e,"X-Matrix-Workdir"),U=v(e,"X-Matrix-Workspace-ID"),Ut=v(e,"X-Matrix-Agent-Mode"),Q=v(e,"X-Elevo-LLM-Binding")?.trim()||void 0,V=o?void 0:j.bindingFor(r);if(Q&&V&&Q!==V)throw new q(409,"LLM proxy binding does not match the existing conversation");const L=V??Q,Lt=de(e),At=ue(e),G=he(d.parts),Y=G?void 0:await Ie(d.parts),ot=G?void 0:we(r),c=G,O=Y&&ot?ot:void 0;let A=!o&&r?_t(r):void 0;const Wt=(()=>{try{return Zt(vt,U)}catch(S){throw new q(400,S instanceof Error?S.message:"Invalid workspace")}})(),{thread:y,isNewSession:Ft}=await(async()=>{try{if(o){if(!("threadId"in o))throw new q(400,"Codex fork requires threadId and turnId");return await j.fork(o,x,K,U,i,"danger-full-access",L)}return await j.getOrCreate(r,x,K,U,i,"danger-full-access",L)}catch(S){A&&me(A);const _=xe(S);throw _?new q(400,`Workdir not found: ${_}`):S}})();A??=_t(y.id),Yt(y.id,{workspaceRoot:Wt,threadRef:Lt,matrixEvent:At,llmProxyBinding:L});const it=ie(Ut,i);return{run:async(S,_)=>{const w=A,W=w.abortController;z.add(W);const Z=()=>{N(w)&&Ot(w.state.conversationId).catch(a=>at.error("Failed to cancel disconnected Codex stream:",a))};_?.aborted&&Z(),_?.addEventListener("abort",Z,{once:!0});let b=!!c,st=!!c,dt=!1,l,I=y.id,h,F=!1,P;const ut=new Set,lt=new Set,X=new Set,p=new Map,ct=new Set,Xt=new Set,pt=new Set;let ft,tt,mt;const wt=()=>{P=setTimeout(()=>{s({type:"heartbeat"}).catch(()=>{})},6e4)},yt=async a=>{P&&clearTimeout(P),a.data==="[DONE]"?await S.writeDone():await S.writeData(JSON.parse(String(a.data))),wt()},s=async a=>{await yt({data:JSON.stringify(a)})},$=async()=>{await yt({data:"[DONE]"})},$t=async()=>{const a=qt(l,y.id),n=rt.get(a);n&&(rt.delete(a),await s({type:"data",kind:"diff",data:{diff:n}}))},gt=async(a,n)=>{if(a.type!=="contextCompaction")return!1;if(n==="end"&&!ct.has(a.id))return!0;const u=n==="start"?ct:Xt;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},et=async a=>{if(a.type!=="subAgentActivity")return!1;if(pt.has(a.id))return!0;pt.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};wt();const It=a=>{l!==a&&(l=a,Ft&&j.persist(a,x,K,U,L))},E=async(a,n)=>{b||(b=!0,It(a),await s({type:"start",messageMetadata:{conversationId:a,threadId:a,...n?{turnId:n}:null}}),await s({type:"start-step"}),st=!0)},nt=async a=>{dt||(dt=!0,st&&(await $t(),a==="stop"&&tt&&await s({type:"data",kind:"token_usage",data:tt}),await s({type:"finish-step"})),await s({type:"finish",finishReason:a}),await $())},ht=async a=>{if(ut.has(a.id))return;ut.add(a.id);const n=a.type==="plan"?{type:"plan"}:void 0;await s({type:"text-start",id:a.id,...n?{textMetadata:n}:{}})},Ct=async a=>{lt.has(a.id)||(lt.add(a.id),await s({type:"reasoning-start",id:a.id}))},jt=async a=>{const n=xt(a);if(n)return await s({type:"tool-input-available",toolCallId:m(p,l,a.id),toolName:"bash",input:ne(n.command)}),!0;const u=Tt(a);if(u){for(const[T,C]of u.changes.entries())await s({type:"tool-input-available",toolCallId:m(p,l,a.id,String(T)),toolName:C.kind.type==="update"?"Edit":C.kind.type==="add"?"Add":"Delete",title:C.path,input:C});return!0}const g=Et(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=bt(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=Rt(a);return M?(await s({type:"tool-input-available",toolCallId:m(p,l,a.id),toolName:"web_search",title:M.query,input:kt(M)}),!0):!1},Bt=async a=>{const n=xt(a);if(n){const T=n.status==="failed"||n.status==="declined";return await s(T?{type:"tool-output-error",toolCallId:m(p,l,a.id),errorText:Ce(n.aggregatedOutput,"Command execution failed")}:{type:"tool-output-available",toolCallId:m(p,l,a.id),output:n.aggregatedOutput??""}),!0}const u=Tt(a);if(u){const T=u.status==="failed"||u.status==="declined";for(const[C]of u.changes.entries())await s(T?{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=Et(a);if(g){const T=g.status==="failed";return await s(T?{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:re(g)}),!0}const R=bt(a);if(R){const T=R.status==="failed"||R.success===!1,C=ae(R);return await s(T?{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=Rt(a);return M?(await s({type:"tool-output-available",toolCallId:m(p,l,a.id),output:kt(M)}),!0):!1};try{if(c&&!w.ownsOutput){await pe(w,y,c.itemId,c.response),k.delete(J(r,c.itemId)),await $();return}const a=O?await fe(w,y,O,Y,it):c?await ce(w,y,()=>y.continueWithUserInput(c.itemId,c.response,{signal:W.signal,sourceTurnId:c.sourceTurnId})):await le(w,y,Y,it);if(c?k.delete(J(r,c.itemId)):O&&k.delete(J(r,O.toolCallId)),Mt(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"&&ve(n),n.type==="thread.started")I=n.thread_id,It(I);else if(n.type==="turn.started")h=n.turn_id,Mt(w,y,h),await E(I,h);else if(n.type==="item.started"){if(b||await E(I,h),await gt(n.item,"start")||await et(n.item)||await jt(n.item))continue;n.item.type==="agentMessage"||n.item.type==="plan"?await ht(n.item):n.item.type==="reasoning"&&await Ct(n.item)}else if(n.type==="item.delta")b||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 et(n.item))continue}else if(n.type==="item.completed"){if(b||await E(I,h),await gt(n.item,"end")||await et(n.item)||await Bt(n.item))continue;if(n.item.type==="agentMessage"||n.item.type==="plan"){await ht(n.item);const u=St(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 Ct(n.item);const u=St(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"){b||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")b||await E(I,h),rt.set(qt(l,y.id),n.diff);else if(n.type==="thread.tokenUsage.updated"){const u=oe(n.usage);if(u){const g=ee(mt,u.last);g&&(mt=g,tt={...u,last:g})}}else if(n.type==="turn.failed"){ft!==n.error.message&&await s({type:"error",errorText:n.error.message}),await nt("error"),F=!0,Pt(w);return}else n.type==="error"&&(ft=n.message,await s({type:"error",errorText:n.message}));F=!W.signal.aborted,F&&Pt(w),b||await E(I,h),await nt("stop")}catch(a){const n=a instanceof Error?a.message:"Chat stream failed";b||await E(I,h),await s({type:"error",errorText:n}),await nt("error")}finally{P&&clearTimeout(P),_?.removeEventListener("abort",Z),z.delete(W),F||Dt(w)}}}}async function Le({body:t,headers:e,signal:r},o){await(await Nt({body:t,headers:e})).run(o,r)}B.post("/v1/chat/stream",Ht("json",Vt),async t=>{const e=t.req.valid("json"),r=await(async()=>{try{return await Nt({body:e,headers:t.req.raw.headers})}catch(o){throw o instanceof q?new D(o.status,{message:o.message}):o}})();return Jt(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 Ae=B;export{q as ChatRequestError,Ot as cancelCodexConversation,Ue as codexActivitySnapshot,Ae as default,Le as handleCodexChatStream,Nt 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;;){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/dist/routes/shared.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CommandExecutionItem, DynamicToolCallItem, FileChangeItem, McpToolCallItem, ThreadItem, TurnStartParams, Usage, WebSearchItem } from "../app-server-protocol.js";
|
|
2
|
+
import type { AgentEffort } from "../schemas.js";
|
|
2
3
|
export type TokenUsageBreakdown = {
|
|
3
4
|
input_tokens: number;
|
|
4
5
|
output_tokens: number;
|
|
@@ -19,7 +20,7 @@ export declare function textFromContent(content: string | Array<{
|
|
|
19
20
|
text?: string;
|
|
20
21
|
}>): string;
|
|
21
22
|
export declare function inputToPrompt(input: unknown, instructions: string | undefined, isNewSession: boolean, systemReminder?: string): string;
|
|
22
|
-
export declare function turnOptions(agentMode: string | undefined, model: string): {
|
|
23
|
+
export declare function turnOptions(agentMode: string | undefined, model: string, effort: AgentEffort): {
|
|
23
24
|
collaborationMode: TurnStartParams["collaborationMode"];
|
|
24
25
|
};
|
|
25
26
|
export declare function itemText(item: ThreadItem): string | undefined;
|
package/dist/routes/shared.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import{HTTPException as
|
|
2
|
-
`)}function
|
|
1
|
+
import{HTTPException as c}from"hono/http-exception";function h(t,e){const n=(t?.input_tokens??0)+e.input_tokens,o=(t?.output_tokens??0)+e.output_tokens,r=(t?.total_tokens??0)+e.total_tokens;if([n,o,r].every(Number.isSafeInteger))return{input_tokens:n,output_tokens:o,total_tokens:r}}function g(t){const e=s=>{const l=s?.input_tokens,i=s?.output_tokens,f=s?.total_tokens;if(!(typeof l!="number"||typeof i!="number"||typeof f!="number"||!Number.isSafeInteger(l)||l<0||!Number.isSafeInteger(i)||i<0||!Number.isSafeInteger(f)||f<0))return{input_tokens:l,output_tokens:i,total_tokens:f}},n=e(t?.total),o=e(t?.last),r=t?.model_context_window;if(!(!n||!o))return{total:n,last:o,...typeof r=="number"&&Number.isSafeInteger(r)&&r>0?{context:{used_tokens:o.total_tokens,window_tokens:r}}:{}}}function p(t){return typeof t=="string"?t:t.filter(e=>e.type==="input_text").map(e=>e.text??"").join(`
|
|
2
|
+
`)}function k(t,e,n,o){const r=(n?[o,e]:[o]).filter(u=>!!u),s=r.length>0?`${r.join(`
|
|
3
3
|
|
|
4
4
|
`)}
|
|
5
5
|
|
|
6
|
-
`:"";if(typeof t=="string")return`${s}${t}`;if(!Array.isArray(t))throw new
|
|
6
|
+
`:"";if(typeof t=="string")return`${s}${t}`;if(!Array.isArray(t))throw new c(400,{message:"Invalid input"});const l=[...t].reverse().find(u=>!!(u&&typeof u=="object"&&"role"in u&&u.role==="user"));if(!l)throw new c(400,{message:"No user message found"});let i=e;if(n&&!i){const u=[...t].reverse().find(a=>!!(a&&typeof a=="object"&&"role"in a)&&(a.role==="system"||a.role==="developer"));u&&(i=p(u.content))}const f=n?[o,i].filter(u=>!!u):[];return`${f.length>0?`${f.join(`
|
|
7
7
|
|
|
8
8
|
`)}
|
|
9
9
|
|
|
10
|
-
`:""}${p(l.content)}`}function m(t,e){return{collaborationMode:{mode:t==="plan"?"plan":"default",settings:{model:e||null,reasoning_effort:"
|
|
10
|
+
`:""}${p(l.content)}`}function m(t,e,n){return{collaborationMode:{mode:t==="plan"?"plan":"default",settings:{model:e||null,reasoning_effort:n==="max"?"xhigh":n,developer_instructions:null}}}}function _(t){if(t.type==="agentMessage")return t.text;if(t.type==="reasoning")return t.summary.join(`
|
|
11
11
|
`)||t.content.join(`
|
|
12
12
|
`);if(t.type==="plan")return t.text}function T(t){if(t.type==="commandExecution")return t}function b(t){if(t.type==="fileChange")return t}function C(t){if(t.type==="mcpToolCall")return t}function w(t){if(t.type==="dynamicToolCall")return t}function I(t){if(t.type==="webSearch")return t}function d(t){if(!Array.isArray(t))return;const e=t.map(n=>n&&typeof n=="object"&&"type"in n&&n.type==="text"&&"text"in n&&typeof n.text=="string"?n.text:"").filter(n=>n.length>0).join(`
|
|
13
13
|
`);return e.length>0?e:void 0}function j(t){const e=t.result?.structuredContent;return e&&typeof e=="object"&&"filePath"in e&&typeof e.filePath=="string"?e.filePath:d(t.result?.content)??JSON.stringify(t.result??null)}function x(t){let e=0,n="";for(;e<t.length;){const o=t[e];if(/\s/.test(o))break;if(o==="'"){for(e+=1;e<t.length;){if(t[e]==="'"){e+=1;break}n+=t[e],e+=1}continue}if(o==='"'){for(e+=1;e<t.length;){const r=t[e];if(r==='"'){e+=1;break}if(r==="\\"&&e+1<t.length){n+=t[e+1],e+=2;continue}n+=r,e+=1}continue}if(o==="\\"&&e+1<t.length){n+=t[e+1],e+=2;continue}n+=o,e+=1}return t.slice(e).trim()===""?n:void 0}function S(t){const e=t.match(/^\/bin\/bash\s+-lc\s+([\s\S]+)$/);return e?x(e[1].trim())??t:t}function v(t){return(t.contentItems??[]).map(n=>n.type==="inputText"?n.text:n.imageUrl).filter(n=>n.length>0).join(`
|
|
14
|
-
`)}function $(t){return{query:t.query,...t.action?{action:t.action}:null}}export{h as addTokenUsage,T as commandExecutionItem,S as displayCommand,w as dynamicToolCallItem,v as dynamicToolCallOutput,b as fileChangeItem,
|
|
14
|
+
`)}function $(t){return{query:t.query,...t.action?{action:t.action}:null}}export{h as addTokenUsage,T as commandExecutionItem,S as displayCommand,w as dynamicToolCallItem,v as dynamicToolCallOutput,b as fileChangeItem,k as inputToPrompt,_ as itemText,C as mcpToolCallItem,j as mcpToolCallOutput,p as textFromContent,g as toUsage,m as turnOptions,I as webSearchItem,$ as webSearchPayload};
|
package/dist/schemas.d.ts
CHANGED
|
@@ -44,6 +44,14 @@ export declare const ChatForkSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
44
44
|
sessionId: z.ZodString;
|
|
45
45
|
messageId: z.ZodString;
|
|
46
46
|
}, z.core.$strict>]>;
|
|
47
|
+
export declare const AgentEffortSchema: z.ZodEnum<{
|
|
48
|
+
medium: "medium";
|
|
49
|
+
high: "high";
|
|
50
|
+
low: "low";
|
|
51
|
+
xhigh: "xhigh";
|
|
52
|
+
max: "max";
|
|
53
|
+
}>;
|
|
54
|
+
export type AgentEffort = z.infer<typeof AgentEffortSchema>;
|
|
47
55
|
export declare const ChatBodySchema: z.ZodObject<{
|
|
48
56
|
conversationId: z.ZodOptional<z.ZodString>;
|
|
49
57
|
fork: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|
|
@@ -54,6 +62,13 @@ export declare const ChatBodySchema: z.ZodObject<{
|
|
|
54
62
|
messageId: z.ZodString;
|
|
55
63
|
}, z.core.$strict>]>>;
|
|
56
64
|
model: z.ZodString;
|
|
65
|
+
effort: z.ZodDefault<z.ZodEnum<{
|
|
66
|
+
medium: "medium";
|
|
67
|
+
high: "high";
|
|
68
|
+
low: "low";
|
|
69
|
+
xhigh: "xhigh";
|
|
70
|
+
max: "max";
|
|
71
|
+
}>>;
|
|
57
72
|
message: z.ZodObject<{
|
|
58
73
|
id: z.ZodOptional<z.ZodString>;
|
|
59
74
|
role: z.ZodOptional<z.ZodEnum<{
|
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(),url:t.string()}),r=t.object({type:t.literal("step-start")}),n=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()}),s=t.strictObject({threadId:t.string().min(1),turnId:t.string().min(1)}),i=t.strictObject({sessionId:t.string().min(1),messageId:t.string().min(1)}),l=t.union([s,i]),
|
|
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(),url:t.string()}),r=t.object({type:t.literal("step-start")}),n=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()}),s=t.strictObject({threadId:t.string().min(1),turnId:t.string().min(1)}),i=t.strictObject({sessionId:t.string().min(1),messageId:t.string().min(1)}),l=t.union([s,i]),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,r,n,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,r as ChatStepStartPartSchema,o as ChatTextPartSchema,n as ChatToolPartSchema,i as ClaudeChatForkSchema,s 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.26.0-alpha.
|
|
3
|
+
"version": "0.26.0-alpha.5",
|
|
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.26.0-alpha.
|
|
25
|
+
"@messenger-agent/shared": "0.26.0-alpha.5"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/ws": "^8.18.1"
|